mirror of
https://github.com/sst/opencode.git
synced 2025-08-22 14:04:07 +00:00
run formatter
This commit is contained in:
parent
6df19f1828
commit
3b746162d2
52 changed files with 1376 additions and 1390 deletions
39
README.md
39
README.md
|
@ -83,6 +83,7 @@ You can configure OpenCode using environment variables:
|
||||||
| `AZURE_OPENAI_ENDPOINT` | For Azure OpenAI models |
|
| `AZURE_OPENAI_ENDPOINT` | For Azure OpenAI models |
|
||||||
| `AZURE_OPENAI_API_KEY` | For Azure OpenAI models (optional when using Entra ID) |
|
| `AZURE_OPENAI_API_KEY` | For Azure OpenAI models (optional when using Entra ID) |
|
||||||
| `AZURE_OPENAI_API_VERSION` | For Azure OpenAI models |
|
| `AZURE_OPENAI_API_VERSION` | For Azure OpenAI models |
|
||||||
|
|
||||||
### Configuration File Structure
|
### Configuration File Structure
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
@ -205,7 +206,7 @@ To use bedrock models with OpenCode you need three things.
|
||||||
|
|
||||||
1. Valid AWS credentials (the env vars: `AWS_SECRET_KEY_ID`, `AWS_SECRET_ACCESS_KEY` and `AWS_REGION`)
|
1. Valid AWS credentials (the env vars: `AWS_SECRET_KEY_ID`, `AWS_SECRET_ACCESS_KEY` and `AWS_REGION`)
|
||||||
2. Access to the corresponding model in AWS Bedrock in your region.
|
2. Access to the corresponding model in AWS Bedrock in your region.
|
||||||
a. You can request access in the AWS console on the Bedrock -> "Model access" page.
|
a. You can request access in the AWS console on the Bedrock -> "Model access" page.
|
||||||
3. A correct configuration file. You don't need the `providers` key. Instead you have to prefix your models per agent with `bedrock.` and then a valid model. For now only Claude 3.7 is supported.
|
3. A correct configuration file. You don't need the `providers` key. Instead you have to prefix your models per agent with `bedrock.` and then a valid model. For now only Claude 3.7 is supported.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
@ -226,10 +227,10 @@ To use bedrock models with OpenCode you need three things.
|
||||||
"maxTokens": 80,
|
"maxTokens": 80,
|
||||||
"reasoningEffort": ""
|
"reasoningEffort": ""
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Interactive Mode Usage
|
## Interactive Mode Usage
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
@ -295,26 +296,26 @@ These flags are mutually exclusive - you can use either `--allowedTools` or `--e
|
||||||
|
|
||||||
OpenCode supports the following output formats in non-interactive mode:
|
OpenCode supports the following output formats in non-interactive mode:
|
||||||
|
|
||||||
| Format | Description |
|
| Format | Description |
|
||||||
| ------ | -------------------------------------- |
|
| ------ | ------------------------------- |
|
||||||
| `text` | Plain text output (default) |
|
| `text` | Plain text output (default) |
|
||||||
| `json` | Output wrapped in a JSON object |
|
| `json` | Output wrapped in a JSON object |
|
||||||
|
|
||||||
The output format is implemented as a strongly-typed `OutputFormat` in the codebase, ensuring type safety and validation when processing outputs.
|
The output format is implemented as a strongly-typed `OutputFormat` in the codebase, ensuring type safety and validation when processing outputs.
|
||||||
|
|
||||||
## Command-line Flags
|
## Command-line Flags
|
||||||
|
|
||||||
| Flag | Short | Description |
|
| Flag | Short | Description |
|
||||||
| ----------------- | ----- | ---------------------------------------------------------- |
|
| ----------------- | ----- | --------------------------------------------------- |
|
||||||
| `--help` | `-h` | Display help information |
|
| `--help` | `-h` | Display help information |
|
||||||
| `--debug` | `-d` | Enable debug mode |
|
| `--debug` | `-d` | Enable debug mode |
|
||||||
| `--cwd` | `-c` | Set current working directory |
|
| `--cwd` | `-c` | Set current working directory |
|
||||||
| `--prompt` | `-p` | Run a single prompt in non-interactive mode |
|
| `--prompt` | `-p` | Run a single prompt in non-interactive mode |
|
||||||
| `--output-format` | `-f` | Output format for non-interactive mode (text, json) |
|
| `--output-format` | `-f` | Output format for non-interactive mode (text, json) |
|
||||||
| `--quiet` | `-q` | Hide spinner in non-interactive mode |
|
| `--quiet` | `-q` | Hide spinner in non-interactive mode |
|
||||||
| `--verbose` | | Display logs to stderr in non-interactive mode |
|
| `--verbose` | | Display logs to stderr in non-interactive mode |
|
||||||
| `--allowedTools` | | Restrict the agent to only use specified tools |
|
| `--allowedTools` | | Restrict the agent to only use specified tools |
|
||||||
| `--excludedTools` | | Prevent the agent from using specified tools |
|
| `--excludedTools` | | Prevent the agent from using specified tools |
|
||||||
|
|
||||||
## Keyboard Shortcuts
|
## Keyboard Shortcuts
|
||||||
|
|
||||||
|
@ -483,6 +484,7 @@ You don't need to define all colors. Any undefined colors will fall back to the
|
||||||
### Shell Configuration
|
### Shell Configuration
|
||||||
|
|
||||||
OpenCode allows you to configure the shell used by the `bash` tool. By default, it uses:
|
OpenCode allows you to configure the shell used by the `bash` tool. By default, it uses:
|
||||||
|
|
||||||
1. The shell specified in the config file (if provided)
|
1. The shell specified in the config file (if provided)
|
||||||
2. The shell from the `$SHELL` environment variable (if available)
|
2. The shell from the `$SHELL` environment variable (if available)
|
||||||
3. Falls back to `/bin/bash` if neither of the above is available
|
3. Falls back to `/bin/bash` if neither of the above is available
|
||||||
|
@ -577,6 +579,7 @@ RUN grep -R "$SEARCH_PATTERN" $DIRECTORY
|
||||||
```
|
```
|
||||||
|
|
||||||
When you run a command with arguments, OpenCode will prompt you to enter values for each unique placeholder. Named arguments provide several benefits:
|
When you run a command with arguments, OpenCode will prompt you to enter values for each unique placeholder. Named arguments provide several benefits:
|
||||||
|
|
||||||
- Clear identification of what each argument represents
|
- Clear identification of what each argument represents
|
||||||
- Ability to use the same argument multiple times
|
- Ability to use the same argument multiple times
|
||||||
- Better organization for commands with multiple inputs
|
- Better organization for commands with multiple inputs
|
||||||
|
|
76
bun.lock
76
bun.lock
|
@ -30,7 +30,6 @@
|
||||||
"env-paths": "3.0.0",
|
"env-paths": "3.0.0",
|
||||||
"hono": "4.7.10",
|
"hono": "4.7.10",
|
||||||
"hono-openapi": "0.4.8",
|
"hono-openapi": "0.4.8",
|
||||||
"jsdom": "26.1.0",
|
|
||||||
"remeda": "2.22.3",
|
"remeda": "2.22.3",
|
||||||
"ts-lsp-client": "1.0.3",
|
"ts-lsp-client": "1.0.3",
|
||||||
"turndown": "7.2.0",
|
"turndown": "7.2.0",
|
||||||
|
@ -42,7 +41,6 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tsconfig/bun": "1.0.7",
|
"@tsconfig/bun": "1.0.7",
|
||||||
"@types/bun": "latest",
|
"@types/bun": "latest",
|
||||||
"@types/jsdom": "21.1.7",
|
|
||||||
"@types/turndown": "5.0.5",
|
"@types/turndown": "5.0.5",
|
||||||
"typescript": "catalog:",
|
"typescript": "catalog:",
|
||||||
},
|
},
|
||||||
|
@ -97,8 +95,6 @@
|
||||||
|
|
||||||
"@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=="],
|
"@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=="],
|
||||||
|
|
||||||
"@asamuzakjp/css-color": ["@asamuzakjp/css-color@3.2.0", "", { "dependencies": { "@csstools/css-calc": "^2.1.3", "@csstools/css-color-parser": "^3.0.9", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", "lru-cache": "^10.4.3" } }, "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw=="],
|
|
||||||
|
|
||||||
"@astrojs/compiler": ["@astrojs/compiler@2.12.0", "", {}, "sha512-7bCjW6tVDpUurQLeKBUN9tZ5kSv5qYrGmcn0sG0IwacL7isR2ZbyyA3AdZ4uxsuUFOS2SlgReTH7wkxO6zpqWA=="],
|
"@astrojs/compiler": ["@astrojs/compiler@2.12.0", "", {}, "sha512-7bCjW6tVDpUurQLeKBUN9tZ5kSv5qYrGmcn0sG0IwacL7isR2ZbyyA3AdZ4uxsuUFOS2SlgReTH7wkxO6zpqWA=="],
|
||||||
|
|
||||||
"@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.6.1", "", {}, "sha512-l5Pqf6uZu31aG+3Lv8nl/3s4DbUzdlxTWDof4pEpto6GUJNhhCbelVi9dEyurOVyqaelwmS9oSyOWOENSfgo9A=="],
|
"@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.6.1", "", {}, "sha512-l5Pqf6uZu31aG+3Lv8nl/3s4DbUzdlxTWDof4pEpto6GUJNhhCbelVi9dEyurOVyqaelwmS9oSyOWOENSfgo9A=="],
|
||||||
|
@ -157,16 +153,6 @@
|
||||||
|
|
||||||
"@cloudflare/workers-types": ["@cloudflare/workers-types@4.20250522.0", "", {}, "sha512-9RIffHobc35JWeddzBguGgPa4wLDr5x5F94+0/qy7LiV6pTBQ/M5qGEN9VA16IDT3EUpYI0WKh6VpcmeVEtVtw=="],
|
"@cloudflare/workers-types": ["@cloudflare/workers-types@4.20250522.0", "", {}, "sha512-9RIffHobc35JWeddzBguGgPa4wLDr5x5F94+0/qy7LiV6pTBQ/M5qGEN9VA16IDT3EUpYI0WKh6VpcmeVEtVtw=="],
|
||||||
|
|
||||||
"@csstools/color-helpers": ["@csstools/color-helpers@5.0.2", "", {}, "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA=="],
|
|
||||||
|
|
||||||
"@csstools/css-calc": ["@csstools/css-calc@2.1.4", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ=="],
|
|
||||||
|
|
||||||
"@csstools/css-color-parser": ["@csstools/css-color-parser@3.0.10", "", { "dependencies": { "@csstools/color-helpers": "^5.0.2", "@csstools/css-calc": "^2.1.4" }, "peerDependencies": { "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg=="],
|
|
||||||
|
|
||||||
"@csstools/css-parser-algorithms": ["@csstools/css-parser-algorithms@3.0.5", "", { "peerDependencies": { "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ=="],
|
|
||||||
|
|
||||||
"@csstools/css-tokenizer": ["@csstools/css-tokenizer@3.0.4", "", {}, "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw=="],
|
|
||||||
|
|
||||||
"@ctrl/tinycolor": ["@ctrl/tinycolor@4.1.0", "", {}, "sha512-WyOx8cJQ+FQus4Mm4uPIZA64gbk3Wxh0so5Lcii0aJifqwoVOlfFtorjLE0Hen4OYyHZMXDWqMmaQemBhgxFRQ=="],
|
"@ctrl/tinycolor": ["@ctrl/tinycolor@4.1.0", "", {}, "sha512-WyOx8cJQ+FQus4Mm4uPIZA64gbk3Wxh0so5Lcii0aJifqwoVOlfFtorjLE0Hen4OYyHZMXDWqMmaQemBhgxFRQ=="],
|
||||||
|
|
||||||
"@emnapi/runtime": ["@emnapi/runtime@1.4.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ=="],
|
"@emnapi/runtime": ["@emnapi/runtime@1.4.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ=="],
|
||||||
|
@ -407,8 +393,6 @@
|
||||||
|
|
||||||
"@types/js-yaml": ["@types/js-yaml@4.0.9", "", {}, "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg=="],
|
"@types/js-yaml": ["@types/js-yaml@4.0.9", "", {}, "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg=="],
|
||||||
|
|
||||||
"@types/jsdom": ["@types/jsdom@21.1.7", "", { "dependencies": { "@types/node": "*", "@types/tough-cookie": "*", "parse5": "^7.0.0" } }, "sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA=="],
|
|
||||||
|
|
||||||
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
||||||
|
|
||||||
"@types/luxon": ["@types/luxon@3.6.2", "", {}, "sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw=="],
|
"@types/luxon": ["@types/luxon@3.6.2", "", {}, "sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw=="],
|
||||||
|
@ -425,8 +409,6 @@
|
||||||
|
|
||||||
"@types/sax": ["@types/sax@1.2.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A=="],
|
"@types/sax": ["@types/sax@1.2.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A=="],
|
||||||
|
|
||||||
"@types/tough-cookie": ["@types/tough-cookie@4.0.5", "", {}, "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA=="],
|
|
||||||
|
|
||||||
"@types/turndown": ["@types/turndown@5.0.5", "", {}, "sha512-TL2IgGgc7B5j78rIccBtlYAnkuv8nUQqhQc+DSYV5j9Be9XOcm/SKOVRuA47xAVI3680Tk9B1d8flK2GWT2+4w=="],
|
"@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/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
|
||||||
|
@ -439,8 +421,6 @@
|
||||||
|
|
||||||
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
|
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
|
||||||
|
|
||||||
"agent-base": ["agent-base@7.1.3", "", {}, "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw=="],
|
|
||||||
|
|
||||||
"ai": ["ai@5.0.0-alpha.7", "", { "dependencies": { "@ai-sdk/gateway": "1.0.0-alpha.7", "@ai-sdk/provider": "2.0.0-alpha.7", "@ai-sdk/provider-utils": "3.0.0-alpha.7", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-ShCk3frIMdVtK9knvWKiFS7N6Vwnf8mLMv670+T//W9oqfoetSVPBhTF6Dy+oDM/bjVSsBf1BuYImLDvHICOIQ=="],
|
"ai": ["ai@5.0.0-alpha.7", "", { "dependencies": { "@ai-sdk/gateway": "1.0.0-alpha.7", "@ai-sdk/provider": "2.0.0-alpha.7", "@ai-sdk/provider-utils": "3.0.0-alpha.7", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-ShCk3frIMdVtK9knvWKiFS7N6Vwnf8mLMv670+T//W9oqfoetSVPBhTF6Dy+oDM/bjVSsBf1BuYImLDvHICOIQ=="],
|
||||||
|
|
||||||
"ansi-align": ["ansi-align@3.0.1", "", { "dependencies": { "string-width": "^4.1.0" } }, "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w=="],
|
"ansi-align": ["ansi-align@3.0.1", "", { "dependencies": { "string-width": "^4.1.0" } }, "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w=="],
|
||||||
|
@ -601,12 +581,8 @@
|
||||||
|
|
||||||
"cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
|
"cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
|
||||||
|
|
||||||
"cssstyle": ["cssstyle@4.3.1", "", { "dependencies": { "@asamuzakjp/css-color": "^3.1.2", "rrweb-cssom": "^0.8.0" } }, "sha512-ZgW+Jgdd7i52AaLYCriF8Mxqft0gD/R9i9wi6RWBhs1pqdPEzPjym7rvRKi397WmQFf3SlyUsszhw+VVCbx79Q=="],
|
|
||||||
|
|
||||||
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
||||||
|
|
||||||
"data-urls": ["data-urls@5.0.0", "", { "dependencies": { "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.0.0" } }, "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg=="],
|
|
||||||
|
|
||||||
"dateformat": ["dateformat@4.6.3", "", {}, "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA=="],
|
"dateformat": ["dateformat@4.6.3", "", {}, "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA=="],
|
||||||
|
|
||||||
"debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
"debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
||||||
|
@ -827,8 +803,6 @@
|
||||||
|
|
||||||
"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=="],
|
"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=="],
|
||||||
|
|
||||||
"html-encoding-sniffer": ["html-encoding-sniffer@4.0.0", "", { "dependencies": { "whatwg-encoding": "^3.1.1" } }, "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ=="],
|
|
||||||
|
|
||||||
"html-entities": ["html-entities@2.3.3", "", {}, "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA=="],
|
"html-entities": ["html-entities@2.3.3", "", {}, "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA=="],
|
||||||
|
|
||||||
"html-escaper": ["html-escaper@3.0.3", "", {}, "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ=="],
|
"html-escaper": ["html-escaper@3.0.3", "", {}, "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ=="],
|
||||||
|
@ -841,10 +815,6 @@
|
||||||
|
|
||||||
"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-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=="],
|
|
||||||
|
|
||||||
"https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
|
|
||||||
|
|
||||||
"i18next": ["i18next@23.16.8", "", { "dependencies": { "@babel/runtime": "^7.23.2" } }, "sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg=="],
|
"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=="],
|
"iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
|
||||||
|
@ -887,8 +857,6 @@
|
||||||
|
|
||||||
"is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="],
|
"is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="],
|
||||||
|
|
||||||
"is-potential-custom-element-name": ["is-potential-custom-element-name@1.0.1", "", {}, "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="],
|
|
||||||
|
|
||||||
"is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="],
|
"is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="],
|
||||||
|
|
||||||
"is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="],
|
"is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="],
|
||||||
|
@ -911,8 +879,6 @@
|
||||||
|
|
||||||
"js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
|
"js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
|
||||||
|
|
||||||
"jsdom": ["jsdom@26.1.0", "", { "dependencies": { "cssstyle": "^4.2.1", "data-urls": "^5.0.0", "decimal.js": "^10.5.0", "html-encoding-sniffer": "^4.0.0", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.6", "is-potential-custom-element-name": "^1.0.1", "nwsapi": "^2.2.16", "parse5": "^7.2.1", "rrweb-cssom": "^0.8.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^5.1.1", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^7.0.0", "whatwg-encoding": "^3.1.1", "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.1.1", "ws": "^8.18.0", "xml-name-validator": "^5.0.0" }, "peerDependencies": { "canvas": "^3.0.0" }, "optionalPeers": ["canvas"] }, "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg=="],
|
|
||||||
|
|
||||||
"jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
|
"jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
|
||||||
|
|
||||||
"json-rpc-2.0": ["json-rpc-2.0@1.7.0", "", {}, "sha512-asnLgC1qD5ytP+fvBP8uL0rvj+l8P6iYICbzZ8dVxCpESffVjzA7KkYkbKCIbavs7cllwH1ZUaNtJwphdeRqpg=="],
|
"json-rpc-2.0": ["json-rpc-2.0@1.7.0", "", {}, "sha512-asnLgC1qD5ytP+fvBP8uL0rvj+l8P6iYICbzZ8dVxCpESffVjzA7KkYkbKCIbavs7cllwH1ZUaNtJwphdeRqpg=="],
|
||||||
|
@ -1105,8 +1071,6 @@
|
||||||
|
|
||||||
"nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="],
|
"nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="],
|
||||||
|
|
||||||
"nwsapi": ["nwsapi@2.2.20", "", {}, "sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA=="],
|
|
||||||
|
|
||||||
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
|
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
|
||||||
|
|
||||||
"object-hash": ["object-hash@2.2.0", "", {}, "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw=="],
|
"object-hash": ["object-hash@2.2.0", "", {}, "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw=="],
|
||||||
|
@ -1283,8 +1247,6 @@
|
||||||
|
|
||||||
"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=="],
|
"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=="],
|
||||||
|
|
||||||
"rrweb-cssom": ["rrweb-cssom@0.8.0", "", {}, "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw=="],
|
|
||||||
|
|
||||||
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
|
"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=="],
|
"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=="],
|
||||||
|
@ -1295,8 +1257,6 @@
|
||||||
|
|
||||||
"sax": ["sax@1.2.1", "", {}, "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA=="],
|
"sax": ["sax@1.2.1", "", {}, "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA=="],
|
||||||
|
|
||||||
"saxes": ["saxes@6.0.0", "", { "dependencies": { "xmlchars": "^2.2.0" } }, "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA=="],
|
|
||||||
|
|
||||||
"semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
|
"semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
|
||||||
|
|
||||||
"send": ["send@1.2.0", "", { "dependencies": { "debug": "^4.3.5", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.0", "mime-types": "^3.0.1", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.1" } }, "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw=="],
|
"send": ["send@1.2.0", "", { "dependencies": { "debug": "^4.3.5", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.0", "mime-types": "^3.0.1", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.1" } }, "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw=="],
|
||||||
|
@ -1393,8 +1353,6 @@
|
||||||
|
|
||||||
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
||||||
|
|
||||||
"symbol-tree": ["symbol-tree@3.2.4", "", {}, "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="],
|
|
||||||
|
|
||||||
"tar-fs": ["tar-fs@3.0.9", "", { "dependencies": { "pump": "^3.0.0", "tar-stream": "^3.1.5" }, "optionalDependencies": { "bare-fs": "^4.0.1", "bare-path": "^3.0.0" } }, "sha512-XF4w9Xp+ZQgifKakjZYmFdkLoSWd34VGKcsTCwlNWM7QG3ZbaxnTsaBwnjFZqHRf/rROxaR8rXnbtwdvaDI+lA=="],
|
"tar-fs": ["tar-fs@3.0.9", "", { "dependencies": { "pump": "^3.0.0", "tar-stream": "^3.1.5" }, "optionalDependencies": { "bare-fs": "^4.0.1", "bare-path": "^3.0.0" } }, "sha512-XF4w9Xp+ZQgifKakjZYmFdkLoSWd34VGKcsTCwlNWM7QG3ZbaxnTsaBwnjFZqHRf/rROxaR8rXnbtwdvaDI+lA=="],
|
||||||
|
|
||||||
"tar-stream": ["tar-stream@3.1.7", "", { "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", "streamx": "^2.15.0" } }, "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ=="],
|
"tar-stream": ["tar-stream@3.1.7", "", { "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", "streamx": "^2.15.0" } }, "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ=="],
|
||||||
|
@ -1409,19 +1367,13 @@
|
||||||
|
|
||||||
"tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="],
|
"tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="],
|
||||||
|
|
||||||
"tldts": ["tldts@6.1.86", "", { "dependencies": { "tldts-core": "^6.1.86" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ=="],
|
|
||||||
|
|
||||||
"tldts-core": ["tldts-core@6.1.86", "", {}, "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA=="],
|
|
||||||
|
|
||||||
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
|
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
|
||||||
|
|
||||||
"token-types": ["token-types@6.0.0", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA=="],
|
"token-types": ["token-types@6.0.0", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA=="],
|
||||||
|
|
||||||
"toolbeam-docs-theme": ["toolbeam-docs-theme@0.2.4", "", { "peerDependencies": { "@astrojs/starlight": "^0.34.3", "astro": "^5.7.13" } }, "sha512-W5mdbcgRpTBDFyEdcU81USs3MFZoXMInpSznc/AFZCwqz8atk4iBNDIlhvihpGHY54Nf5crKmZwJjxVojkHFvA=="],
|
"toolbeam-docs-theme": ["toolbeam-docs-theme@0.2.4", "", { "peerDependencies": { "@astrojs/starlight": "^0.34.3", "astro": "^5.7.13" } }, "sha512-W5mdbcgRpTBDFyEdcU81USs3MFZoXMInpSznc/AFZCwqz8atk4iBNDIlhvihpGHY54Nf5crKmZwJjxVojkHFvA=="],
|
||||||
|
|
||||||
"tough-cookie": ["tough-cookie@5.1.2", "", { "dependencies": { "tldts": "^6.1.32" } }, "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A=="],
|
"tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
|
||||||
|
|
||||||
"tr46": ["tr46@5.1.1", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw=="],
|
|
||||||
|
|
||||||
"trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="],
|
"trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="],
|
||||||
|
|
||||||
|
@ -1519,17 +1471,11 @@
|
||||||
|
|
||||||
"vscode-languageserver-types": ["vscode-languageserver-types@3.17.3", "", {}, "sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA=="],
|
"vscode-languageserver-types": ["vscode-languageserver-types@3.17.3", "", {}, "sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA=="],
|
||||||
|
|
||||||
"w3c-xmlserializer": ["w3c-xmlserializer@5.0.0", "", { "dependencies": { "xml-name-validator": "^5.0.0" } }, "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA=="],
|
|
||||||
|
|
||||||
"web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="],
|
"web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="],
|
||||||
|
|
||||||
"webidl-conversions": ["webidl-conversions@7.0.0", "", {}, "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="],
|
"webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
|
||||||
|
|
||||||
"whatwg-encoding": ["whatwg-encoding@3.1.1", "", { "dependencies": { "iconv-lite": "0.6.3" } }, "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ=="],
|
"whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
|
||||||
|
|
||||||
"whatwg-mimetype": ["whatwg-mimetype@4.0.0", "", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="],
|
|
||||||
|
|
||||||
"whatwg-url": ["whatwg-url@14.2.0", "", { "dependencies": { "tr46": "^5.1.0", "webidl-conversions": "^7.0.0" } }, "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw=="],
|
|
||||||
|
|
||||||
"which-pm-runs": ["which-pm-runs@1.1.0", "", {}, "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA=="],
|
"which-pm-runs": ["which-pm-runs@1.1.0", "", {}, "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA=="],
|
||||||
|
|
||||||
|
@ -1541,16 +1487,10 @@
|
||||||
|
|
||||||
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
|
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
|
||||||
|
|
||||||
"ws": ["ws@8.18.2", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ=="],
|
|
||||||
|
|
||||||
"xml-name-validator": ["xml-name-validator@5.0.0", "", {}, "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg=="],
|
|
||||||
|
|
||||||
"xml2js": ["xml2js@0.6.2", "", { "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" } }, "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA=="],
|
"xml2js": ["xml2js@0.6.2", "", { "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" } }, "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA=="],
|
||||||
|
|
||||||
"xmlbuilder": ["xmlbuilder@11.0.1", "", {}, "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="],
|
"xmlbuilder": ["xmlbuilder@11.0.1", "", {}, "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="],
|
||||||
|
|
||||||
"xmlchars": ["xmlchars@2.2.0", "", {}, "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="],
|
|
||||||
|
|
||||||
"xxhash-wasm": ["xxhash-wasm@1.1.0", "", {}, "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA=="],
|
"xxhash-wasm": ["xxhash-wasm@1.1.0", "", {}, "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA=="],
|
||||||
|
|
||||||
"yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
|
"yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
|
||||||
|
@ -1573,8 +1513,6 @@
|
||||||
|
|
||||||
"zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
|
"zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
|
||||||
|
|
||||||
"@asamuzakjp/css-color/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
|
||||||
|
|
||||||
"@astrojs/mdx/@astrojs/markdown-remark": ["@astrojs/markdown-remark@6.3.2", "", { "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.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-bO35JbWpVvyKRl7cmSJD822e8YA8ThR/YbUsciWNA7yTcqpIAL2hJDToWP5KcZBWxGT6IOdOkHSXARSNZc4l/Q=="],
|
"@astrojs/mdx/@astrojs/markdown-remark": ["@astrojs/markdown-remark@6.3.2", "", { "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.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-bO35JbWpVvyKRl7cmSJD822e8YA8ThR/YbUsciWNA7yTcqpIAL2hJDToWP5KcZBWxGT6IOdOkHSXARSNZc4l/Q=="],
|
||||||
|
|
||||||
"@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
"@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||||
|
@ -1611,8 +1549,6 @@
|
||||||
|
|
||||||
"mdast-util-find-and-replace/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
|
"mdast-util-find-and-replace/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
|
||||||
|
|
||||||
"node-fetch/whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
|
|
||||||
|
|
||||||
"opencontrol/hono": ["hono@4.7.4", "", {}, "sha512-Pst8FuGqz3L7tFF+u9Pu70eI0xa5S3LPUmrNd5Jm8nTHze9FxLTK9Kaj5g/k4UcwuJSXTP65SyHOPLrffpcAJg=="],
|
"opencontrol/hono": ["hono@4.7.4", "", {}, "sha512-Pst8FuGqz3L7tFF+u9Pu70eI0xa5S3LPUmrNd5Jm8nTHze9FxLTK9Kaj5g/k4UcwuJSXTP65SyHOPLrffpcAJg=="],
|
||||||
|
|
||||||
"opencontrol/zod-to-json-schema": ["zod-to-json-schema@3.24.3", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-HIAfWdYIt1sssHfYZFCXp4rU1w2r8hVVXYIlmoa0r0gABLs5di3RCqPU5DDROogVz1pAdYBaz7HK5n9pSUNs3A=="],
|
"opencontrol/zod-to-json-schema": ["zod-to-json-schema@3.24.3", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-HIAfWdYIt1sssHfYZFCXp4rU1w2r8hVVXYIlmoa0r0gABLs5di3RCqPU5DDROogVz1pAdYBaz7HK5n9pSUNs3A=="],
|
||||||
|
@ -1639,8 +1575,6 @@
|
||||||
|
|
||||||
"token-types/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
"token-types/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
||||||
|
|
||||||
"tr46/punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
|
||||||
|
|
||||||
"unstorage/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
"unstorage/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
||||||
|
|
||||||
"vscode-languageserver-protocol/vscode-jsonrpc": ["vscode-jsonrpc@8.1.0", "", {}, "sha512-6TDy/abTQk+zDGYazgbIPc+4JoXdwC8NHU9Pbn4UJP1fehUyZmM4RHp5IthX7A6L5KS30PRui+j+tbbMMMafdw=="],
|
"vscode-languageserver-protocol/vscode-jsonrpc": ["vscode-jsonrpc@8.1.0", "", {}, "sha512-6TDy/abTQk+zDGYazgbIPc+4JoXdwC8NHU9Pbn4UJP1fehUyZmM4RHp5IthX7A6L5KS30PRui+j+tbbMMMafdw=="],
|
||||||
|
@ -1659,10 +1593,6 @@
|
||||||
|
|
||||||
"bl/buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
"bl/buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
||||||
|
|
||||||
"node-fetch/whatwg-url/tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
|
|
||||||
|
|
||||||
"node-fetch/whatwg-url/webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
|
|
||||||
|
|
||||||
"pino-pretty/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
"pino-pretty/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||||
|
|
||||||
"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=="],
|
"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=="],
|
||||||
|
|
16
packages/function/sst-env.d.ts
vendored
16
packages/function/sst-env.d.ts
vendored
|
@ -6,20 +6,20 @@
|
||||||
import "sst"
|
import "sst"
|
||||||
declare module "sst" {
|
declare module "sst" {
|
||||||
export interface Resource {
|
export interface Resource {
|
||||||
"Web": {
|
Web: {
|
||||||
"type": "sst.cloudflare.StaticSite"
|
type: "sst.cloudflare.StaticSite"
|
||||||
"url": string
|
url: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// cloudflare
|
// cloudflare
|
||||||
import * as cloudflare from "@cloudflare/workers-types";
|
import * as cloudflare from "@cloudflare/workers-types"
|
||||||
declare module "sst" {
|
declare module "sst" {
|
||||||
export interface Resource {
|
export interface Resource {
|
||||||
"Api": cloudflare.Service
|
Api: cloudflare.Service
|
||||||
"Bucket": cloudflare.R2Bucket
|
Bucket: cloudflare.R2Bucket
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
import "sst"
|
import "sst"
|
||||||
export {}
|
export {}
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tsconfig/bun": "1.0.7",
|
"@tsconfig/bun": "1.0.7",
|
||||||
"@types/bun": "latest",
|
"@types/bun": "latest",
|
||||||
"@types/jsdom": "21.1.7",
|
|
||||||
"@types/turndown": "5.0.5",
|
"@types/turndown": "5.0.5",
|
||||||
"typescript": "catalog:"
|
"typescript": "catalog:"
|
||||||
},
|
},
|
||||||
|
@ -28,7 +27,6 @@
|
||||||
"env-paths": "3.0.0",
|
"env-paths": "3.0.0",
|
||||||
"hono": "4.7.10",
|
"hono": "4.7.10",
|
||||||
"hono-openapi": "0.4.8",
|
"hono-openapi": "0.4.8",
|
||||||
"jsdom": "26.1.0",
|
|
||||||
"remeda": "2.22.3",
|
"remeda": "2.22.3",
|
||||||
"ts-lsp-client": "1.0.3",
|
"ts-lsp-client": "1.0.3",
|
||||||
"turndown": "7.2.0",
|
"turndown": "7.2.0",
|
||||||
|
|
|
@ -28,7 +28,7 @@ for (const [os, arch] of targets) {
|
||||||
console.log(`building ${os}-${arch}`)
|
console.log(`building ${os}-${arch}`)
|
||||||
const name = `${pkg.name}-${os}-${arch}`
|
const name = `${pkg.name}-${os}-${arch}`
|
||||||
await $`mkdir -p dist/${name}/bin`
|
await $`mkdir -p dist/${name}/bin`
|
||||||
await $`GOOS=${os} GOARCH=${GOARCH[arch]} go build -o ../opencode/dist/${name}/bin/tui ../tui/main.go`.cwd(
|
await $`GOOS=${os} GOARCH=${GOARCH[arch]} go build -ldflags="-s -w -X github.com/sst/opencode/internal/version.Version=${version}" -o ../opencode/dist/${name}/bin/tui ../tui/main.go`.cwd(
|
||||||
"../tui",
|
"../tui",
|
||||||
)
|
)
|
||||||
await $`bun build --compile --minify --target=bun-${os}-${arch} --outfile=dist/${name}/bin/opencode ./src/index.ts ./dist/${name}/bin/tui`
|
await $`bun build --compile --minify --target=bun-${os}-${arch} --outfile=dist/${name}/bin/opencode ./src/index.ts ./dist/${name}/bin/tui`
|
||||||
|
|
|
@ -1,40 +1,40 @@
|
||||||
import fs from "fs/promises";
|
import fs from "fs/promises"
|
||||||
import { AppPath } from "./path";
|
import { AppPath } from "./path"
|
||||||
import { Log } from "../util/log";
|
import { Log } from "../util/log"
|
||||||
import { Context } from "../util/context";
|
import { Context } from "../util/context"
|
||||||
|
|
||||||
export namespace App {
|
export namespace App {
|
||||||
const log = Log.create({ service: "app" });
|
const log = Log.create({ service: "app" })
|
||||||
|
|
||||||
export type Info = Awaited<ReturnType<typeof create>>;
|
export type Info = Awaited<ReturnType<typeof create>>
|
||||||
|
|
||||||
const ctx = Context.create<Info>("app");
|
const ctx = Context.create<Info>("app")
|
||||||
|
|
||||||
async function create(input: { directory: string }) {
|
async function create(input: { directory: string }) {
|
||||||
const dataDir = AppPath.data(input.directory);
|
const dataDir = AppPath.data(input.directory)
|
||||||
await fs.mkdir(dataDir, { recursive: true });
|
await fs.mkdir(dataDir, { recursive: true })
|
||||||
await Log.file(input.directory);
|
await Log.file(input.directory)
|
||||||
|
|
||||||
log.info("created", { path: dataDir });
|
log.info("created", { path: dataDir })
|
||||||
|
|
||||||
const services = new Map<
|
const services = new Map<
|
||||||
any,
|
any,
|
||||||
{
|
{
|
||||||
state: any;
|
state: any
|
||||||
shutdown?: (input: any) => Promise<void>;
|
shutdown?: (input: any) => Promise<void>
|
||||||
}
|
}
|
||||||
>();
|
>()
|
||||||
|
|
||||||
const result = {
|
const result = {
|
||||||
get services() {
|
get services() {
|
||||||
return services;
|
return services
|
||||||
},
|
},
|
||||||
get root() {
|
get root() {
|
||||||
return input.directory;
|
return input.directory
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
|
|
||||||
return result;
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
export function state<State>(
|
export function state<State>(
|
||||||
|
@ -43,36 +43,36 @@ export namespace App {
|
||||||
shutdown?: (state: Awaited<State>) => Promise<void>,
|
shutdown?: (state: Awaited<State>) => Promise<void>,
|
||||||
) {
|
) {
|
||||||
return () => {
|
return () => {
|
||||||
const app = ctx.use();
|
const app = ctx.use()
|
||||||
const services = app.services;
|
const services = app.services
|
||||||
if (!services.has(key)) {
|
if (!services.has(key)) {
|
||||||
log.info("registering service", { name: key });
|
log.info("registering service", { name: key })
|
||||||
services.set(key, {
|
services.set(key, {
|
||||||
state: init(app),
|
state: init(app),
|
||||||
shutdown: shutdown,
|
shutdown: shutdown,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
return services.get(key)?.state as State;
|
return services.get(key)?.state as State
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function use() {
|
export async function use() {
|
||||||
return ctx.use();
|
return ctx.use()
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function provide<T extends (app: Info) => any>(
|
export async function provide<T extends (app: Info) => any>(
|
||||||
input: { directory: string },
|
input: { directory: string },
|
||||||
cb: T,
|
cb: T,
|
||||||
) {
|
) {
|
||||||
const app = await create(input);
|
const app = await create(input)
|
||||||
|
|
||||||
return ctx.provide(app, async () => {
|
return ctx.provide(app, async () => {
|
||||||
const result = await cb(app);
|
const result = await cb(app)
|
||||||
for (const [key, entry] of app.services.entries()) {
|
for (const [key, entry] of app.services.entries()) {
|
||||||
log.info("shutdown", { name: key });
|
log.info("shutdown", { name: key })
|
||||||
await entry.shutdown?.(await entry.state);
|
await entry.shutdown?.(await entry.state)
|
||||||
}
|
}
|
||||||
return result;
|
return result
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import path from "path";
|
import path from "path"
|
||||||
|
|
||||||
export namespace AppPath {
|
export namespace AppPath {
|
||||||
export function data(input: string) {
|
export function data(input: string) {
|
||||||
return path.join(input, ".opencode");
|
return path.join(input, ".opencode")
|
||||||
}
|
}
|
||||||
|
|
||||||
export function storage(input: string) {
|
export function storage(input: string) {
|
||||||
return path.join(data(input), "storage");
|
return path.join(data(input), "storage")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import path from "path";
|
import path from "path"
|
||||||
import { Log } from "../util/log";
|
import { Log } from "../util/log"
|
||||||
export namespace BunProc {
|
export namespace BunProc {
|
||||||
const log = Log.create({ service: "bun" });
|
const log = Log.create({ service: "bun" })
|
||||||
|
|
||||||
export function run(
|
export function run(
|
||||||
cmd: string[],
|
cmd: string[],
|
||||||
|
@ -10,11 +10,11 @@ export namespace BunProc {
|
||||||
const root =
|
const root =
|
||||||
process.argv0 !== "bun"
|
process.argv0 !== "bun"
|
||||||
? path.resolve(process.cwd(), process.argv0)
|
? path.resolve(process.cwd(), process.argv0)
|
||||||
: process.argv0;
|
: process.argv0
|
||||||
log.info("running", {
|
log.info("running", {
|
||||||
cmd: [root, ...cmd],
|
cmd: [root, ...cmd],
|
||||||
options,
|
options,
|
||||||
});
|
})
|
||||||
const result = Bun.spawnSync([root, ...cmd], {
|
const result = Bun.spawnSync([root, ...cmd], {
|
||||||
...options,
|
...options,
|
||||||
argv0: "bun",
|
argv0: "bun",
|
||||||
|
@ -22,7 +22,11 @@ export namespace BunProc {
|
||||||
...process.env,
|
...process.env,
|
||||||
...options?.env,
|
...options?.env,
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
return result;
|
if (result.exitCode !== 0) {
|
||||||
|
console.error(result.stderr?.toString("utf8") ?? "")
|
||||||
|
throw new Error(`Command failed with exit code ${result.exitCode}`)
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
import { z, type ZodType } from "zod";
|
import { z, type ZodType } from "zod"
|
||||||
import { App } from "../app/app";
|
import { App } from "../app/app"
|
||||||
import { Log } from "../util/log";
|
import { Log } from "../util/log"
|
||||||
|
|
||||||
export namespace Bus {
|
export namespace Bus {
|
||||||
const log = Log.create({ service: "bus" });
|
const log = Log.create({ service: "bus" })
|
||||||
type Subscription = (event: any) => void;
|
type Subscription = (event: any) => void
|
||||||
|
|
||||||
const state = App.state("bus", () => {
|
const state = App.state("bus", () => {
|
||||||
const subscriptions = new Map<any, Subscription[]>();
|
const subscriptions = new Map<any, Subscription[]>()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
subscriptions,
|
subscriptions,
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
export type EventDefinition = ReturnType<typeof event>;
|
export type EventDefinition = ReturnType<typeof event>
|
||||||
|
|
||||||
const registry = new Map<string, EventDefinition>();
|
const registry = new Map<string, EventDefinition>()
|
||||||
|
|
||||||
export function event<Type extends string, Properties extends ZodType>(
|
export function event<Type extends string, Properties extends ZodType>(
|
||||||
type: Type,
|
type: Type,
|
||||||
|
@ -25,9 +25,9 @@ export namespace Bus {
|
||||||
const result = {
|
const result = {
|
||||||
type,
|
type,
|
||||||
properties,
|
properties,
|
||||||
};
|
}
|
||||||
registry.set(type, result);
|
registry.set(type, result)
|
||||||
return result;
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
export function payloads() {
|
export function payloads() {
|
||||||
|
@ -46,7 +46,7 @@ export namespace Bus {
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.toArray() as any,
|
.toArray() as any,
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function publish<Definition extends EventDefinition>(
|
export function publish<Definition extends EventDefinition>(
|
||||||
|
@ -56,14 +56,14 @@ export namespace Bus {
|
||||||
const payload = {
|
const payload = {
|
||||||
type: def.type,
|
type: def.type,
|
||||||
properties,
|
properties,
|
||||||
};
|
}
|
||||||
log.info("publishing", {
|
log.info("publishing", {
|
||||||
type: def.type,
|
type: def.type,
|
||||||
});
|
})
|
||||||
for (const key of [def.type, "*"]) {
|
for (const key of [def.type, "*"]) {
|
||||||
const match = state().subscriptions.get(key);
|
const match = state().subscriptions.get(key)
|
||||||
for (const sub of match ?? []) {
|
for (const sub of match ?? []) {
|
||||||
sub(payload);
|
sub(payload)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,31 +71,31 @@ export namespace Bus {
|
||||||
export function subscribe<Definition extends EventDefinition>(
|
export function subscribe<Definition extends EventDefinition>(
|
||||||
def: Definition,
|
def: Definition,
|
||||||
callback: (event: {
|
callback: (event: {
|
||||||
type: Definition["type"];
|
type: Definition["type"]
|
||||||
properties: z.infer<Definition["properties"]>;
|
properties: z.infer<Definition["properties"]>
|
||||||
}) => void,
|
}) => void,
|
||||||
) {
|
) {
|
||||||
return raw(def.type, callback);
|
return raw(def.type, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function subscribeAll(callback: (event: any) => void) {
|
export function subscribeAll(callback: (event: any) => void) {
|
||||||
return raw("*", callback);
|
return raw("*", callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
function raw(type: string, callback: (event: any) => void) {
|
function raw(type: string, callback: (event: any) => void) {
|
||||||
log.info("subscribing", { type });
|
log.info("subscribing", { type })
|
||||||
const subscriptions = state().subscriptions;
|
const subscriptions = state().subscriptions
|
||||||
let match = subscriptions.get(type) ?? [];
|
let match = subscriptions.get(type) ?? []
|
||||||
match.push(callback);
|
match.push(callback)
|
||||||
subscriptions.set(type, match);
|
subscriptions.set(type, match)
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
log.info("unsubscribing", { type });
|
log.info("unsubscribing", { type })
|
||||||
const match = subscriptions.get(type);
|
const match = subscriptions.get(type)
|
||||||
if (!match) return;
|
if (!match) return
|
||||||
const index = match.indexOf(callback);
|
const index = match.indexOf(callback)
|
||||||
if (index === -1) return;
|
if (index === -1) return
|
||||||
match.splice(index, 1);
|
match.splice(index, 1)
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,51 +1,51 @@
|
||||||
import path from "path";
|
import path from "path"
|
||||||
import { Log } from "../util/log";
|
import { Log } from "../util/log"
|
||||||
import { z } from "zod";
|
import { z } from "zod"
|
||||||
import { App } from "../app/app";
|
import { App } from "../app/app"
|
||||||
import { Provider } from "../provider/provider";
|
import { Provider } from "../provider/provider"
|
||||||
|
|
||||||
export namespace Config {
|
export namespace Config {
|
||||||
const log = Log.create({ service: "config" });
|
const log = Log.create({ service: "config" })
|
||||||
|
|
||||||
export const state = App.state("config", async (app) => {
|
export const state = App.state("config", async (app) => {
|
||||||
const result = await load(app.root);
|
const result = await load(app.root)
|
||||||
return result;
|
return result
|
||||||
});
|
})
|
||||||
|
|
||||||
export const Info = z
|
export const Info = z
|
||||||
.object({
|
.object({
|
||||||
providers: Provider.Info.array().optional(),
|
providers: Provider.Info.array().optional(),
|
||||||
})
|
})
|
||||||
.strict();
|
.strict()
|
||||||
|
|
||||||
export type Info = z.output<typeof Info>;
|
export type Info = z.output<typeof Info>
|
||||||
|
|
||||||
export function get() {
|
export function get() {
|
||||||
return state();
|
return state()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function load(directory: string) {
|
async function load(directory: string) {
|
||||||
let result: Info = {};
|
let result: Info = {}
|
||||||
for (const file of ["opencode.jsonc", "opencode.json"]) {
|
for (const file of ["opencode.jsonc", "opencode.json"]) {
|
||||||
const resolved = path.join(directory, file);
|
const resolved = path.join(directory, file)
|
||||||
log.info("searching", { path: resolved });
|
log.info("searching", { path: resolved })
|
||||||
try {
|
try {
|
||||||
result = await import(path.join(directory, file)).then((mod) =>
|
result = await import(path.join(directory, file)).then((mod) =>
|
||||||
Info.parse(mod.default),
|
Info.parse(mod.default),
|
||||||
);
|
)
|
||||||
log.info("found", { path: resolved });
|
log.info("found", { path: resolved })
|
||||||
break;
|
break
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof z.ZodError) {
|
if (e instanceof z.ZodError) {
|
||||||
for (const issue of e.issues) {
|
for (const issue of e.issues) {
|
||||||
log.info(issue.message);
|
log.info(issue.message)
|
||||||
}
|
}
|
||||||
throw e;
|
throw e
|
||||||
}
|
}
|
||||||
continue;
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.info("loaded", result);
|
log.info("loaded", result)
|
||||||
return result;
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
import envpaths from "env-paths";
|
import envpaths from "env-paths"
|
||||||
import fs from "fs/promises";
|
import fs from "fs/promises"
|
||||||
const paths = envpaths("opencode", {
|
const paths = envpaths("opencode", {
|
||||||
suffix: "",
|
suffix: "",
|
||||||
});
|
})
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
fs.mkdir(paths.config, { recursive: true }),
|
fs.mkdir(paths.config, { recursive: true }),
|
||||||
fs.mkdir(paths.cache, { recursive: true }),
|
fs.mkdir(paths.cache, { recursive: true }),
|
||||||
]);
|
])
|
||||||
|
|
||||||
export namespace Global {
|
export namespace Global {
|
||||||
export function config() {
|
export function config() {
|
||||||
return paths.config;
|
return paths.config
|
||||||
}
|
}
|
||||||
|
|
||||||
export function cache() {
|
export function cache() {
|
||||||
return paths.cache;
|
return paths.cache
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,28 +1,28 @@
|
||||||
import { z } from "zod";
|
import { z } from "zod"
|
||||||
import { randomBytes } from "crypto";
|
import { randomBytes } from "crypto"
|
||||||
|
|
||||||
export namespace Identifier {
|
export namespace Identifier {
|
||||||
const prefixes = {
|
const prefixes = {
|
||||||
session: "ses",
|
session: "ses",
|
||||||
message: "msg",
|
message: "msg",
|
||||||
} as const;
|
} as const
|
||||||
|
|
||||||
export function schema(prefix: keyof typeof prefixes) {
|
export function schema(prefix: keyof typeof prefixes) {
|
||||||
return z.string().startsWith(prefixes[prefix]);
|
return z.string().startsWith(prefixes[prefix])
|
||||||
}
|
}
|
||||||
|
|
||||||
const LENGTH = 26;
|
const LENGTH = 26
|
||||||
|
|
||||||
// State for monotonic ID generation
|
// State for monotonic ID generation
|
||||||
let lastTimestamp = 0;
|
let lastTimestamp = 0
|
||||||
let counter = 0;
|
let counter = 0
|
||||||
|
|
||||||
export function ascending(prefix: keyof typeof prefixes, given?: string) {
|
export function ascending(prefix: keyof typeof prefixes, given?: string) {
|
||||||
return generateID(prefix, false, given);
|
return generateID(prefix, false, given)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function descending(prefix: keyof typeof prefixes, given?: string) {
|
export function descending(prefix: keyof typeof prefixes, given?: string) {
|
||||||
return generateID(prefix, true, given);
|
return generateID(prefix, true, given)
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateID(
|
function generateID(
|
||||||
|
@ -31,44 +31,44 @@ export namespace Identifier {
|
||||||
given?: string,
|
given?: string,
|
||||||
): string {
|
): string {
|
||||||
if (!given) {
|
if (!given) {
|
||||||
return generateNewID(prefix, descending);
|
return generateNewID(prefix, descending)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!given.startsWith(prefixes[prefix])) {
|
if (!given.startsWith(prefixes[prefix])) {
|
||||||
throw new Error(`ID ${given} does not start with ${prefixes[prefix]}`);
|
throw new Error(`ID ${given} does not start with ${prefixes[prefix]}`)
|
||||||
}
|
}
|
||||||
return given;
|
return given
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateNewID(
|
function generateNewID(
|
||||||
prefix: keyof typeof prefixes,
|
prefix: keyof typeof prefixes,
|
||||||
descending: boolean,
|
descending: boolean,
|
||||||
): string {
|
): string {
|
||||||
const currentTimestamp = Date.now();
|
const currentTimestamp = Date.now()
|
||||||
|
|
||||||
if (currentTimestamp !== lastTimestamp) {
|
if (currentTimestamp !== lastTimestamp) {
|
||||||
lastTimestamp = currentTimestamp;
|
lastTimestamp = currentTimestamp
|
||||||
counter = 0;
|
counter = 0
|
||||||
}
|
}
|
||||||
counter++;
|
counter++
|
||||||
|
|
||||||
let now = BigInt(currentTimestamp) * BigInt(0x1000) + BigInt(counter);
|
let now = BigInt(currentTimestamp) * BigInt(0x1000) + BigInt(counter)
|
||||||
|
|
||||||
now = descending ? ~now : now;
|
now = descending ? ~now : now
|
||||||
|
|
||||||
const timeBytes = Buffer.alloc(6);
|
const timeBytes = Buffer.alloc(6)
|
||||||
for (let i = 0; i < 6; i++) {
|
for (let i = 0; i < 6; i++) {
|
||||||
timeBytes[i] = Number((now >> BigInt(40 - 8 * i)) & BigInt(0xff));
|
timeBytes[i] = Number((now >> BigInt(40 - 8 * i)) & BigInt(0xff))
|
||||||
}
|
}
|
||||||
|
|
||||||
const randLength = (LENGTH - 12) / 2;
|
const randLength = (LENGTH - 12) / 2
|
||||||
const random = randomBytes(randLength);
|
const random = randomBytes(randLength)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
prefixes[prefix] +
|
prefixes[prefix] +
|
||||||
"_" +
|
"_" +
|
||||||
timeBytes.toString("hex") +
|
timeBytes.toString("hex") +
|
||||||
random.toString("hex")
|
random.toString("hex")
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ cli.command("", "Start the opencode in interactive mode").action(async () => {
|
||||||
if (!(await file.exists())) {
|
if (!(await file.exists())) {
|
||||||
console.log("installing tui binary...")
|
console.log("installing tui binary...")
|
||||||
await Bun.write(file, blob, { mode: 0o755 })
|
await Bun.write(file, blob, { mode: 0o755 })
|
||||||
|
await fs.chmod(binary, 0o755)
|
||||||
}
|
}
|
||||||
cwd = process.cwd()
|
cwd = process.cwd()
|
||||||
cmd = [binary]
|
cmd = [binary]
|
||||||
|
|
|
@ -115,7 +115,7 @@ export namespace LLM {
|
||||||
provider.id,
|
provider.id,
|
||||||
)
|
)
|
||||||
if (!(await Bun.file(path.join(dir, "package.json")).exists())) {
|
if (!(await Bun.file(path.join(dir, "package.json")).exists())) {
|
||||||
BunProc.run(["add", "--exact", `@ai-sdk/${provider.id}@alpha`], {
|
BunProc.run(["add", `@ai-sdk/${provider.id}@alpha`], {
|
||||||
cwd: Global.cache(),
|
cwd: Global.cache(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
import { spawn } from "child_process";
|
import { spawn } from "child_process"
|
||||||
import path from "path";
|
import path from "path"
|
||||||
import {
|
import {
|
||||||
createMessageConnection,
|
createMessageConnection,
|
||||||
StreamMessageReader,
|
StreamMessageReader,
|
||||||
StreamMessageWriter,
|
StreamMessageWriter,
|
||||||
} from "vscode-jsonrpc/node";
|
} from "vscode-jsonrpc/node"
|
||||||
import type { Diagnostic as VSCodeDiagnostic } from "vscode-languageserver-types";
|
import type { Diagnostic as VSCodeDiagnostic } from "vscode-languageserver-types"
|
||||||
import { App } from "../app/app";
|
import { App } from "../app/app"
|
||||||
import { Log } from "../util/log";
|
import { Log } from "../util/log"
|
||||||
import { LANGUAGE_EXTENSIONS } from "./language";
|
import { LANGUAGE_EXTENSIONS } from "./language"
|
||||||
import { Bus } from "../bus";
|
import { Bus } from "../bus"
|
||||||
import z from "zod";
|
import z from "zod"
|
||||||
|
|
||||||
export namespace LSPClient {
|
export namespace LSPClient {
|
||||||
const log = Log.create({ service: "lsp.client" });
|
const log = Log.create({ service: "lsp.client" })
|
||||||
|
|
||||||
export type Info = Awaited<ReturnType<typeof create>>;
|
export type Info = Awaited<ReturnType<typeof create>>
|
||||||
|
|
||||||
export type Diagnostic = VSCodeDiagnostic;
|
export type Diagnostic = VSCodeDiagnostic
|
||||||
|
|
||||||
export const Event = {
|
export const Event = {
|
||||||
Diagnostics: Bus.event(
|
Diagnostics: Bus.event(
|
||||||
|
@ -27,36 +27,36 @@ export namespace LSPClient {
|
||||||
path: z.string(),
|
path: z.string(),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
};
|
}
|
||||||
|
|
||||||
export async function create(input: { cmd: string[]; serverID: string }) {
|
export async function create(input: { cmd: string[]; serverID: string }) {
|
||||||
log.info("starting client", input);
|
log.info("starting client", input)
|
||||||
|
|
||||||
const app = await App.use();
|
const app = await App.use()
|
||||||
const [command, ...args] = input.cmd;
|
const [command, ...args] = input.cmd
|
||||||
const server = spawn(command, args, {
|
const server = spawn(command, args, {
|
||||||
stdio: ["pipe", "pipe", "pipe"],
|
stdio: ["pipe", "pipe", "pipe"],
|
||||||
cwd: app.root,
|
cwd: app.root,
|
||||||
});
|
})
|
||||||
|
|
||||||
const connection = createMessageConnection(
|
const connection = createMessageConnection(
|
||||||
new StreamMessageReader(server.stdout),
|
new StreamMessageReader(server.stdout),
|
||||||
new StreamMessageWriter(server.stdin),
|
new StreamMessageWriter(server.stdin),
|
||||||
);
|
)
|
||||||
|
|
||||||
const diagnostics = new Map<string, Diagnostic[]>();
|
const diagnostics = new Map<string, Diagnostic[]>()
|
||||||
connection.onNotification("textDocument/publishDiagnostics", (params) => {
|
connection.onNotification("textDocument/publishDiagnostics", (params) => {
|
||||||
const path = new URL(params.uri).pathname;
|
const path = new URL(params.uri).pathname
|
||||||
log.info("textDocument/publishDiagnostics", {
|
log.info("textDocument/publishDiagnostics", {
|
||||||
path,
|
path,
|
||||||
});
|
})
|
||||||
const exists = diagnostics.has(path);
|
const exists = diagnostics.has(path)
|
||||||
diagnostics.set(path, params.diagnostics);
|
diagnostics.set(path, params.diagnostics)
|
||||||
// servers seem to send one blank publishDiagnostics event before the first real one
|
// servers seem to send one blank publishDiagnostics event before the first real one
|
||||||
if (!exists && !params.diagnostics.length) return;
|
if (!exists && !params.diagnostics.length) return
|
||||||
Bus.publish(Event.Diagnostics, { path, serverID: input.serverID });
|
Bus.publish(Event.Diagnostics, { path, serverID: input.serverID })
|
||||||
});
|
})
|
||||||
connection.listen();
|
connection.listen()
|
||||||
|
|
||||||
await connection.sendRequest("initialize", {
|
await connection.sendRequest("initialize", {
|
||||||
processId: server.pid,
|
processId: server.pid,
|
||||||
|
@ -116,29 +116,29 @@ export namespace LSPClient {
|
||||||
},
|
},
|
||||||
window: {},
|
window: {},
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
await connection.sendNotification("initialized", {});
|
await connection.sendNotification("initialized", {})
|
||||||
log.info("initialized");
|
log.info("initialized")
|
||||||
|
|
||||||
const files = new Set<string>();
|
const files = new Set<string>()
|
||||||
|
|
||||||
const result = {
|
const result = {
|
||||||
get clientID() {
|
get clientID() {
|
||||||
return input.serverID;
|
return input.serverID
|
||||||
},
|
},
|
||||||
get connection() {
|
get connection() {
|
||||||
return connection;
|
return connection
|
||||||
},
|
},
|
||||||
notify: {
|
notify: {
|
||||||
async open(input: { path: string }) {
|
async open(input: { path: string }) {
|
||||||
const file = Bun.file(input.path);
|
const file = Bun.file(input.path)
|
||||||
const text = await file.text();
|
const text = await file.text()
|
||||||
const opened = files.has(input.path);
|
const opened = files.has(input.path)
|
||||||
if (!opened) {
|
if (!opened) {
|
||||||
log.info("textDocument/didOpen", input);
|
log.info("textDocument/didOpen", input)
|
||||||
diagnostics.delete(input.path);
|
diagnostics.delete(input.path)
|
||||||
const extension = path.extname(input.path);
|
const extension = path.extname(input.path)
|
||||||
const languageId = LANGUAGE_EXTENSIONS[extension] ?? "plaintext";
|
const languageId = LANGUAGE_EXTENSIONS[extension] ?? "plaintext"
|
||||||
await connection.sendNotification("textDocument/didOpen", {
|
await connection.sendNotification("textDocument/didOpen", {
|
||||||
textDocument: {
|
textDocument: {
|
||||||
uri: `file://` + input.path,
|
uri: `file://` + input.path,
|
||||||
|
@ -146,13 +146,13 @@ export namespace LSPClient {
|
||||||
version: Date.now(),
|
version: Date.now(),
|
||||||
text,
|
text,
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
files.add(input.path);
|
files.add(input.path)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("textDocument/didChange", input);
|
log.info("textDocument/didChange", input)
|
||||||
diagnostics.delete(input.path);
|
diagnostics.delete(input.path)
|
||||||
await connection.sendNotification("textDocument/didChange", {
|
await connection.sendNotification("textDocument/didChange", {
|
||||||
textDocument: {
|
textDocument: {
|
||||||
uri: `file://` + input.path,
|
uri: `file://` + input.path,
|
||||||
|
@ -163,16 +163,16 @@ export namespace LSPClient {
|
||||||
text,
|
text,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
get diagnostics() {
|
get diagnostics() {
|
||||||
return diagnostics;
|
return diagnostics
|
||||||
},
|
},
|
||||||
async waitForDiagnostics(input: { path: string }) {
|
async waitForDiagnostics(input: { path: string }) {
|
||||||
log.info("waiting for diagnostics", input);
|
log.info("waiting for diagnostics", input)
|
||||||
let unsub: () => void;
|
let unsub: () => void
|
||||||
let timeout: NodeJS.Timeout;
|
let timeout: NodeJS.Timeout
|
||||||
return await Promise.race([
|
return await Promise.race([
|
||||||
new Promise<void>(async (resolve) => {
|
new Promise<void>(async (resolve) => {
|
||||||
unsub = Bus.subscribe(Event.Diagnostics, (event) => {
|
unsub = Bus.subscribe(Event.Diagnostics, (event) => {
|
||||||
|
@ -180,29 +180,29 @@ export namespace LSPClient {
|
||||||
event.properties.path === input.path &&
|
event.properties.path === input.path &&
|
||||||
event.properties.serverID === result.clientID
|
event.properties.serverID === result.clientID
|
||||||
) {
|
) {
|
||||||
log.info("got diagnostics", input);
|
log.info("got diagnostics", input)
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout)
|
||||||
unsub?.();
|
unsub?.()
|
||||||
resolve();
|
resolve()
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}),
|
}),
|
||||||
new Promise<void>((resolve) => {
|
new Promise<void>((resolve) => {
|
||||||
timeout = setTimeout(() => {
|
timeout = setTimeout(() => {
|
||||||
log.info("timed out refreshing diagnostics", input);
|
log.info("timed out refreshing diagnostics", input)
|
||||||
unsub?.();
|
unsub?.()
|
||||||
resolve();
|
resolve()
|
||||||
}, 5000);
|
}, 5000)
|
||||||
}),
|
}),
|
||||||
]);
|
])
|
||||||
},
|
},
|
||||||
async shutdown() {
|
async shutdown() {
|
||||||
log.info("shutting down");
|
log.info("shutting down")
|
||||||
connection.end();
|
connection.end()
|
||||||
connection.dispose();
|
connection.dispose()
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
|
|
||||||
return result;
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,64 +1,64 @@
|
||||||
import { App } from "../app/app";
|
import { App } from "../app/app"
|
||||||
import { Log } from "../util/log";
|
import { Log } from "../util/log"
|
||||||
import { LSPClient } from "./client";
|
import { LSPClient } from "./client"
|
||||||
import path from "path";
|
import path from "path"
|
||||||
|
|
||||||
export namespace LSP {
|
export namespace LSP {
|
||||||
const log = Log.create({ service: "lsp" });
|
const log = Log.create({ service: "lsp" })
|
||||||
|
|
||||||
const state = App.state(
|
const state = App.state(
|
||||||
"lsp",
|
"lsp",
|
||||||
async () => {
|
async () => {
|
||||||
log.info("initializing");
|
log.info("initializing")
|
||||||
const clients = new Map<string, LSPClient.Info>();
|
const clients = new Map<string, LSPClient.Info>()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
clients,
|
clients,
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
async (state) => {
|
async (state) => {
|
||||||
for (const client of state.clients.values()) {
|
for (const client of state.clients.values()) {
|
||||||
await client.shutdown();
|
await client.shutdown()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
)
|
||||||
|
|
||||||
export async function file(input: string) {
|
export async function file(input: string) {
|
||||||
const extension = path.parse(input).ext;
|
const extension = path.parse(input).ext
|
||||||
const s = await state();
|
const s = await state()
|
||||||
const matches = AUTO.filter((x) => x.extensions.includes(extension));
|
const matches = AUTO.filter((x) => x.extensions.includes(extension))
|
||||||
for (const match of matches) {
|
for (const match of matches) {
|
||||||
const existing = s.clients.get(match.id);
|
const existing = s.clients.get(match.id)
|
||||||
if (existing) continue;
|
if (existing) continue
|
||||||
const client = await LSPClient.create({
|
const client = await LSPClient.create({
|
||||||
cmd: match.command,
|
cmd: match.command,
|
||||||
serverID: match.id,
|
serverID: match.id,
|
||||||
});
|
})
|
||||||
s.clients.set(match.id, client);
|
s.clients.set(match.id, client)
|
||||||
}
|
}
|
||||||
await run(async (client) => {
|
await run(async (client) => {
|
||||||
const wait = client.waitForDiagnostics({ path: input });
|
const wait = client.waitForDiagnostics({ path: input })
|
||||||
await client.notify.open({ path: input });
|
await client.notify.open({ path: input })
|
||||||
return wait;
|
return wait
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function diagnostics() {
|
export async function diagnostics() {
|
||||||
const results: Record<string, LSPClient.Diagnostic[]> = {};
|
const results: Record<string, LSPClient.Diagnostic[]> = {}
|
||||||
for (const result of await run(async (client) => client.diagnostics)) {
|
for (const result of await run(async (client) => client.diagnostics)) {
|
||||||
for (const [path, diagnostics] of result.entries()) {
|
for (const [path, diagnostics] of result.entries()) {
|
||||||
const arr = results[path] || [];
|
const arr = results[path] || []
|
||||||
arr.push(...diagnostics);
|
arr.push(...diagnostics)
|
||||||
results[path] = arr;
|
results[path] = arr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return results;
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function hover(input: {
|
export async function hover(input: {
|
||||||
file: string;
|
file: string
|
||||||
line: number;
|
line: number
|
||||||
character: number;
|
character: number
|
||||||
}) {
|
}) {
|
||||||
return run((client) => {
|
return run((client) => {
|
||||||
return client.connection.sendRequest("textDocument/hover", {
|
return client.connection.sendRequest("textDocument/hover", {
|
||||||
|
@ -69,23 +69,23 @@ export namespace LSP {
|
||||||
line: input.line,
|
line: input.line,
|
||||||
character: input.character,
|
character: input.character,
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function run<T>(
|
async function run<T>(
|
||||||
input: (client: LSPClient.Info) => Promise<T>,
|
input: (client: LSPClient.Info) => Promise<T>,
|
||||||
): Promise<T[]> {
|
): Promise<T[]> {
|
||||||
const clients = await state().then((x) => [...x.clients.values()]);
|
const clients = await state().then((x) => [...x.clients.values()])
|
||||||
const tasks = clients.map((x) => input(x));
|
const tasks = clients.map((x) => input(x))
|
||||||
return Promise.all(tasks);
|
return Promise.all(tasks)
|
||||||
}
|
}
|
||||||
|
|
||||||
const AUTO: {
|
const AUTO: {
|
||||||
id: string;
|
id: string
|
||||||
command: string[];
|
command: string[]
|
||||||
extensions: string[];
|
extensions: string[]
|
||||||
install?: () => Promise<void>;
|
install?: () => Promise<void>
|
||||||
}[] = [
|
}[] = [
|
||||||
{
|
{
|
||||||
id: "typescript",
|
id: "typescript",
|
||||||
|
@ -110,7 +110,7 @@ export namespace LSP {
|
||||||
extensions: [".go"],
|
extensions: [".go"],
|
||||||
},
|
},
|
||||||
*/
|
*/
|
||||||
];
|
]
|
||||||
|
|
||||||
export namespace Diagnostic {
|
export namespace Diagnostic {
|
||||||
export function pretty(diagnostic: LSPClient.Diagnostic) {
|
export function pretty(diagnostic: LSPClient.Diagnostic) {
|
||||||
|
@ -119,13 +119,13 @@ export namespace LSP {
|
||||||
2: "WARN",
|
2: "WARN",
|
||||||
3: "INFO",
|
3: "INFO",
|
||||||
4: "HINT",
|
4: "HINT",
|
||||||
};
|
}
|
||||||
|
|
||||||
const severity = severityMap[diagnostic.severity || 1];
|
const severity = severityMap[diagnostic.severity || 1]
|
||||||
const line = diagnostic.range.start.line + 1;
|
const line = diagnostic.range.start.line + 1
|
||||||
const col = diagnostic.range.start.character + 1;
|
const col = diagnostic.range.start.character + 1
|
||||||
|
|
||||||
return `${severity} [${line}:${col}] ${diagnostic.message}`;
|
return `${severity} [${line}:${col}] ${diagnostic.message}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,4 +86,4 @@ export const LANGUAGE_EXTENSIONS: Record<string, string> = {
|
||||||
".yml": "yaml",
|
".yml": "yaml",
|
||||||
".mjs": "javascript",
|
".mjs": "javascript",
|
||||||
".cjs": "javascript",
|
".cjs": "javascript",
|
||||||
} as const;
|
} as const
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import z from "zod";
|
import z from "zod"
|
||||||
|
|
||||||
export namespace Provider {
|
export namespace Provider {
|
||||||
export const Model = z
|
export const Model = z
|
||||||
|
@ -18,8 +18,8 @@ export namespace Provider {
|
||||||
})
|
})
|
||||||
.openapi({
|
.openapi({
|
||||||
ref: "Provider.Model",
|
ref: "Provider.Model",
|
||||||
});
|
})
|
||||||
export type Model = z.output<typeof Model>;
|
export type Model = z.output<typeof Model>
|
||||||
|
|
||||||
export const Info = z
|
export const Info = z
|
||||||
.object({
|
.object({
|
||||||
|
@ -30,6 +30,6 @@ export namespace Provider {
|
||||||
})
|
})
|
||||||
.openapi({
|
.openapi({
|
||||||
ref: "Provider.Info",
|
ref: "Provider.Info",
|
||||||
});
|
})
|
||||||
export type Info = z.output<typeof Info>;
|
export type Info = z.output<typeof Info>
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
import { Log } from "../util/log";
|
import { Log } from "../util/log"
|
||||||
import { Bus } from "../bus";
|
import { Bus } from "../bus"
|
||||||
import { describeRoute, generateSpecs, openAPISpecs } from "hono-openapi";
|
import { describeRoute, generateSpecs, openAPISpecs } from "hono-openapi"
|
||||||
import { Hono } from "hono";
|
import { Hono } from "hono"
|
||||||
import { streamSSE } from "hono/streaming";
|
import { streamSSE } from "hono/streaming"
|
||||||
import { Session } from "../session/session";
|
import { Session } from "../session/session"
|
||||||
import { resolver, validator as zValidator } from "hono-openapi/zod";
|
import { resolver, validator as zValidator } from "hono-openapi/zod"
|
||||||
import { z } from "zod";
|
import { z } from "zod"
|
||||||
import { LLM } from "../llm/llm";
|
import { LLM } from "../llm/llm"
|
||||||
import { Message } from "../session/message";
|
import { Message } from "../session/message"
|
||||||
import { Provider } from "../provider/provider";
|
import { Provider } from "../provider/provider"
|
||||||
|
|
||||||
export namespace Server {
|
export namespace Server {
|
||||||
const log = Log.create({ service: "server" });
|
const log = Log.create({ service: "server" })
|
||||||
const PORT = 16713;
|
const PORT = 16713
|
||||||
|
|
||||||
export type App = ReturnType<typeof app>;
|
export type App = ReturnType<typeof app>
|
||||||
|
|
||||||
function app() {
|
function app() {
|
||||||
const app = new Hono();
|
const app = new Hono()
|
||||||
|
|
||||||
const result = app
|
const result = app
|
||||||
.get(
|
.get(
|
||||||
|
@ -53,24 +53,24 @@ export namespace Server {
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
log.info("event connected");
|
log.info("event connected")
|
||||||
return streamSSE(c, async (stream) => {
|
return streamSSE(c, async (stream) => {
|
||||||
stream.writeSSE({
|
stream.writeSSE({
|
||||||
data: JSON.stringify({}),
|
data: JSON.stringify({}),
|
||||||
});
|
})
|
||||||
const unsub = Bus.subscribeAll(async (event) => {
|
const unsub = Bus.subscribeAll(async (event) => {
|
||||||
await stream.writeSSE({
|
await stream.writeSSE({
|
||||||
data: JSON.stringify(event),
|
data: JSON.stringify(event),
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
await new Promise<void>((resolve) => {
|
await new Promise<void>((resolve) => {
|
||||||
stream.onAbort(() => {
|
stream.onAbort(() => {
|
||||||
unsub();
|
unsub()
|
||||||
resolve();
|
resolve()
|
||||||
log.info("event disconnected");
|
log.info("event disconnected")
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.post(
|
.post(
|
||||||
|
@ -89,8 +89,8 @@ export namespace Server {
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
const session = await Session.create();
|
const session = await Session.create()
|
||||||
return c.json(session);
|
return c.json(session)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.post(
|
.post(
|
||||||
|
@ -115,10 +115,10 @@ export namespace Server {
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
const body = c.req.valid("json");
|
const body = c.req.valid("json")
|
||||||
await Session.share(body.sessionID);
|
await Session.share(body.sessionID)
|
||||||
const session = await Session.get(body.sessionID);
|
const session = await Session.get(body.sessionID)
|
||||||
return c.json(session);
|
return c.json(session)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.post(
|
.post(
|
||||||
|
@ -143,10 +143,8 @@ export namespace Server {
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
const messages = await Session.messages(
|
const messages = await Session.messages(c.req.valid("json").sessionID)
|
||||||
c.req.valid("json").sessionID,
|
return c.json(messages)
|
||||||
);
|
|
||||||
return c.json(messages);
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.post(
|
.post(
|
||||||
|
@ -165,8 +163,8 @@ export namespace Server {
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
const sessions = await Array.fromAsync(Session.list());
|
const sessions = await Array.fromAsync(Session.list())
|
||||||
return c.json(sessions);
|
return c.json(sessions)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.post(
|
.post(
|
||||||
|
@ -191,8 +189,8 @@ export namespace Server {
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
const body = c.req.valid("json");
|
const body = c.req.valid("json")
|
||||||
return c.json(Session.abort(body.sessionID));
|
return c.json(Session.abort(body.sessionID))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.post(
|
.post(
|
||||||
|
@ -219,9 +217,9 @@ export namespace Server {
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
const body = c.req.valid("json");
|
const body = c.req.valid("json")
|
||||||
await Session.summarize(body);
|
await Session.summarize(body)
|
||||||
return c.json(true);
|
return c.json(true)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.post(
|
.post(
|
||||||
|
@ -249,9 +247,9 @@ export namespace Server {
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
const body = c.req.valid("json");
|
const body = c.req.valid("json")
|
||||||
const msg = await Session.chat(body);
|
const msg = await Session.chat(body)
|
||||||
return c.json(msg);
|
return c.json(msg)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.post(
|
.post(
|
||||||
|
@ -270,20 +268,20 @@ export namespace Server {
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
const providers = await LLM.providers();
|
const providers = await LLM.providers()
|
||||||
const result = [] as (Provider.Info & { key: string })[];
|
const result = [] as (Provider.Info & { key: string })[]
|
||||||
for (const [key, provider] of Object.entries(providers)) {
|
for (const [key, provider] of Object.entries(providers)) {
|
||||||
result.push({ ...provider.info, key });
|
result.push({ ...provider.info, key })
|
||||||
}
|
}
|
||||||
return c.json(result);
|
return c.json(result)
|
||||||
},
|
},
|
||||||
);
|
)
|
||||||
|
|
||||||
return result;
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function openapi() {
|
export async function openapi() {
|
||||||
const a = app();
|
const a = app()
|
||||||
const result = await generateSpecs(a, {
|
const result = await generateSpecs(a, {
|
||||||
documentation: {
|
documentation: {
|
||||||
info: {
|
info: {
|
||||||
|
@ -293,8 +291,8 @@ export namespace Server {
|
||||||
},
|
},
|
||||||
openapi: "3.0.0",
|
openapi: "3.0.0",
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
return result;
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
export function listen() {
|
export function listen() {
|
||||||
|
@ -303,7 +301,7 @@ export namespace Server {
|
||||||
hostname: "0.0.0.0",
|
hostname: "0.0.0.0",
|
||||||
idleTimeout: 0,
|
idleTimeout: 0,
|
||||||
fetch: app().fetch,
|
fetch: app().fetch,
|
||||||
});
|
})
|
||||||
return server;
|
return server
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import z from "zod";
|
import z from "zod"
|
||||||
import { Bus } from "../bus";
|
import { Bus } from "../bus"
|
||||||
|
|
||||||
export namespace Message {
|
export namespace Message {
|
||||||
export const ToolCall = z
|
export const ToolCall = z
|
||||||
|
@ -12,8 +12,8 @@ export namespace Message {
|
||||||
})
|
})
|
||||||
.openapi({
|
.openapi({
|
||||||
ref: "Message.ToolInvocation.ToolCall",
|
ref: "Message.ToolInvocation.ToolCall",
|
||||||
});
|
})
|
||||||
export type ToolCall = z.infer<typeof ToolCall>;
|
export type ToolCall = z.infer<typeof ToolCall>
|
||||||
|
|
||||||
export const ToolPartialCall = z
|
export const ToolPartialCall = z
|
||||||
.object({
|
.object({
|
||||||
|
@ -25,8 +25,8 @@ export namespace Message {
|
||||||
})
|
})
|
||||||
.openapi({
|
.openapi({
|
||||||
ref: "Message.ToolInvocation.ToolPartialCall",
|
ref: "Message.ToolInvocation.ToolPartialCall",
|
||||||
});
|
})
|
||||||
export type ToolPartialCall = z.infer<typeof ToolPartialCall>;
|
export type ToolPartialCall = z.infer<typeof ToolPartialCall>
|
||||||
|
|
||||||
export const ToolResult = z
|
export const ToolResult = z
|
||||||
.object({
|
.object({
|
||||||
|
@ -39,15 +39,15 @@ export namespace Message {
|
||||||
})
|
})
|
||||||
.openapi({
|
.openapi({
|
||||||
ref: "Message.ToolInvocation.ToolResult",
|
ref: "Message.ToolInvocation.ToolResult",
|
||||||
});
|
})
|
||||||
export type ToolResult = z.infer<typeof ToolResult>;
|
export type ToolResult = z.infer<typeof ToolResult>
|
||||||
|
|
||||||
export const ToolInvocation = z
|
export const ToolInvocation = z
|
||||||
.discriminatedUnion("state", [ToolCall, ToolPartialCall, ToolResult])
|
.discriminatedUnion("state", [ToolCall, ToolPartialCall, ToolResult])
|
||||||
.openapi({
|
.openapi({
|
||||||
ref: "Message.ToolInvocation",
|
ref: "Message.ToolInvocation",
|
||||||
});
|
})
|
||||||
export type ToolInvocation = z.infer<typeof ToolInvocation>;
|
export type ToolInvocation = z.infer<typeof ToolInvocation>
|
||||||
|
|
||||||
export const TextPart = z
|
export const TextPart = z
|
||||||
.object({
|
.object({
|
||||||
|
@ -56,8 +56,8 @@ export namespace Message {
|
||||||
})
|
})
|
||||||
.openapi({
|
.openapi({
|
||||||
ref: "Message.Part.Text",
|
ref: "Message.Part.Text",
|
||||||
});
|
})
|
||||||
export type TextPart = z.infer<typeof TextPart>;
|
export type TextPart = z.infer<typeof TextPart>
|
||||||
|
|
||||||
export const ReasoningPart = z
|
export const ReasoningPart = z
|
||||||
.object({
|
.object({
|
||||||
|
@ -67,8 +67,8 @@ export namespace Message {
|
||||||
})
|
})
|
||||||
.openapi({
|
.openapi({
|
||||||
ref: "Message.Part.Reasoning",
|
ref: "Message.Part.Reasoning",
|
||||||
});
|
})
|
||||||
export type ReasoningPart = z.infer<typeof ReasoningPart>;
|
export type ReasoningPart = z.infer<typeof ReasoningPart>
|
||||||
|
|
||||||
export const ToolInvocationPart = z
|
export const ToolInvocationPart = z
|
||||||
.object({
|
.object({
|
||||||
|
@ -77,8 +77,8 @@ export namespace Message {
|
||||||
})
|
})
|
||||||
.openapi({
|
.openapi({
|
||||||
ref: "Message.Part.ToolInvocation",
|
ref: "Message.Part.ToolInvocation",
|
||||||
});
|
})
|
||||||
export type ToolInvocationPart = z.infer<typeof ToolInvocationPart>;
|
export type ToolInvocationPart = z.infer<typeof ToolInvocationPart>
|
||||||
|
|
||||||
export const SourceUrlPart = z
|
export const SourceUrlPart = z
|
||||||
.object({
|
.object({
|
||||||
|
@ -90,8 +90,8 @@ export namespace Message {
|
||||||
})
|
})
|
||||||
.openapi({
|
.openapi({
|
||||||
ref: "Message.Part.SourceUrl",
|
ref: "Message.Part.SourceUrl",
|
||||||
});
|
})
|
||||||
export type SourceUrlPart = z.infer<typeof SourceUrlPart>;
|
export type SourceUrlPart = z.infer<typeof SourceUrlPart>
|
||||||
|
|
||||||
export const FilePart = z
|
export const FilePart = z
|
||||||
.object({
|
.object({
|
||||||
|
@ -102,8 +102,8 @@ export namespace Message {
|
||||||
})
|
})
|
||||||
.openapi({
|
.openapi({
|
||||||
ref: "Message.Part.File",
|
ref: "Message.Part.File",
|
||||||
});
|
})
|
||||||
export type FilePart = z.infer<typeof FilePart>;
|
export type FilePart = z.infer<typeof FilePart>
|
||||||
|
|
||||||
export const StepStartPart = z
|
export const StepStartPart = z
|
||||||
.object({
|
.object({
|
||||||
|
@ -111,8 +111,8 @@ export namespace Message {
|
||||||
})
|
})
|
||||||
.openapi({
|
.openapi({
|
||||||
ref: "Message.Part.StepStart",
|
ref: "Message.Part.StepStart",
|
||||||
});
|
})
|
||||||
export type StepStartPart = z.infer<typeof StepStartPart>;
|
export type StepStartPart = z.infer<typeof StepStartPart>
|
||||||
|
|
||||||
export const Part = z
|
export const Part = z
|
||||||
.discriminatedUnion("type", [
|
.discriminatedUnion("type", [
|
||||||
|
@ -125,8 +125,8 @@ export namespace Message {
|
||||||
])
|
])
|
||||||
.openapi({
|
.openapi({
|
||||||
ref: "Message.Part",
|
ref: "Message.Part",
|
||||||
});
|
})
|
||||||
export type Part = z.infer<typeof Part>;
|
export type Part = z.infer<typeof Part>
|
||||||
|
|
||||||
export const Info = z
|
export const Info = z
|
||||||
.object({
|
.object({
|
||||||
|
@ -157,8 +157,8 @@ export namespace Message {
|
||||||
})
|
})
|
||||||
.openapi({
|
.openapi({
|
||||||
ref: "Message.Info",
|
ref: "Message.Info",
|
||||||
});
|
})
|
||||||
export type Info = z.infer<typeof Info>;
|
export type Info = z.infer<typeof Info>
|
||||||
|
|
||||||
export const Event = {
|
export const Event = {
|
||||||
Updated: Bus.event(
|
Updated: Bus.event(
|
||||||
|
@ -167,5 +167,5 @@ export namespace Message {
|
||||||
info: Info,
|
info: Info,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,55 +1,55 @@
|
||||||
import { FileStorage } from "@flystorage/file-storage";
|
import { FileStorage } from "@flystorage/file-storage"
|
||||||
import { LocalStorageAdapter } from "@flystorage/local-fs";
|
import { LocalStorageAdapter } from "@flystorage/local-fs"
|
||||||
import fs from "fs/promises";
|
import fs from "fs/promises"
|
||||||
import { Log } from "../util/log";
|
import { Log } from "../util/log"
|
||||||
import { App } from "../app/app";
|
import { App } from "../app/app"
|
||||||
import { AppPath } from "../app/path";
|
import { AppPath } from "../app/path"
|
||||||
import { Bus } from "../bus";
|
import { Bus } from "../bus"
|
||||||
import z from "zod";
|
import z from "zod"
|
||||||
|
|
||||||
export namespace Storage {
|
export namespace Storage {
|
||||||
const log = Log.create({ service: "storage" });
|
const log = Log.create({ service: "storage" })
|
||||||
|
|
||||||
export const Event = {
|
export const Event = {
|
||||||
Write: Bus.event(
|
Write: Bus.event(
|
||||||
"storage.write",
|
"storage.write",
|
||||||
z.object({ key: z.string(), content: z.any() }),
|
z.object({ key: z.string(), content: z.any() }),
|
||||||
),
|
),
|
||||||
};
|
}
|
||||||
|
|
||||||
const state = App.state("storage", async () => {
|
const state = App.state("storage", async () => {
|
||||||
const app = await App.use();
|
const app = await App.use()
|
||||||
const storageDir = AppPath.storage(app.root);
|
const storageDir = AppPath.storage(app.root)
|
||||||
await fs.mkdir(storageDir, { recursive: true });
|
await fs.mkdir(storageDir, { recursive: true })
|
||||||
const storage = new FileStorage(new LocalStorageAdapter(storageDir));
|
const storage = new FileStorage(new LocalStorageAdapter(storageDir))
|
||||||
log.info("created", { path: storageDir });
|
log.info("created", { path: storageDir })
|
||||||
return {
|
return {
|
||||||
storage,
|
storage,
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
export async function readJSON<T>(key: string) {
|
export async function readJSON<T>(key: string) {
|
||||||
const storage = await state().then((x) => x.storage);
|
const storage = await state().then((x) => x.storage)
|
||||||
const data = await storage.readToString(key + ".json");
|
const data = await storage.readToString(key + ".json")
|
||||||
return JSON.parse(data) as T;
|
return JSON.parse(data) as T
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function writeJSON<T>(key: string, content: T) {
|
export async function writeJSON<T>(key: string, content: T) {
|
||||||
const storage = await state().then((x) => x.storage);
|
const storage = await state().then((x) => x.storage)
|
||||||
const json = JSON.stringify(content);
|
const json = JSON.stringify(content)
|
||||||
await storage.write(key + ".json", json);
|
await storage.write(key + ".json", json)
|
||||||
Bus.publish(Event.Write, { key, content });
|
Bus.publish(Event.Write, { key, content })
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function* list(prefix: string) {
|
export async function* list(prefix: string) {
|
||||||
try {
|
try {
|
||||||
const storage = await state().then((x) => x.storage);
|
const storage = await state().then((x) => x.storage)
|
||||||
const list = storage.list(prefix);
|
const list = storage.list(prefix)
|
||||||
for await (const item of list) {
|
for await (const item of list) {
|
||||||
yield item.path.slice(0, -5);
|
yield item.path.slice(0, -5)
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { z } from "zod";
|
import { z } from "zod"
|
||||||
import { Tool } from "./tool";
|
import { Tool } from "./tool"
|
||||||
|
|
||||||
const MAX_OUTPUT_LENGTH = 30000;
|
const MAX_OUTPUT_LENGTH = 30000
|
||||||
const BANNED_COMMANDS = [
|
const BANNED_COMMANDS = [
|
||||||
"alias",
|
"alias",
|
||||||
"curl",
|
"curl",
|
||||||
|
@ -20,9 +20,9 @@ const BANNED_COMMANDS = [
|
||||||
"chrome",
|
"chrome",
|
||||||
"firefox",
|
"firefox",
|
||||||
"safari",
|
"safari",
|
||||||
];
|
]
|
||||||
const DEFAULT_TIMEOUT = 1 * 60 * 1000;
|
const DEFAULT_TIMEOUT = 1 * 60 * 1000
|
||||||
const MAX_TIMEOUT = 10 * 60 * 1000;
|
const MAX_TIMEOUT = 10 * 60 * 1000
|
||||||
|
|
||||||
const DESCRIPTION = `Executes a given bash command in a persistent shell session with optional timeout, ensuring proper handling and security measures.
|
const DESCRIPTION = `Executes a given bash command in a persistent shell session with optional timeout, ensuring proper handling and security measures.
|
||||||
|
|
||||||
|
@ -168,7 +168,7 @@ EOF
|
||||||
|
|
||||||
Important:
|
Important:
|
||||||
- Return an empty response - the user will see the gh output directly
|
- Return an empty response - the user will see the gh output directly
|
||||||
- Never update git config`;
|
- Never update git config`
|
||||||
|
|
||||||
export const bash = Tool.define({
|
export const bash = Tool.define({
|
||||||
name: "opencode.bash",
|
name: "opencode.bash",
|
||||||
|
@ -183,17 +183,17 @@ export const bash = Tool.define({
|
||||||
.optional(),
|
.optional(),
|
||||||
}),
|
}),
|
||||||
async execute(params) {
|
async execute(params) {
|
||||||
const timeout = Math.min(params.timeout ?? DEFAULT_TIMEOUT, MAX_TIMEOUT);
|
const timeout = Math.min(params.timeout ?? DEFAULT_TIMEOUT, MAX_TIMEOUT)
|
||||||
if (BANNED_COMMANDS.some((item) => params.command.startsWith(item)))
|
if (BANNED_COMMANDS.some((item) => params.command.startsWith(item)))
|
||||||
throw new Error(`Command '${params.command}' is not allowed`);
|
throw new Error(`Command '${params.command}' is not allowed`)
|
||||||
|
|
||||||
const process = Bun.spawnSync({
|
const process = Bun.spawnSync({
|
||||||
cmd: ["bash", "-c", params.command],
|
cmd: ["bash", "-c", params.command],
|
||||||
maxBuffer: MAX_OUTPUT_LENGTH,
|
maxBuffer: MAX_OUTPUT_LENGTH,
|
||||||
timeout: timeout,
|
timeout: timeout,
|
||||||
});
|
})
|
||||||
return {
|
return {
|
||||||
output: process.stdout.toString("utf-8"),
|
output: process.stdout.toString("utf-8"),
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { z } from "zod";
|
import { z } from "zod"
|
||||||
import * as path from "path";
|
import * as path from "path"
|
||||||
import { Tool } from "./tool";
|
import { Tool } from "./tool"
|
||||||
import { FileTimes } from "./util/file-times";
|
import { FileTimes } from "./util/file-times"
|
||||||
import { LSP } from "../lsp";
|
import { LSP } from "../lsp"
|
||||||
|
|
||||||
const DESCRIPTION = `Edits files by replacing text, creating new files, or deleting content. For moving or renaming files, use the Bash tool with the 'mv' command instead. For larger file edits, use the FileWrite tool to overwrite files.
|
const DESCRIPTION = `Edits files by replacing text, creating new files, or deleting content. For moving or renaming files, use the Bash tool with the 'mv' command instead. For larger file edits, use the FileWrite tool to overwrite files.
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ When making edits:
|
||||||
- Do not leave the code in a broken state
|
- Do not leave the code in a broken state
|
||||||
- Always use relative file paths
|
- Always use relative file paths
|
||||||
|
|
||||||
Remember: when making multiple file edits in a row to the same file, you should prefer to send all edits in a single message with multiple calls to this tool, rather than multiple messages with a single call each.`;
|
Remember: when making multiple file edits in a row to the same file, you should prefer to send all edits in a single message with multiple calls to this tool, rather than multiple messages with a single call each.`
|
||||||
|
|
||||||
export const edit = Tool.define({
|
export const edit = Tool.define({
|
||||||
name: "opencode.edit",
|
name: "opencode.edit",
|
||||||
|
@ -62,68 +62,68 @@ export const edit = Tool.define({
|
||||||
}),
|
}),
|
||||||
async execute(params) {
|
async execute(params) {
|
||||||
if (!params.filePath) {
|
if (!params.filePath) {
|
||||||
throw new Error("filePath is required");
|
throw new Error("filePath is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
let filePath = params.filePath;
|
let filePath = params.filePath
|
||||||
if (!path.isAbsolute(filePath)) {
|
if (!path.isAbsolute(filePath)) {
|
||||||
filePath = path.join(process.cwd(), filePath);
|
filePath = path.join(process.cwd(), filePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
await (async () => {
|
await (async () => {
|
||||||
if (params.oldString === "") {
|
if (params.oldString === "") {
|
||||||
await Bun.write(filePath, params.newString);
|
await Bun.write(filePath, params.newString)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const read = FileTimes.get(filePath);
|
const read = FileTimes.get(filePath)
|
||||||
if (!read)
|
if (!read)
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`You must read the file ${filePath} before editing it. Use the View tool first`,
|
`You must read the file ${filePath} before editing it. Use the View tool first`,
|
||||||
);
|
)
|
||||||
const file = Bun.file(filePath);
|
const file = Bun.file(filePath)
|
||||||
if (!(await file.exists())) throw new Error(`File ${filePath} not found`);
|
if (!(await file.exists())) throw new Error(`File ${filePath} not found`)
|
||||||
const stats = await file.stat();
|
const stats = await file.stat()
|
||||||
if (stats.isDirectory())
|
if (stats.isDirectory())
|
||||||
throw new Error(`Path is a directory, not a file: ${filePath}`);
|
throw new Error(`Path is a directory, not a file: ${filePath}`)
|
||||||
if (stats.mtime.getTime() > read.getTime())
|
if (stats.mtime.getTime() > read.getTime())
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`File ${filePath} has been modified since it was last read.\nLast modification: ${read.toISOString()}\nLast read: ${stats.mtime.toISOString()}\n\nPlease read the file again before modifying it.`,
|
`File ${filePath} has been modified since it was last read.\nLast modification: ${read.toISOString()}\nLast read: ${stats.mtime.toISOString()}\n\nPlease read the file again before modifying it.`,
|
||||||
);
|
)
|
||||||
|
|
||||||
const content = await file.text();
|
const content = await file.text()
|
||||||
const index = content.indexOf(params.oldString);
|
const index = content.indexOf(params.oldString)
|
||||||
if (index === -1)
|
if (index === -1)
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`oldString not found in file. Make sure it matches exactly, including whitespace and line breaks`,
|
`oldString not found in file. Make sure it matches exactly, including whitespace and line breaks`,
|
||||||
);
|
)
|
||||||
const lastIndex = content.lastIndexOf(params.oldString);
|
const lastIndex = content.lastIndexOf(params.oldString)
|
||||||
if (index !== lastIndex)
|
if (index !== lastIndex)
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`oldString appears multiple times in the file. Please provide more context to ensure a unique match`,
|
`oldString appears multiple times in the file. Please provide more context to ensure a unique match`,
|
||||||
);
|
)
|
||||||
|
|
||||||
const newContent =
|
const newContent =
|
||||||
content.substring(0, index) +
|
content.substring(0, index) +
|
||||||
params.newString +
|
params.newString +
|
||||||
content.substring(index + params.oldString.length);
|
content.substring(index + params.oldString.length)
|
||||||
|
|
||||||
await file.write(newContent);
|
await file.write(newContent)
|
||||||
})();
|
})()
|
||||||
|
|
||||||
FileTimes.write(filePath);
|
FileTimes.write(filePath)
|
||||||
FileTimes.read(filePath);
|
FileTimes.read(filePath)
|
||||||
|
|
||||||
let output = "";
|
let output = ""
|
||||||
await LSP.file(filePath);
|
await LSP.file(filePath)
|
||||||
const diagnostics = await LSP.diagnostics();
|
const diagnostics = await LSP.diagnostics()
|
||||||
for (const [file, issues] of Object.entries(diagnostics)) {
|
for (const [file, issues] of Object.entries(diagnostics)) {
|
||||||
if (issues.length === 0) continue;
|
if (issues.length === 0) continue
|
||||||
if (file === filePath) {
|
if (file === filePath) {
|
||||||
output += `\nThis file has errors, please fix\n<file_diagnostics>\n${issues.map(LSP.Diagnostic.pretty).join("\n")}\n</file_diagnostics>\n`;
|
output += `\nThis file has errors, please fix\n<file_diagnostics>\n${issues.map(LSP.Diagnostic.pretty).join("\n")}\n</file_diagnostics>\n`
|
||||||
continue;
|
continue
|
||||||
}
|
}
|
||||||
output += `\n<project_diagnostics>\n${file}\n${issues.map(LSP.Diagnostic.pretty).join("\n")}\n</project_diagnostics>\n`;
|
output += `\n<project_diagnostics>\n${file}\n${issues.map(LSP.Diagnostic.pretty).join("\n")}\n</project_diagnostics>\n`
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -131,6 +131,6 @@ export const edit = Tool.define({
|
||||||
diagnostics,
|
diagnostics,
|
||||||
},
|
},
|
||||||
output,
|
output,
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
import { z } from "zod";
|
import { z } from "zod"
|
||||||
import { Tool } from "./tool";
|
import { Tool } from "./tool"
|
||||||
import { JSDOM } from "jsdom";
|
import TurndownService from "turndown"
|
||||||
import TurndownService from "turndown";
|
|
||||||
|
|
||||||
const MAX_RESPONSE_SIZE = 5 * 1024 * 1024; // 5MB
|
const MAX_RESPONSE_SIZE = 5 * 1024 * 1024 // 5MB
|
||||||
const DEFAULT_TIMEOUT = 30 * 1000; // 30 seconds
|
const DEFAULT_TIMEOUT = 30 * 1000 // 30 seconds
|
||||||
const MAX_TIMEOUT = 120 * 1000; // 2 minutes
|
const MAX_TIMEOUT = 120 * 1000 // 2 minutes
|
||||||
|
|
||||||
const DESCRIPTION = `Fetches content from a URL and returns it in the specified format.
|
const DESCRIPTION = `Fetches content from a URL and returns it in the specified format.
|
||||||
|
|
||||||
|
@ -35,7 +34,7 @@ TIPS:
|
||||||
- Use text format for plain text content or simple API responses
|
- Use text format for plain text content or simple API responses
|
||||||
- Use markdown format for content that should be rendered with formatting
|
- Use markdown format for content that should be rendered with formatting
|
||||||
- Use html format when you need the raw HTML structure
|
- Use html format when you need the raw HTML structure
|
||||||
- Set appropriate timeouts for potentially slow websites`;
|
- Set appropriate timeouts for potentially slow websites`
|
||||||
|
|
||||||
export const Fetch = Tool.define({
|
export const Fetch = Tool.define({
|
||||||
name: "opencode.fetch",
|
name: "opencode.fetch",
|
||||||
|
@ -60,18 +59,18 @@ export const Fetch = Tool.define({
|
||||||
!params.url.startsWith("http://") &&
|
!params.url.startsWith("http://") &&
|
||||||
!params.url.startsWith("https://")
|
!params.url.startsWith("https://")
|
||||||
) {
|
) {
|
||||||
throw new Error("URL must start with http:// or https://");
|
throw new Error("URL must start with http:// or https://")
|
||||||
}
|
}
|
||||||
|
|
||||||
const timeout = Math.min(
|
const timeout = Math.min(
|
||||||
(params.timeout ?? DEFAULT_TIMEOUT / 1000) * 1000,
|
(params.timeout ?? DEFAULT_TIMEOUT / 1000) * 1000,
|
||||||
MAX_TIMEOUT,
|
MAX_TIMEOUT,
|
||||||
);
|
)
|
||||||
|
|
||||||
const controller = new AbortController();
|
const controller = new AbortController()
|
||||||
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
const timeoutId = setTimeout(() => controller.abort(), timeout)
|
||||||
if (opts?.abortSignal) {
|
if (opts?.abortSignal) {
|
||||||
opts.abortSignal.addEventListener("abort", () => controller.abort());
|
opts.abortSignal.addEventListener("abort", () => controller.abort())
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch(params.url, {
|
const response = await fetch(params.url, {
|
||||||
|
@ -79,59 +78,59 @@ export const Fetch = Tool.define({
|
||||||
headers: {
|
headers: {
|
||||||
"User-Agent": "opencode/1.0",
|
"User-Agent": "opencode/1.0",
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId)
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`Request failed with status code: ${response.status}`);
|
throw new Error(`Request failed with status code: ${response.status}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check content length
|
// Check content length
|
||||||
const contentLength = response.headers.get("content-length");
|
const contentLength = response.headers.get("content-length")
|
||||||
if (contentLength && parseInt(contentLength) > MAX_RESPONSE_SIZE) {
|
if (contentLength && parseInt(contentLength) > MAX_RESPONSE_SIZE) {
|
||||||
throw new Error("Response too large (exceeds 5MB limit)");
|
throw new Error("Response too large (exceeds 5MB limit)")
|
||||||
}
|
}
|
||||||
|
|
||||||
const arrayBuffer = await response.arrayBuffer();
|
const arrayBuffer = await response.arrayBuffer()
|
||||||
if (arrayBuffer.byteLength > MAX_RESPONSE_SIZE) {
|
if (arrayBuffer.byteLength > MAX_RESPONSE_SIZE) {
|
||||||
throw new Error("Response too large (exceeds 5MB limit)");
|
throw new Error("Response too large (exceeds 5MB limit)")
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = new TextDecoder().decode(arrayBuffer);
|
const content = new TextDecoder().decode(arrayBuffer)
|
||||||
const contentType = response.headers.get("content-type") || "";
|
const contentType = response.headers.get("content-type") || ""
|
||||||
|
|
||||||
switch (params.format) {
|
switch (params.format) {
|
||||||
case "text":
|
case "text":
|
||||||
if (contentType.includes("text/html")) {
|
if (contentType.includes("text/html")) {
|
||||||
const text = extractTextFromHTML(content);
|
const text = extractTextFromHTML(content)
|
||||||
return { output: text };
|
return { output: text }
|
||||||
}
|
}
|
||||||
return { output: content };
|
return { output: content }
|
||||||
|
|
||||||
case "markdown":
|
case "markdown":
|
||||||
if (contentType.includes("text/html")) {
|
if (contentType.includes("text/html")) {
|
||||||
const markdown = convertHTMLToMarkdown(content);
|
const markdown = convertHTMLToMarkdown(content)
|
||||||
return { output: markdown };
|
return { output: markdown }
|
||||||
}
|
}
|
||||||
return { output: "```\n" + content + "\n```" };
|
return { output: "```\n" + content + "\n```" }
|
||||||
|
|
||||||
case "html":
|
case "html":
|
||||||
return { output: content };
|
return { output: content }
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return { output: content };
|
return { output: content }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
function extractTextFromHTML(html: string): string {
|
function extractTextFromHTML(html: string): string {
|
||||||
const dom = new JSDOM(html);
|
const doc = new DOMParser().parseFromString(html, "text/html")
|
||||||
const text = dom.window.document.body?.textContent || "";
|
const text = doc.body.textContent || doc.body.innerText || ""
|
||||||
return text.replace(/\s+/g, " ").trim();
|
return text.replace(/\s+/g, " ").trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertHTMLToMarkdown(html: string): string {
|
function convertHTMLToMarkdown(html: string): string {
|
||||||
const turndownService = new TurndownService();
|
const turndownService = new TurndownService()
|
||||||
return turndownService.turndown(html);
|
return turndownService.turndown(html)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { z } from "zod";
|
import { z } from "zod"
|
||||||
import { Tool } from "./tool";
|
import { Tool } from "./tool"
|
||||||
import { App } from "../app/app";
|
import { App } from "../app/app"
|
||||||
|
|
||||||
const DESCRIPTION = `Fast file pattern matching tool that finds files by name and pattern, returning matching paths sorted by modification time (newest first).
|
const DESCRIPTION = `Fast file pattern matching tool that finds files by name and pattern, returning matching paths sorted by modification time (newest first).
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ LIMITATIONS:
|
||||||
TIPS:
|
TIPS:
|
||||||
- For the most useful results, combine with the Grep tool: first find files with Glob, then search their contents with Grep
|
- For the most useful results, combine with the Grep tool: first find files with Glob, then search their contents with Grep
|
||||||
- When doing iterative exploration that may require multiple rounds of searching, consider using the Agent tool instead
|
- When doing iterative exploration that may require multiple rounds of searching, consider using the Agent tool instead
|
||||||
- Always check if results are truncated and refine your search pattern if needed`;
|
- Always check if results are truncated and refine your search pattern if needed`
|
||||||
|
|
||||||
export const glob = Tool.define({
|
export const glob = Tool.define({
|
||||||
name: "opencode.glob",
|
name: "opencode.glob",
|
||||||
|
@ -50,37 +50,37 @@ export const glob = Tool.define({
|
||||||
.optional(),
|
.optional(),
|
||||||
}),
|
}),
|
||||||
async execute(params) {
|
async execute(params) {
|
||||||
const app = await App.use();
|
const app = await App.use()
|
||||||
const search = params.path || app.root;
|
const search = params.path || app.root
|
||||||
const limit = 100;
|
const limit = 100
|
||||||
const glob = new Bun.Glob(params.pattern);
|
const glob = new Bun.Glob(params.pattern)
|
||||||
const files = [];
|
const files = []
|
||||||
let truncated = false;
|
let truncated = false
|
||||||
for await (const file of glob.scan({ cwd: search })) {
|
for await (const file of glob.scan({ cwd: search })) {
|
||||||
if (files.length >= limit) {
|
if (files.length >= limit) {
|
||||||
truncated = true;
|
truncated = true
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
const stats = await Bun.file(file)
|
const stats = await Bun.file(file)
|
||||||
.stat()
|
.stat()
|
||||||
.then((x) => x.mtime.getTime())
|
.then((x) => x.mtime.getTime())
|
||||||
.catch(() => 0);
|
.catch(() => 0)
|
||||||
files.push({
|
files.push({
|
||||||
path: file,
|
path: file,
|
||||||
mtime: stats,
|
mtime: stats,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
files.sort((a, b) => b.mtime - a.mtime);
|
files.sort((a, b) => b.mtime - a.mtime)
|
||||||
|
|
||||||
const output = [];
|
const output = []
|
||||||
if (files.length === 0) output.push("No files found");
|
if (files.length === 0) output.push("No files found")
|
||||||
if (files.length > 0) {
|
if (files.length > 0) {
|
||||||
output.push(...files.map((f) => f.path));
|
output.push(...files.map((f) => f.path))
|
||||||
if (truncated) {
|
if (truncated) {
|
||||||
output.push("");
|
output.push("")
|
||||||
output.push(
|
output.push(
|
||||||
"(Results are truncated. Consider using a more specific path or pattern.)",
|
"(Results are truncated. Consider using a more specific path or pattern.)",
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +90,6 @@ export const glob = Tool.define({
|
||||||
truncated,
|
truncated,
|
||||||
},
|
},
|
||||||
output: output.join("\n"),
|
output: output.join("\n"),
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { z } from "zod";
|
import { z } from "zod"
|
||||||
import { Tool } from "./tool";
|
import { Tool } from "./tool"
|
||||||
import { App } from "../app/app";
|
import { App } from "../app/app"
|
||||||
import { spawn } from "child_process";
|
import { spawn } from "child_process"
|
||||||
import { promises as fs } from "fs";
|
import { promises as fs } from "fs"
|
||||||
import path from "path";
|
import path from "path"
|
||||||
|
|
||||||
const DESCRIPTION = `Fast content search tool that finds files containing specific text or patterns, returning matching file paths sorted by modification time (newest first).
|
const DESCRIPTION = `Fast content search tool that finds files containing specific text or patterns, returning matching file paths sorted by modification time (newest first).
|
||||||
|
|
||||||
|
@ -40,13 +40,13 @@ TIPS:
|
||||||
- For faster, more targeted searches, first use Glob to find relevant files, then use Grep
|
- For faster, more targeted searches, first use Glob to find relevant files, then use Grep
|
||||||
- When doing iterative exploration that may require multiple rounds of searching, consider using the Agent tool instead
|
- When doing iterative exploration that may require multiple rounds of searching, consider using the Agent tool instead
|
||||||
- Always check if results are truncated and refine your search pattern if needed
|
- Always check if results are truncated and refine your search pattern if needed
|
||||||
- Use literal_text=true when searching for exact text containing special characters like dots, parentheses, etc.`;
|
- Use literal_text=true when searching for exact text containing special characters like dots, parentheses, etc.`
|
||||||
|
|
||||||
interface GrepMatch {
|
interface GrepMatch {
|
||||||
path: string;
|
path: string
|
||||||
modTime: number;
|
modTime: number
|
||||||
lineNum: number;
|
lineNum: number
|
||||||
lineText: string;
|
lineText: string
|
||||||
}
|
}
|
||||||
|
|
||||||
function escapeRegexPattern(pattern: string): string {
|
function escapeRegexPattern(pattern: string): string {
|
||||||
|
@ -65,27 +65,27 @@ function escapeRegexPattern(pattern: string): string {
|
||||||
"^",
|
"^",
|
||||||
"$",
|
"$",
|
||||||
"|",
|
"|",
|
||||||
];
|
]
|
||||||
let escaped = pattern;
|
let escaped = pattern
|
||||||
|
|
||||||
for (const char of specialChars) {
|
for (const char of specialChars) {
|
||||||
escaped = escaped.replaceAll(char, "\\" + char);
|
escaped = escaped.replaceAll(char, "\\" + char)
|
||||||
}
|
}
|
||||||
|
|
||||||
return escaped;
|
return escaped
|
||||||
}
|
}
|
||||||
|
|
||||||
function globToRegex(glob: string): string {
|
function globToRegex(glob: string): string {
|
||||||
let regexPattern = glob.replaceAll(".", "\\.");
|
let regexPattern = glob.replaceAll(".", "\\.")
|
||||||
regexPattern = regexPattern.replaceAll("*", ".*");
|
regexPattern = regexPattern.replaceAll("*", ".*")
|
||||||
regexPattern = regexPattern.replaceAll("?", ".");
|
regexPattern = regexPattern.replaceAll("?", ".")
|
||||||
|
|
||||||
// Handle {a,b,c} patterns
|
// Handle {a,b,c} patterns
|
||||||
regexPattern = regexPattern.replace(/\{([^}]+)\}/g, (_, inner) => {
|
regexPattern = regexPattern.replace(/\{([^}]+)\}/g, (_, inner) => {
|
||||||
return "(" + inner.replace(/,/g, "|") + ")";
|
return "(" + inner.replace(/,/g, "|") + ")"
|
||||||
});
|
})
|
||||||
|
|
||||||
return regexPattern;
|
return regexPattern
|
||||||
}
|
}
|
||||||
|
|
||||||
async function searchWithRipgrep(
|
async function searchWithRipgrep(
|
||||||
|
@ -94,71 +94,71 @@ async function searchWithRipgrep(
|
||||||
include?: string,
|
include?: string,
|
||||||
): Promise<GrepMatch[]> {
|
): Promise<GrepMatch[]> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const args = ["-n", pattern];
|
const args = ["-n", pattern]
|
||||||
if (include) {
|
if (include) {
|
||||||
args.push("--glob", include);
|
args.push("--glob", include)
|
||||||
}
|
}
|
||||||
args.push(searchPath);
|
args.push(searchPath)
|
||||||
|
|
||||||
const rg = spawn("rg", args);
|
const rg = spawn("rg", args)
|
||||||
let output = "";
|
let output = ""
|
||||||
let errorOutput = "";
|
let errorOutput = ""
|
||||||
|
|
||||||
rg.stdout.on("data", (data) => {
|
rg.stdout.on("data", (data) => {
|
||||||
output += data.toString();
|
output += data.toString()
|
||||||
});
|
})
|
||||||
|
|
||||||
rg.stderr.on("data", (data) => {
|
rg.stderr.on("data", (data) => {
|
||||||
errorOutput += data.toString();
|
errorOutput += data.toString()
|
||||||
});
|
})
|
||||||
|
|
||||||
rg.on("close", async (code) => {
|
rg.on("close", async (code) => {
|
||||||
if (code === 1) {
|
if (code === 1) {
|
||||||
// No matches found
|
// No matches found
|
||||||
resolve([]);
|
resolve([])
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (code !== 0) {
|
if (code !== 0) {
|
||||||
reject(new Error(`ripgrep failed: ${errorOutput}`));
|
reject(new Error(`ripgrep failed: ${errorOutput}`))
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const lines = output.trim().split("\n");
|
const lines = output.trim().split("\n")
|
||||||
const matches: GrepMatch[] = [];
|
const matches: GrepMatch[] = []
|
||||||
|
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
if (!line) continue;
|
if (!line) continue
|
||||||
|
|
||||||
// Parse ripgrep output format: file:line:content
|
// Parse ripgrep output format: file:line:content
|
||||||
const parts = line.split(":", 3);
|
const parts = line.split(":", 3)
|
||||||
if (parts.length < 3) continue;
|
if (parts.length < 3) continue
|
||||||
|
|
||||||
const filePath = parts[0];
|
const filePath = parts[0]
|
||||||
const lineNum = parseInt(parts[1], 10);
|
const lineNum = parseInt(parts[1], 10)
|
||||||
const lineText = parts[2];
|
const lineText = parts[2]
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const stats = await fs.stat(filePath);
|
const stats = await fs.stat(filePath)
|
||||||
matches.push({
|
matches.push({
|
||||||
path: filePath,
|
path: filePath,
|
||||||
modTime: stats.mtime.getTime(),
|
modTime: stats.mtime.getTime(),
|
||||||
lineNum,
|
lineNum,
|
||||||
lineText,
|
lineText,
|
||||||
});
|
})
|
||||||
} catch {
|
} catch {
|
||||||
// Skip files we can't access
|
// Skip files we can't access
|
||||||
continue;
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(matches);
|
resolve(matches)
|
||||||
});
|
})
|
||||||
|
|
||||||
rg.on("error", (err) => {
|
rg.on("error", (err) => {
|
||||||
reject(err);
|
reject(err)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function searchFilesWithRegex(
|
async function searchFilesWithRegex(
|
||||||
|
@ -166,68 +166,68 @@ async function searchFilesWithRegex(
|
||||||
rootPath: string,
|
rootPath: string,
|
||||||
include?: string,
|
include?: string,
|
||||||
): Promise<GrepMatch[]> {
|
): Promise<GrepMatch[]> {
|
||||||
const matches: GrepMatch[] = [];
|
const matches: GrepMatch[] = []
|
||||||
const regex = new RegExp(pattern);
|
const regex = new RegExp(pattern)
|
||||||
|
|
||||||
let includePattern: RegExp | undefined;
|
let includePattern: RegExp | undefined
|
||||||
if (include) {
|
if (include) {
|
||||||
const regexPattern = globToRegex(include);
|
const regexPattern = globToRegex(include)
|
||||||
includePattern = new RegExp(regexPattern);
|
includePattern = new RegExp(regexPattern)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function walkDir(dir: string) {
|
async function walkDir(dir: string) {
|
||||||
if (matches.length >= 200) return;
|
if (matches.length >= 200) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const entries = await fs.readdir(dir, { withFileTypes: true });
|
const entries = await fs.readdir(dir, { withFileTypes: true })
|
||||||
|
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
if (matches.length >= 200) break;
|
if (matches.length >= 200) break
|
||||||
|
|
||||||
const fullPath = path.join(dir, entry.name);
|
const fullPath = path.join(dir, entry.name)
|
||||||
|
|
||||||
if (entry.isDirectory()) {
|
if (entry.isDirectory()) {
|
||||||
// Skip hidden directories
|
// Skip hidden directories
|
||||||
if (entry.name.startsWith(".")) continue;
|
if (entry.name.startsWith(".")) continue
|
||||||
await walkDir(fullPath);
|
await walkDir(fullPath)
|
||||||
} else if (entry.isFile()) {
|
} else if (entry.isFile()) {
|
||||||
// Skip hidden files
|
// Skip hidden files
|
||||||
if (entry.name.startsWith(".")) continue;
|
if (entry.name.startsWith(".")) continue
|
||||||
|
|
||||||
if (includePattern && !includePattern.test(fullPath)) {
|
if (includePattern && !includePattern.test(fullPath)) {
|
||||||
continue;
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const content = await fs.readFile(fullPath, "utf-8");
|
const content = await fs.readFile(fullPath, "utf-8")
|
||||||
const lines = content.split("\n");
|
const lines = content.split("\n")
|
||||||
|
|
||||||
for (let i = 0; i < lines.length; i++) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
if (regex.test(lines[i])) {
|
if (regex.test(lines[i])) {
|
||||||
const stats = await fs.stat(fullPath);
|
const stats = await fs.stat(fullPath)
|
||||||
matches.push({
|
matches.push({
|
||||||
path: fullPath,
|
path: fullPath,
|
||||||
modTime: stats.mtime.getTime(),
|
modTime: stats.mtime.getTime(),
|
||||||
lineNum: i + 1,
|
lineNum: i + 1,
|
||||||
lineText: lines[i],
|
lineText: lines[i],
|
||||||
});
|
})
|
||||||
break; // Only first match per file
|
break // Only first match per file
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// Skip files we can't read
|
// Skip files we can't read
|
||||||
continue;
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// Skip directories we can't read
|
// Skip directories we can't read
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await walkDir(rootPath);
|
await walkDir(rootPath)
|
||||||
return matches;
|
return matches
|
||||||
}
|
}
|
||||||
|
|
||||||
async function searchFiles(
|
async function searchFiles(
|
||||||
|
@ -236,23 +236,23 @@ async function searchFiles(
|
||||||
include?: string,
|
include?: string,
|
||||||
limit: number = 100,
|
limit: number = 100,
|
||||||
): Promise<{ matches: GrepMatch[]; truncated: boolean }> {
|
): Promise<{ matches: GrepMatch[]; truncated: boolean }> {
|
||||||
let matches: GrepMatch[];
|
let matches: GrepMatch[]
|
||||||
|
|
||||||
try {
|
try {
|
||||||
matches = await searchWithRipgrep(pattern, rootPath, include);
|
matches = await searchWithRipgrep(pattern, rootPath, include)
|
||||||
} catch {
|
} catch {
|
||||||
matches = await searchFilesWithRegex(pattern, rootPath, include);
|
matches = await searchFilesWithRegex(pattern, rootPath, include)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort by modification time (newest first)
|
// Sort by modification time (newest first)
|
||||||
matches.sort((a, b) => b.modTime - a.modTime);
|
matches.sort((a, b) => b.modTime - a.modTime)
|
||||||
|
|
||||||
const truncated = matches.length > limit;
|
const truncated = matches.length > limit
|
||||||
if (truncated) {
|
if (truncated) {
|
||||||
matches = matches.slice(0, limit);
|
matches = matches.slice(0, limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
return { matches, truncated };
|
return { matches, truncated }
|
||||||
}
|
}
|
||||||
|
|
||||||
export const grep = Tool.define({
|
export const grep = Tool.define({
|
||||||
|
@ -283,54 +283,54 @@ export const grep = Tool.define({
|
||||||
}),
|
}),
|
||||||
async execute(params) {
|
async execute(params) {
|
||||||
if (!params.pattern) {
|
if (!params.pattern) {
|
||||||
throw new Error("pattern is required");
|
throw new Error("pattern is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
const app = await App.use();
|
const app = await App.use()
|
||||||
const searchPath = params.path || app.root;
|
const searchPath = params.path || app.root
|
||||||
|
|
||||||
// If literalText is true, escape the pattern
|
// If literalText is true, escape the pattern
|
||||||
const searchPattern = params.literalText
|
const searchPattern = params.literalText
|
||||||
? escapeRegexPattern(params.pattern)
|
? escapeRegexPattern(params.pattern)
|
||||||
: params.pattern;
|
: params.pattern
|
||||||
|
|
||||||
const { matches, truncated } = await searchFiles(
|
const { matches, truncated } = await searchFiles(
|
||||||
searchPattern,
|
searchPattern,
|
||||||
searchPath,
|
searchPath,
|
||||||
params.include,
|
params.include,
|
||||||
100,
|
100,
|
||||||
);
|
)
|
||||||
|
|
||||||
if (matches.length === 0) {
|
if (matches.length === 0) {
|
||||||
return {
|
return {
|
||||||
metadata: { matches: 0, truncated },
|
metadata: { matches: 0, truncated },
|
||||||
output: "No files found"
|
output: "No files found",
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const lines = [`Found ${matches.length} matches`];
|
const lines = [`Found ${matches.length} matches`]
|
||||||
|
|
||||||
let currentFile = "";
|
let currentFile = ""
|
||||||
for (const match of matches) {
|
for (const match of matches) {
|
||||||
if (currentFile !== match.path) {
|
if (currentFile !== match.path) {
|
||||||
if (currentFile !== "") {
|
if (currentFile !== "") {
|
||||||
lines.push("");
|
lines.push("")
|
||||||
}
|
}
|
||||||
currentFile = match.path;
|
currentFile = match.path
|
||||||
lines.push(`${match.path}:`);
|
lines.push(`${match.path}:`)
|
||||||
}
|
}
|
||||||
if (match.lineNum > 0) {
|
if (match.lineNum > 0) {
|
||||||
lines.push(` Line ${match.lineNum}: ${match.lineText}`);
|
lines.push(` Line ${match.lineNum}: ${match.lineText}`)
|
||||||
} else {
|
} else {
|
||||||
lines.push(` ${match.path}`);
|
lines.push(` ${match.path}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (truncated) {
|
if (truncated) {
|
||||||
lines.push("");
|
lines.push("")
|
||||||
lines.push(
|
lines.push(
|
||||||
"(Results are truncated. Consider using a more specific path or pattern.)",
|
"(Results are truncated. Consider using a more specific path or pattern.)",
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -339,7 +339,6 @@ export const grep = Tool.define({
|
||||||
truncated,
|
truncated,
|
||||||
},
|
},
|
||||||
output: lines.join("\n"),
|
output: lines.join("\n"),
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
export * from "./bash";
|
export * from "./bash"
|
||||||
export * from "./edit";
|
export * from "./edit"
|
||||||
export * from "./fetch";
|
export * from "./fetch"
|
||||||
export * from "./glob";
|
export * from "./glob"
|
||||||
export * from "./grep";
|
export * from "./grep"
|
||||||
export * from "./view";
|
export * from "./view"
|
||||||
export * from "./ls";
|
export * from "./ls"
|
||||||
export * from "./lsp-diagnostics";
|
export * from "./lsp-diagnostics"
|
||||||
export * from "./lsp-hover";
|
export * from "./lsp-hover"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { z } from "zod";
|
import { z } from "zod"
|
||||||
import { Tool } from "./tool";
|
import { Tool } from "./tool"
|
||||||
import { App } from "../app/app";
|
import { App } from "../app/app"
|
||||||
import * as path from "path";
|
import * as path from "path"
|
||||||
|
|
||||||
const IGNORE_PATTERNS = [
|
const IGNORE_PATTERNS = [
|
||||||
"node_modules/",
|
"node_modules/",
|
||||||
|
@ -15,7 +15,7 @@ const IGNORE_PATTERNS = [
|
||||||
"obj/",
|
"obj/",
|
||||||
".idea/",
|
".idea/",
|
||||||
".vscode/",
|
".vscode/",
|
||||||
];
|
]
|
||||||
|
|
||||||
export const ls = Tool.define({
|
export const ls = Tool.define({
|
||||||
name: "opencode.ls",
|
name: "opencode.ls",
|
||||||
|
@ -25,72 +25,72 @@ export const ls = Tool.define({
|
||||||
ignore: z.array(z.string()).optional(),
|
ignore: z.array(z.string()).optional(),
|
||||||
}),
|
}),
|
||||||
async execute(params) {
|
async execute(params) {
|
||||||
const app = await App.use();
|
const app = await App.use()
|
||||||
const searchPath = path.resolve(app.root, params.path || ".");
|
const searchPath = path.resolve(app.root, params.path || ".")
|
||||||
|
|
||||||
const glob = new Bun.Glob("**/*");
|
const glob = new Bun.Glob("**/*")
|
||||||
const files = [];
|
const files = []
|
||||||
|
|
||||||
for await (const file of glob.scan({ cwd: searchPath })) {
|
for await (const file of glob.scan({ cwd: searchPath })) {
|
||||||
if (file.startsWith(".") || IGNORE_PATTERNS.some((p) => file.includes(p)))
|
if (file.startsWith(".") || IGNORE_PATTERNS.some((p) => file.includes(p)))
|
||||||
continue;
|
continue
|
||||||
if (params.ignore?.some((pattern) => new Bun.Glob(pattern).match(file)))
|
if (params.ignore?.some((pattern) => new Bun.Glob(pattern).match(file)))
|
||||||
continue;
|
continue
|
||||||
files.push(file);
|
files.push(file)
|
||||||
if (files.length >= 1000) break;
|
if (files.length >= 1000) break
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build directory structure
|
// Build directory structure
|
||||||
const dirs = new Set<string>();
|
const dirs = new Set<string>()
|
||||||
const filesByDir = new Map<string, string[]>();
|
const filesByDir = new Map<string, string[]>()
|
||||||
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
const dir = path.dirname(file);
|
const dir = path.dirname(file)
|
||||||
const parts = dir === "." ? [] : dir.split("/");
|
const parts = dir === "." ? [] : dir.split("/")
|
||||||
|
|
||||||
// Add all parent directories
|
// Add all parent directories
|
||||||
for (let i = 0; i <= parts.length; i++) {
|
for (let i = 0; i <= parts.length; i++) {
|
||||||
const dirPath = i === 0 ? "." : parts.slice(0, i).join("/");
|
const dirPath = i === 0 ? "." : parts.slice(0, i).join("/")
|
||||||
dirs.add(dirPath);
|
dirs.add(dirPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add file to its directory
|
// Add file to its directory
|
||||||
if (!filesByDir.has(dir)) filesByDir.set(dir, []);
|
if (!filesByDir.has(dir)) filesByDir.set(dir, [])
|
||||||
filesByDir.get(dir)!.push(path.basename(file));
|
filesByDir.get(dir)!.push(path.basename(file))
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderDir(dirPath: string, depth: number): string {
|
function renderDir(dirPath: string, depth: number): string {
|
||||||
const indent = " ".repeat(depth);
|
const indent = " ".repeat(depth)
|
||||||
let output = "";
|
let output = ""
|
||||||
|
|
||||||
if (depth > 0) {
|
if (depth > 0) {
|
||||||
output += `${indent}${path.basename(dirPath)}/\n`;
|
output += `${indent}${path.basename(dirPath)}/\n`
|
||||||
}
|
}
|
||||||
|
|
||||||
const childIndent = " ".repeat(depth + 1);
|
const childIndent = " ".repeat(depth + 1)
|
||||||
const children = Array.from(dirs)
|
const children = Array.from(dirs)
|
||||||
.filter((d) => path.dirname(d) === dirPath && d !== dirPath)
|
.filter((d) => path.dirname(d) === dirPath && d !== dirPath)
|
||||||
.sort();
|
.sort()
|
||||||
|
|
||||||
// Render subdirectories first
|
// Render subdirectories first
|
||||||
for (const child of children) {
|
for (const child of children) {
|
||||||
output += renderDir(child, depth + 1);
|
output += renderDir(child, depth + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render files
|
// Render files
|
||||||
const files = filesByDir.get(dirPath) || [];
|
const files = filesByDir.get(dirPath) || []
|
||||||
for (const file of files.sort()) {
|
for (const file of files.sort()) {
|
||||||
output += `${childIndent}${file}\n`;
|
output += `${childIndent}${file}\n`
|
||||||
}
|
}
|
||||||
|
|
||||||
return output;
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
const output = `${searchPath}/\n` + renderDir(".", 0);
|
const output = `${searchPath}/\n` + renderDir(".", 0)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
metadata: { count: files.length, truncated: files.length >= 1000 },
|
metadata: { count: files.length, truncated: files.length >= 1000 },
|
||||||
output,
|
output,
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { z } from "zod";
|
import { z } from "zod"
|
||||||
import { Tool } from "./tool";
|
import { Tool } from "./tool"
|
||||||
import path from "path";
|
import path from "path"
|
||||||
import { LSP } from "../lsp";
|
import { LSP } from "../lsp"
|
||||||
import { App } from "../app/app";
|
import { App } from "../app/app"
|
||||||
|
|
||||||
export const LspDiagnosticTool = Tool.define({
|
export const LspDiagnosticTool = Tool.define({
|
||||||
name: "opencode.lsp_diagnostic",
|
name: "opencode.lsp_diagnostic",
|
||||||
|
@ -34,13 +34,13 @@ TIPS:
|
||||||
path: z.string().describe("The path to the file to get diagnostics."),
|
path: z.string().describe("The path to the file to get diagnostics."),
|
||||||
}),
|
}),
|
||||||
execute: async (args) => {
|
execute: async (args) => {
|
||||||
const app = await App.use();
|
const app = await App.use()
|
||||||
const normalized = path.isAbsolute(args.path)
|
const normalized = path.isAbsolute(args.path)
|
||||||
? args.path
|
? args.path
|
||||||
: path.join(app.root, args.path);
|
: path.join(app.root, args.path)
|
||||||
await LSP.file(normalized);
|
await LSP.file(normalized)
|
||||||
const diagnostics = await LSP.diagnostics();
|
const diagnostics = await LSP.diagnostics()
|
||||||
const file = diagnostics[normalized];
|
const file = diagnostics[normalized]
|
||||||
return {
|
return {
|
||||||
metadata: {
|
metadata: {
|
||||||
diagnostics,
|
diagnostics,
|
||||||
|
@ -48,6 +48,6 @@ TIPS:
|
||||||
output: file?.length
|
output: file?.length
|
||||||
? file.map(LSP.Diagnostic.pretty).join("\n")
|
? file.map(LSP.Diagnostic.pretty).join("\n")
|
||||||
: "No errors found",
|
: "No errors found",
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { z } from "zod";
|
import { z } from "zod"
|
||||||
import { Tool } from "./tool";
|
import { Tool } from "./tool"
|
||||||
import path from "path";
|
import path from "path"
|
||||||
import { LSP } from "../lsp";
|
import { LSP } from "../lsp"
|
||||||
import { App } from "../app/app";
|
import { App } from "../app/app"
|
||||||
|
|
||||||
export const LspHoverTool = Tool.define({
|
export const LspHoverTool = Tool.define({
|
||||||
name: "opencode.lsp_hover",
|
name: "opencode.lsp_hover",
|
||||||
|
@ -17,22 +17,22 @@ export const LspHoverTool = Tool.define({
|
||||||
character: z.number().describe("The character number to get diagnostics."),
|
character: z.number().describe("The character number to get diagnostics."),
|
||||||
}),
|
}),
|
||||||
execute: async (args) => {
|
execute: async (args) => {
|
||||||
console.log(args);
|
console.log(args)
|
||||||
const app = await App.use();
|
const app = await App.use()
|
||||||
const file = path.isAbsolute(args.file)
|
const file = path.isAbsolute(args.file)
|
||||||
? args.file
|
? args.file
|
||||||
: path.join(app.root, args.file);
|
: path.join(app.root, args.file)
|
||||||
await LSP.file(file);
|
await LSP.file(file)
|
||||||
const result = await LSP.hover({
|
const result = await LSP.hover({
|
||||||
...args,
|
...args,
|
||||||
file,
|
file,
|
||||||
});
|
})
|
||||||
console.log(result);
|
console.log(result)
|
||||||
return {
|
return {
|
||||||
metadata: {
|
metadata: {
|
||||||
result,
|
result,
|
||||||
},
|
},
|
||||||
output: JSON.stringify(result, null, 2),
|
output: JSON.stringify(result, null, 2),
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { z } from "zod";
|
import { z } from "zod"
|
||||||
import * as path from "path";
|
import * as path from "path"
|
||||||
import * as fs from "fs/promises";
|
import * as fs from "fs/promises"
|
||||||
import { Tool } from "./tool";
|
import { Tool } from "./tool"
|
||||||
import { FileTimes } from "./util/file-times";
|
import { FileTimes } from "./util/file-times"
|
||||||
|
|
||||||
const DESCRIPTION = `Applies a patch to multiple files in one operation. This tool is useful for making coordinated changes across multiple files.
|
const DESCRIPTION = `Applies a patch to multiple files in one operation. This tool is useful for making coordinated changes across multiple files.
|
||||||
|
|
||||||
|
@ -31,198 +31,198 @@ CRITICAL REQUIREMENTS FOR USING THIS TOOL:
|
||||||
3. VALIDATION: Ensure edits result in idiomatic, correct code
|
3. VALIDATION: Ensure edits result in idiomatic, correct code
|
||||||
4. PATHS: Always use absolute file paths (starting with /)
|
4. PATHS: Always use absolute file paths (starting with /)
|
||||||
|
|
||||||
The tool will apply all changes in a single atomic operation.`;
|
The tool will apply all changes in a single atomic operation.`
|
||||||
|
|
||||||
const PatchParams = z.object({
|
const PatchParams = z.object({
|
||||||
patchText: z
|
patchText: z
|
||||||
.string()
|
.string()
|
||||||
.describe("The full patch text that describes all changes to be made"),
|
.describe("The full patch text that describes all changes to be made"),
|
||||||
});
|
})
|
||||||
|
|
||||||
interface PatchResponseMetadata {
|
interface PatchResponseMetadata {
|
||||||
changed: string[];
|
changed: string[]
|
||||||
additions: number;
|
additions: number
|
||||||
removals: number;
|
removals: number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Change {
|
interface Change {
|
||||||
type: "add" | "update" | "delete";
|
type: "add" | "update" | "delete"
|
||||||
old_content?: string;
|
old_content?: string
|
||||||
new_content?: string;
|
new_content?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Commit {
|
interface Commit {
|
||||||
changes: Record<string, Change>;
|
changes: Record<string, Change>
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PatchOperation {
|
interface PatchOperation {
|
||||||
type: "update" | "add" | "delete";
|
type: "update" | "add" | "delete"
|
||||||
filePath: string;
|
filePath: string
|
||||||
hunks?: PatchHunk[];
|
hunks?: PatchHunk[]
|
||||||
content?: string;
|
content?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PatchHunk {
|
interface PatchHunk {
|
||||||
contextLine: string;
|
contextLine: string
|
||||||
changes: PatchChange[];
|
changes: PatchChange[]
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PatchChange {
|
interface PatchChange {
|
||||||
type: "keep" | "remove" | "add";
|
type: "keep" | "remove" | "add"
|
||||||
content: string;
|
content: string
|
||||||
}
|
}
|
||||||
|
|
||||||
function identifyFilesNeeded(patchText: string): string[] {
|
function identifyFilesNeeded(patchText: string): string[] {
|
||||||
const files: string[] = [];
|
const files: string[] = []
|
||||||
const lines = patchText.split("\n");
|
const lines = patchText.split("\n")
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
if (
|
if (
|
||||||
line.startsWith("*** Update File:") ||
|
line.startsWith("*** Update File:") ||
|
||||||
line.startsWith("*** Delete File:")
|
line.startsWith("*** Delete File:")
|
||||||
) {
|
) {
|
||||||
const filePath = line.split(":", 2)[1]?.trim();
|
const filePath = line.split(":", 2)[1]?.trim()
|
||||||
if (filePath) files.push(filePath);
|
if (filePath) files.push(filePath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return files;
|
return files
|
||||||
}
|
}
|
||||||
|
|
||||||
function identifyFilesAdded(patchText: string): string[] {
|
function identifyFilesAdded(patchText: string): string[] {
|
||||||
const files: string[] = [];
|
const files: string[] = []
|
||||||
const lines = patchText.split("\n");
|
const lines = patchText.split("\n")
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
if (line.startsWith("*** Add File:")) {
|
if (line.startsWith("*** Add File:")) {
|
||||||
const filePath = line.split(":", 2)[1]?.trim();
|
const filePath = line.split(":", 2)[1]?.trim()
|
||||||
if (filePath) files.push(filePath);
|
if (filePath) files.push(filePath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return files;
|
return files
|
||||||
}
|
}
|
||||||
|
|
||||||
function textToPatch(
|
function textToPatch(
|
||||||
patchText: string,
|
patchText: string,
|
||||||
_currentFiles: Record<string, string>,
|
_currentFiles: Record<string, string>,
|
||||||
): [PatchOperation[], number] {
|
): [PatchOperation[], number] {
|
||||||
const operations: PatchOperation[] = [];
|
const operations: PatchOperation[] = []
|
||||||
const lines = patchText.split("\n");
|
const lines = patchText.split("\n")
|
||||||
let i = 0;
|
let i = 0
|
||||||
let fuzz = 0;
|
let fuzz = 0
|
||||||
|
|
||||||
while (i < lines.length) {
|
while (i < lines.length) {
|
||||||
const line = lines[i];
|
const line = lines[i]
|
||||||
|
|
||||||
if (line.startsWith("*** Update File:")) {
|
if (line.startsWith("*** Update File:")) {
|
||||||
const filePath = line.split(":", 2)[1]?.trim();
|
const filePath = line.split(":", 2)[1]?.trim()
|
||||||
if (!filePath) {
|
if (!filePath) {
|
||||||
i++;
|
i++
|
||||||
continue;
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
const hunks: PatchHunk[] = [];
|
const hunks: PatchHunk[] = []
|
||||||
i++;
|
i++
|
||||||
|
|
||||||
while (i < lines.length && !lines[i].startsWith("***")) {
|
while (i < lines.length && !lines[i].startsWith("***")) {
|
||||||
if (lines[i].startsWith("@@")) {
|
if (lines[i].startsWith("@@")) {
|
||||||
const contextLine = lines[i].substring(2).trim();
|
const contextLine = lines[i].substring(2).trim()
|
||||||
const changes: PatchChange[] = [];
|
const changes: PatchChange[] = []
|
||||||
i++;
|
i++
|
||||||
|
|
||||||
while (
|
while (
|
||||||
i < lines.length &&
|
i < lines.length &&
|
||||||
!lines[i].startsWith("@@") &&
|
!lines[i].startsWith("@@") &&
|
||||||
!lines[i].startsWith("***")
|
!lines[i].startsWith("***")
|
||||||
) {
|
) {
|
||||||
const changeLine = lines[i];
|
const changeLine = lines[i]
|
||||||
if (changeLine.startsWith(" ")) {
|
if (changeLine.startsWith(" ")) {
|
||||||
changes.push({ type: "keep", content: changeLine.substring(1) });
|
changes.push({ type: "keep", content: changeLine.substring(1) })
|
||||||
} else if (changeLine.startsWith("-")) {
|
} else if (changeLine.startsWith("-")) {
|
||||||
changes.push({
|
changes.push({
|
||||||
type: "remove",
|
type: "remove",
|
||||||
content: changeLine.substring(1),
|
content: changeLine.substring(1),
|
||||||
});
|
})
|
||||||
} else if (changeLine.startsWith("+")) {
|
} else if (changeLine.startsWith("+")) {
|
||||||
changes.push({ type: "add", content: changeLine.substring(1) });
|
changes.push({ type: "add", content: changeLine.substring(1) })
|
||||||
}
|
}
|
||||||
i++;
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
hunks.push({ contextLine, changes });
|
hunks.push({ contextLine, changes })
|
||||||
} else {
|
} else {
|
||||||
i++;
|
i++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
operations.push({ type: "update", filePath, hunks });
|
operations.push({ type: "update", filePath, hunks })
|
||||||
} else if (line.startsWith("*** Add File:")) {
|
} else if (line.startsWith("*** Add File:")) {
|
||||||
const filePath = line.split(":", 2)[1]?.trim();
|
const filePath = line.split(":", 2)[1]?.trim()
|
||||||
if (!filePath) {
|
if (!filePath) {
|
||||||
i++;
|
i++
|
||||||
continue;
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
let content = "";
|
let content = ""
|
||||||
i++;
|
i++
|
||||||
|
|
||||||
while (i < lines.length && !lines[i].startsWith("***")) {
|
while (i < lines.length && !lines[i].startsWith("***")) {
|
||||||
if (lines[i].startsWith("+")) {
|
if (lines[i].startsWith("+")) {
|
||||||
content += lines[i].substring(1) + "\n";
|
content += lines[i].substring(1) + "\n"
|
||||||
}
|
}
|
||||||
i++;
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
operations.push({ type: "add", filePath, content: content.slice(0, -1) });
|
operations.push({ type: "add", filePath, content: content.slice(0, -1) })
|
||||||
} else if (line.startsWith("*** Delete File:")) {
|
} else if (line.startsWith("*** Delete File:")) {
|
||||||
const filePath = line.split(":", 2)[1]?.trim();
|
const filePath = line.split(":", 2)[1]?.trim()
|
||||||
if (filePath) {
|
if (filePath) {
|
||||||
operations.push({ type: "delete", filePath });
|
operations.push({ type: "delete", filePath })
|
||||||
}
|
}
|
||||||
i++;
|
i++
|
||||||
} else {
|
} else {
|
||||||
i++;
|
i++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return [operations, fuzz];
|
return [operations, fuzz]
|
||||||
}
|
}
|
||||||
|
|
||||||
function patchToCommit(
|
function patchToCommit(
|
||||||
operations: PatchOperation[],
|
operations: PatchOperation[],
|
||||||
currentFiles: Record<string, string>,
|
currentFiles: Record<string, string>,
|
||||||
): Commit {
|
): Commit {
|
||||||
const changes: Record<string, Change> = {};
|
const changes: Record<string, Change> = {}
|
||||||
|
|
||||||
for (const op of operations) {
|
for (const op of operations) {
|
||||||
if (op.type === "delete") {
|
if (op.type === "delete") {
|
||||||
changes[op.filePath] = {
|
changes[op.filePath] = {
|
||||||
type: "delete",
|
type: "delete",
|
||||||
old_content: currentFiles[op.filePath] || "",
|
old_content: currentFiles[op.filePath] || "",
|
||||||
};
|
}
|
||||||
} else if (op.type === "add") {
|
} else if (op.type === "add") {
|
||||||
changes[op.filePath] = {
|
changes[op.filePath] = {
|
||||||
type: "add",
|
type: "add",
|
||||||
new_content: op.content || "",
|
new_content: op.content || "",
|
||||||
};
|
}
|
||||||
} else if (op.type === "update" && op.hunks) {
|
} else if (op.type === "update" && op.hunks) {
|
||||||
const originalContent = currentFiles[op.filePath] || "";
|
const originalContent = currentFiles[op.filePath] || ""
|
||||||
const lines = originalContent.split("\n");
|
const lines = originalContent.split("\n")
|
||||||
|
|
||||||
for (const hunk of op.hunks) {
|
for (const hunk of op.hunks) {
|
||||||
const contextIndex = lines.findIndex((line) =>
|
const contextIndex = lines.findIndex((line) =>
|
||||||
line.includes(hunk.contextLine),
|
line.includes(hunk.contextLine),
|
||||||
);
|
)
|
||||||
if (contextIndex === -1) {
|
if (contextIndex === -1) {
|
||||||
throw new Error(`Context line not found: ${hunk.contextLine}`);
|
throw new Error(`Context line not found: ${hunk.contextLine}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
let currentIndex = contextIndex;
|
let currentIndex = contextIndex
|
||||||
for (const change of hunk.changes) {
|
for (const change of hunk.changes) {
|
||||||
if (change.type === "keep") {
|
if (change.type === "keep") {
|
||||||
currentIndex++;
|
currentIndex++
|
||||||
} else if (change.type === "remove") {
|
} else if (change.type === "remove") {
|
||||||
lines.splice(currentIndex, 1);
|
lines.splice(currentIndex, 1)
|
||||||
} else if (change.type === "add") {
|
} else if (change.type === "add") {
|
||||||
lines.splice(currentIndex, 0, change.content);
|
lines.splice(currentIndex, 0, change.content)
|
||||||
currentIndex++;
|
currentIndex++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -231,11 +231,11 @@ function patchToCommit(
|
||||||
type: "update",
|
type: "update",
|
||||||
old_content: originalContent,
|
old_content: originalContent,
|
||||||
new_content: lines.join("\n"),
|
new_content: lines.join("\n"),
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { changes };
|
return { changes }
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateDiff(
|
function generateDiff(
|
||||||
|
@ -244,11 +244,11 @@ function generateDiff(
|
||||||
filePath: string,
|
filePath: string,
|
||||||
): [string, number, number] {
|
): [string, number, number] {
|
||||||
// Mock implementation - would need actual diff generation
|
// Mock implementation - would need actual diff generation
|
||||||
const lines1 = oldContent.split("\n");
|
const lines1 = oldContent.split("\n")
|
||||||
const lines2 = newContent.split("\n");
|
const lines2 = newContent.split("\n")
|
||||||
const additions = Math.max(0, lines2.length - lines1.length);
|
const additions = Math.max(0, lines2.length - lines1.length)
|
||||||
const removals = Math.max(0, lines1.length - lines2.length);
|
const removals = Math.max(0, lines1.length - lines2.length)
|
||||||
return [`--- ${filePath}\n+++ ${filePath}\n`, additions, removals];
|
return [`--- ${filePath}\n+++ ${filePath}\n`, additions, removals]
|
||||||
}
|
}
|
||||||
|
|
||||||
async function applyCommit(
|
async function applyCommit(
|
||||||
|
@ -258,9 +258,9 @@ async function applyCommit(
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
for (const [filePath, change] of Object.entries(commit.changes)) {
|
for (const [filePath, change] of Object.entries(commit.changes)) {
|
||||||
if (change.type === "delete") {
|
if (change.type === "delete") {
|
||||||
await deleteFile(filePath);
|
await deleteFile(filePath)
|
||||||
} else if (change.new_content !== undefined) {
|
} else if (change.new_content !== undefined) {
|
||||||
await writeFile(filePath, change.new_content);
|
await writeFile(filePath, change.new_content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -271,142 +271,142 @@ export const patch = Tool.define({
|
||||||
parameters: PatchParams,
|
parameters: PatchParams,
|
||||||
execute: async (params) => {
|
execute: async (params) => {
|
||||||
if (!params.patchText) {
|
if (!params.patchText) {
|
||||||
throw new Error("patchText is required");
|
throw new Error("patchText is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Identify all files needed for the patch and verify they've been read
|
// Identify all files needed for the patch and verify they've been read
|
||||||
const filesToRead = identifyFilesNeeded(params.patchText);
|
const filesToRead = identifyFilesNeeded(params.patchText)
|
||||||
for (const filePath of filesToRead) {
|
for (const filePath of filesToRead) {
|
||||||
let absPath = filePath;
|
let absPath = filePath
|
||||||
if (!path.isAbsolute(absPath)) {
|
if (!path.isAbsolute(absPath)) {
|
||||||
absPath = path.resolve(process.cwd(), absPath);
|
absPath = path.resolve(process.cwd(), absPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!FileTimes.get(absPath)) {
|
if (!FileTimes.get(absPath)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`you must read the file ${filePath} before patching it. Use the FileRead tool first`,
|
`you must read the file ${filePath} before patching it. Use the FileRead tool first`,
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const stats = await fs.stat(absPath);
|
const stats = await fs.stat(absPath)
|
||||||
if (stats.isDirectory()) {
|
if (stats.isDirectory()) {
|
||||||
throw new Error(`path is a directory, not a file: ${absPath}`);
|
throw new Error(`path is a directory, not a file: ${absPath}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const lastRead = FileTimes.get(absPath);
|
const lastRead = FileTimes.get(absPath)
|
||||||
if (lastRead && stats.mtime > lastRead) {
|
if (lastRead && stats.mtime > lastRead) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`file ${absPath} has been modified since it was last read (mod time: ${stats.mtime.toISOString()}, last read: ${lastRead.toISOString()})`,
|
`file ${absPath} has been modified since it was last read (mod time: ${stats.mtime.toISOString()}, last read: ${lastRead.toISOString()})`,
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
if (error.code === "ENOENT") {
|
if (error.code === "ENOENT") {
|
||||||
throw new Error(`file not found: ${absPath}`);
|
throw new Error(`file not found: ${absPath}`)
|
||||||
}
|
}
|
||||||
throw new Error(`failed to access file: ${error.message}`);
|
throw new Error(`failed to access file: ${error.message}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for new files to ensure they don't already exist
|
// Check for new files to ensure they don't already exist
|
||||||
const filesToAdd = identifyFilesAdded(params.patchText);
|
const filesToAdd = identifyFilesAdded(params.patchText)
|
||||||
for (const filePath of filesToAdd) {
|
for (const filePath of filesToAdd) {
|
||||||
let absPath = filePath;
|
let absPath = filePath
|
||||||
if (!path.isAbsolute(absPath)) {
|
if (!path.isAbsolute(absPath)) {
|
||||||
absPath = path.resolve(process.cwd(), absPath);
|
absPath = path.resolve(process.cwd(), absPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await fs.stat(absPath);
|
await fs.stat(absPath)
|
||||||
throw new Error(`file already exists and cannot be added: ${absPath}`);
|
throw new Error(`file already exists and cannot be added: ${absPath}`)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
if (error.code !== "ENOENT") {
|
if (error.code !== "ENOENT") {
|
||||||
throw new Error(`failed to check file: ${error.message}`);
|
throw new Error(`failed to check file: ${error.message}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load all required files
|
// Load all required files
|
||||||
const currentFiles: Record<string, string> = {};
|
const currentFiles: Record<string, string> = {}
|
||||||
for (const filePath of filesToRead) {
|
for (const filePath of filesToRead) {
|
||||||
let absPath = filePath;
|
let absPath = filePath
|
||||||
if (!path.isAbsolute(absPath)) {
|
if (!path.isAbsolute(absPath)) {
|
||||||
absPath = path.resolve(process.cwd(), absPath);
|
absPath = path.resolve(process.cwd(), absPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const content = await fs.readFile(absPath, "utf-8");
|
const content = await fs.readFile(absPath, "utf-8")
|
||||||
currentFiles[filePath] = content;
|
currentFiles[filePath] = content
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
throw new Error(`failed to read file ${absPath}: ${error.message}`);
|
throw new Error(`failed to read file ${absPath}: ${error.message}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process the patch
|
// Process the patch
|
||||||
const [patch, fuzz] = textToPatch(params.patchText, currentFiles);
|
const [patch, fuzz] = textToPatch(params.patchText, currentFiles)
|
||||||
if (fuzz > 3) {
|
if (fuzz > 3) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`patch contains fuzzy matches (fuzz level: ${fuzz}). Please make your context lines more precise`,
|
`patch contains fuzzy matches (fuzz level: ${fuzz}). Please make your context lines more precise`,
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert patch to commit
|
// Convert patch to commit
|
||||||
const commit = patchToCommit(patch, currentFiles);
|
const commit = patchToCommit(patch, currentFiles)
|
||||||
|
|
||||||
// Apply the changes to the filesystem
|
// Apply the changes to the filesystem
|
||||||
await applyCommit(
|
await applyCommit(
|
||||||
commit,
|
commit,
|
||||||
async (filePath: string, content: string) => {
|
async (filePath: string, content: string) => {
|
||||||
let absPath = filePath;
|
let absPath = filePath
|
||||||
if (!path.isAbsolute(absPath)) {
|
if (!path.isAbsolute(absPath)) {
|
||||||
absPath = path.resolve(process.cwd(), absPath);
|
absPath = path.resolve(process.cwd(), absPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create parent directories if needed
|
// Create parent directories if needed
|
||||||
const dir = path.dirname(absPath);
|
const dir = path.dirname(absPath)
|
||||||
await fs.mkdir(dir, { recursive: true });
|
await fs.mkdir(dir, { recursive: true })
|
||||||
await fs.writeFile(absPath, content, "utf-8");
|
await fs.writeFile(absPath, content, "utf-8")
|
||||||
},
|
},
|
||||||
async (filePath: string) => {
|
async (filePath: string) => {
|
||||||
let absPath = filePath;
|
let absPath = filePath
|
||||||
if (!path.isAbsolute(absPath)) {
|
if (!path.isAbsolute(absPath)) {
|
||||||
absPath = path.resolve(process.cwd(), absPath);
|
absPath = path.resolve(process.cwd(), absPath)
|
||||||
}
|
}
|
||||||
await fs.unlink(absPath);
|
await fs.unlink(absPath)
|
||||||
},
|
},
|
||||||
);
|
)
|
||||||
|
|
||||||
// Calculate statistics
|
// Calculate statistics
|
||||||
const changedFiles: string[] = [];
|
const changedFiles: string[] = []
|
||||||
let totalAdditions = 0;
|
let totalAdditions = 0
|
||||||
let totalRemovals = 0;
|
let totalRemovals = 0
|
||||||
|
|
||||||
for (const [filePath, change] of Object.entries(commit.changes)) {
|
for (const [filePath, change] of Object.entries(commit.changes)) {
|
||||||
let absPath = filePath;
|
let absPath = filePath
|
||||||
if (!path.isAbsolute(absPath)) {
|
if (!path.isAbsolute(absPath)) {
|
||||||
absPath = path.resolve(process.cwd(), absPath);
|
absPath = path.resolve(process.cwd(), absPath)
|
||||||
}
|
}
|
||||||
changedFiles.push(absPath);
|
changedFiles.push(absPath)
|
||||||
|
|
||||||
const oldContent = change.old_content || "";
|
const oldContent = change.old_content || ""
|
||||||
const newContent = change.new_content || "";
|
const newContent = change.new_content || ""
|
||||||
|
|
||||||
// Calculate diff statistics
|
// Calculate diff statistics
|
||||||
const [, additions, removals] = generateDiff(
|
const [, additions, removals] = generateDiff(
|
||||||
oldContent,
|
oldContent,
|
||||||
newContent,
|
newContent,
|
||||||
filePath,
|
filePath,
|
||||||
);
|
)
|
||||||
totalAdditions += additions;
|
totalAdditions += additions
|
||||||
totalRemovals += removals;
|
totalRemovals += removals
|
||||||
|
|
||||||
// Record file operations
|
// Record file operations
|
||||||
FileTimes.write(absPath);
|
FileTimes.write(absPath)
|
||||||
FileTimes.read(absPath);
|
FileTimes.read(absPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = `Patch applied successfully. ${changedFiles.length} files changed, ${totalAdditions} additions, ${totalRemovals} removals`;
|
const result = `Patch applied successfully. ${changedFiles.length} files changed, ${totalAdditions} additions, ${totalRemovals} removals`
|
||||||
const output = result;
|
const output = result
|
||||||
|
|
||||||
return {
|
return {
|
||||||
metadata: {
|
metadata: {
|
||||||
|
@ -415,6 +415,6 @@ export const patch = Tool.define({
|
||||||
removals: totalRemovals,
|
removals: totalRemovals,
|
||||||
} satisfies PatchResponseMetadata,
|
} satisfies PatchResponseMetadata,
|
||||||
output,
|
output,
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
import { tool, type Tool as AITool } from "ai";
|
import { tool, type Tool as AITool } from "ai"
|
||||||
import { Log } from "../util/log";
|
import { Log } from "../util/log"
|
||||||
|
|
||||||
const log = Log.create({ service: "tool" });
|
const log = Log.create({ service: "tool" })
|
||||||
|
|
||||||
export namespace Tool {
|
export namespace Tool {
|
||||||
export interface Metadata<
|
export interface Metadata<
|
||||||
Properties extends Record<string, any> = Record<string, any>,
|
Properties extends Record<string, any> = Record<string, any>,
|
||||||
> {
|
> {
|
||||||
properties: Properties;
|
properties: Properties
|
||||||
time: {
|
time: {
|
||||||
start: number;
|
start: number
|
||||||
end: number;
|
end: number
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
export function define<
|
export function define<
|
||||||
Params,
|
Params,
|
||||||
|
@ -19,7 +19,7 @@ export namespace Tool {
|
||||||
Name extends string,
|
Name extends string,
|
||||||
>(
|
>(
|
||||||
input: AITool<Params, Output> & {
|
input: AITool<Params, Output> & {
|
||||||
name: Name;
|
name: Name
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
return tool({
|
return tool({
|
||||||
|
@ -29,33 +29,33 @@ export namespace Tool {
|
||||||
id: opts.toolCallId,
|
id: opts.toolCallId,
|
||||||
name: input.name,
|
name: input.name,
|
||||||
...params,
|
...params,
|
||||||
});
|
})
|
||||||
try {
|
try {
|
||||||
const start = Date.now();
|
const start = Date.now()
|
||||||
const result = await input.execute!(params, opts);
|
const result = await input.execute!(params, opts)
|
||||||
const metadata: Metadata<Output["metadata"]> = {
|
const metadata: Metadata<Output["metadata"]> = {
|
||||||
...result.metadata,
|
...result.metadata,
|
||||||
time: {
|
time: {
|
||||||
start,
|
start,
|
||||||
end: Date.now(),
|
end: Date.now(),
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
return {
|
return {
|
||||||
metadata,
|
metadata,
|
||||||
output: result.output,
|
output: result.output,
|
||||||
};
|
}
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
log.error("error", {
|
log.error("error", {
|
||||||
msg: e.toString(),
|
msg: e.toString(),
|
||||||
});
|
})
|
||||||
return {
|
return {
|
||||||
metadata: {
|
metadata: {
|
||||||
error: true,
|
error: true,
|
||||||
},
|
},
|
||||||
output: "An error occurred: " + e.toString(),
|
output: "An error occurred: " + e.toString(),
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
import { App } from "../../app/app";
|
import { App } from "../../app/app"
|
||||||
|
|
||||||
export namespace FileTimes {
|
export namespace FileTimes {
|
||||||
export const state = App.state("tool.filetimes", () => ({
|
export const state = App.state("tool.filetimes", () => ({
|
||||||
read: new Map<string, Date>(),
|
read: new Map<string, Date>(),
|
||||||
write: new Map<string, Date>(),
|
write: new Map<string, Date>(),
|
||||||
}));
|
}))
|
||||||
|
|
||||||
export function read(filePath: string) {
|
export function read(filePath: string) {
|
||||||
state().read.set(filePath, new Date());
|
state().read.set(filePath, new Date())
|
||||||
}
|
}
|
||||||
|
|
||||||
export function write(filePath: string) {
|
export function write(filePath: string) {
|
||||||
state().write.set(filePath, new Date());
|
state().write.set(filePath, new Date())
|
||||||
}
|
}
|
||||||
|
|
||||||
export function get(filePath: string): Date | null {
|
export function get(filePath: string): Date | null {
|
||||||
return state().read.get(filePath) || null;
|
return state().read.get(filePath) || null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import { z } from "zod";
|
import { z } from "zod"
|
||||||
import * as fs from "fs";
|
import * as fs from "fs"
|
||||||
import * as path from "path";
|
import * as path from "path"
|
||||||
import { Tool } from "./tool";
|
import { Tool } from "./tool"
|
||||||
import { LSP } from "../lsp";
|
import { LSP } from "../lsp"
|
||||||
import { FileTimes } from "./util/file-times";
|
import { FileTimes } from "./util/file-times"
|
||||||
|
|
||||||
const MAX_READ_SIZE = 250 * 1024;
|
const MAX_READ_SIZE = 250 * 1024
|
||||||
const DEFAULT_READ_LIMIT = 2000;
|
const DEFAULT_READ_LIMIT = 2000
|
||||||
const MAX_LINE_LENGTH = 2000;
|
const MAX_LINE_LENGTH = 2000
|
||||||
|
|
||||||
const DESCRIPTION = `File viewing tool that reads and displays the contents of files with line numbers, allowing you to examine code, logs, or text data.
|
const DESCRIPTION = `File viewing tool that reads and displays the contents of files with line numbers, allowing you to examine code, logs, or text data.
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ LIMITATIONS:
|
||||||
TIPS:
|
TIPS:
|
||||||
- Use with Glob tool to first find files you want to view
|
- Use with Glob tool to first find files you want to view
|
||||||
- For code exploration, first use Grep to find relevant files, then View to examine them
|
- For code exploration, first use Grep to find relevant files, then View to examine them
|
||||||
- When viewing large files, use the offset parameter to read specific sections`;
|
- When viewing large files, use the offset parameter to read specific sections`
|
||||||
|
|
||||||
export const view = Tool.define({
|
export const view = Tool.define({
|
||||||
name: "opencode.view",
|
name: "opencode.view",
|
||||||
|
@ -55,17 +55,17 @@ export const view = Tool.define({
|
||||||
.optional(),
|
.optional(),
|
||||||
}),
|
}),
|
||||||
async execute(params) {
|
async execute(params) {
|
||||||
let filePath = params.filePath;
|
let filePath = params.filePath
|
||||||
if (!path.isAbsolute(filePath)) {
|
if (!path.isAbsolute(filePath)) {
|
||||||
filePath = path.join(process.cwd(), filePath);
|
filePath = path.join(process.cwd(), filePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
const file = Bun.file(filePath);
|
const file = Bun.file(filePath)
|
||||||
if (!(await file.exists())) {
|
if (!(await file.exists())) {
|
||||||
const dir = path.dirname(filePath);
|
const dir = path.dirname(filePath)
|
||||||
const base = path.basename(filePath);
|
const base = path.basename(filePath)
|
||||||
|
|
||||||
const dirEntries = fs.readdirSync(dir);
|
const dirEntries = fs.readdirSync(dir)
|
||||||
const suggestions = dirEntries
|
const suggestions = dirEntries
|
||||||
.filter(
|
.filter(
|
||||||
(entry) =>
|
(entry) =>
|
||||||
|
@ -73,80 +73,80 @@ export const view = Tool.define({
|
||||||
base.toLowerCase().includes(entry.toLowerCase()),
|
base.toLowerCase().includes(entry.toLowerCase()),
|
||||||
)
|
)
|
||||||
.map((entry) => path.join(dir, entry))
|
.map((entry) => path.join(dir, entry))
|
||||||
.slice(0, 3);
|
.slice(0, 3)
|
||||||
|
|
||||||
if (suggestions.length > 0) {
|
if (suggestions.length > 0) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`File not found: ${filePath}\n\nDid you mean one of these?\n${suggestions.join("\n")}`,
|
`File not found: ${filePath}\n\nDid you mean one of these?\n${suggestions.join("\n")}`,
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(`File not found: ${filePath}`);
|
throw new Error(`File not found: ${filePath}`)
|
||||||
}
|
}
|
||||||
const stats = await file.stat();
|
const stats = await file.stat()
|
||||||
|
|
||||||
if (stats.size > MAX_READ_SIZE)
|
if (stats.size > MAX_READ_SIZE)
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`File is too large (${stats.size} bytes). Maximum size is ${MAX_READ_SIZE} bytes`,
|
`File is too large (${stats.size} bytes). Maximum size is ${MAX_READ_SIZE} bytes`,
|
||||||
);
|
)
|
||||||
const limit = params.limit ?? DEFAULT_READ_LIMIT;
|
const limit = params.limit ?? DEFAULT_READ_LIMIT
|
||||||
const offset = params.offset || 0;
|
const offset = params.offset || 0
|
||||||
const isImage = isImageFile(filePath);
|
const isImage = isImageFile(filePath)
|
||||||
if (isImage)
|
if (isImage)
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`This is an image file of type: ${isImage}\nUse a different tool to process images`,
|
`This is an image file of type: ${isImage}\nUse a different tool to process images`,
|
||||||
);
|
)
|
||||||
const lines = await file.text().then((text) => text.split("\n"));
|
const lines = await file.text().then((text) => text.split("\n"))
|
||||||
const raw = lines.slice(offset, offset + limit).map((line) => {
|
const raw = lines.slice(offset, offset + limit).map((line) => {
|
||||||
return line.length > MAX_LINE_LENGTH
|
return line.length > MAX_LINE_LENGTH
|
||||||
? line.substring(0, MAX_LINE_LENGTH) + "..."
|
? line.substring(0, MAX_LINE_LENGTH) + "..."
|
||||||
: line;
|
: line
|
||||||
});
|
})
|
||||||
const content = raw.map((line, index) => {
|
const content = raw.map((line, index) => {
|
||||||
return `${(index + offset + 1).toString().padStart(5, "0")}| ${line}`;
|
return `${(index + offset + 1).toString().padStart(5, "0")}| ${line}`
|
||||||
});
|
})
|
||||||
const preview = raw.slice(0, 20).join("\n");
|
const preview = raw.slice(0, 20).join("\n")
|
||||||
|
|
||||||
let output = "<file>\n";
|
let output = "<file>\n"
|
||||||
output += content.join("\n");
|
output += content.join("\n")
|
||||||
|
|
||||||
if (lines.length > offset + content.length) {
|
if (lines.length > offset + content.length) {
|
||||||
output += `\n\n(File has more lines. Use 'offset' parameter to read beyond line ${
|
output += `\n\n(File has more lines. Use 'offset' parameter to read beyond line ${
|
||||||
offset + content.length
|
offset + content.length
|
||||||
})`;
|
})`
|
||||||
}
|
}
|
||||||
output += "\n</file>";
|
output += "\n</file>"
|
||||||
|
|
||||||
// just warms the lsp client
|
// just warms the lsp client
|
||||||
LSP.file(filePath);
|
LSP.file(filePath)
|
||||||
FileTimes.read(filePath);
|
FileTimes.read(filePath)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
output,
|
output,
|
||||||
metadata: {
|
metadata: {
|
||||||
preview,
|
preview,
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
function isImageFile(filePath: string): string | false {
|
function isImageFile(filePath: string): string | false {
|
||||||
const ext = path.extname(filePath).toLowerCase();
|
const ext = path.extname(filePath).toLowerCase()
|
||||||
switch (ext) {
|
switch (ext) {
|
||||||
case ".jpg":
|
case ".jpg":
|
||||||
case ".jpeg":
|
case ".jpeg":
|
||||||
return "JPEG";
|
return "JPEG"
|
||||||
case ".png":
|
case ".png":
|
||||||
return "PNG";
|
return "PNG"
|
||||||
case ".gif":
|
case ".gif":
|
||||||
return "GIF";
|
return "GIF"
|
||||||
case ".bmp":
|
case ".bmp":
|
||||||
return "BMP";
|
return "BMP"
|
||||||
case ".svg":
|
case ".svg":
|
||||||
return "SVG";
|
return "SVG"
|
||||||
case ".webp":
|
case ".webp":
|
||||||
return "WebP";
|
return "WebP"
|
||||||
default:
|
default:
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,25 @@
|
||||||
import { AsyncLocalStorage } from "async_hooks";
|
import { AsyncLocalStorage } from "async_hooks"
|
||||||
|
|
||||||
export namespace Context {
|
export namespace Context {
|
||||||
export class NotFound extends Error {
|
export class NotFound extends Error {
|
||||||
constructor(public readonly name: string) {
|
constructor(public readonly name: string) {
|
||||||
super(`No context found for ${name}`);
|
super(`No context found for ${name}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function create<T>(name: string) {
|
export function create<T>(name: string) {
|
||||||
const storage = new AsyncLocalStorage<T>();
|
const storage = new AsyncLocalStorage<T>()
|
||||||
return {
|
return {
|
||||||
use() {
|
use() {
|
||||||
const result = storage.getStore();
|
const result = storage.getStore()
|
||||||
if (!result) {
|
if (!result) {
|
||||||
throw new NotFound(name);
|
throw new NotFound(name)
|
||||||
}
|
}
|
||||||
return result;
|
return result
|
||||||
},
|
},
|
||||||
provide<R>(value: T, fn: () => R) {
|
provide<R>(value: T, fn: () => R) {
|
||||||
return storage.run<R>(value, fn);
|
return storage.run<R>(value, fn)
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,37 +1,37 @@
|
||||||
import path from "path";
|
import path from "path"
|
||||||
import { AppPath } from "../app/path";
|
import { AppPath } from "../app/path"
|
||||||
import fs from "fs/promises";
|
import fs from "fs/promises"
|
||||||
export namespace Log {
|
export namespace Log {
|
||||||
const write = {
|
const write = {
|
||||||
out: (msg: string) => {
|
out: (msg: string) => {
|
||||||
process.stdout.write(msg);
|
process.stdout.write(msg)
|
||||||
},
|
},
|
||||||
err: (msg: string) => {
|
err: (msg: string) => {
|
||||||
process.stderr.write(msg);
|
process.stderr.write(msg)
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
|
|
||||||
export async function file(directory: string) {
|
export async function file(directory: string) {
|
||||||
const outPath = path.join(AppPath.data(directory), "opencode.out.log");
|
const outPath = path.join(AppPath.data(directory), "opencode.out.log")
|
||||||
const errPath = path.join(AppPath.data(directory), "opencode.err.log");
|
const errPath = path.join(AppPath.data(directory), "opencode.err.log")
|
||||||
await fs.truncate(outPath).catch(() => {});
|
await fs.truncate(outPath).catch(() => {})
|
||||||
await fs.truncate(errPath).catch(() => {});
|
await fs.truncate(errPath).catch(() => {})
|
||||||
const out = Bun.file(outPath);
|
const out = Bun.file(outPath)
|
||||||
const err = Bun.file(errPath);
|
const err = Bun.file(errPath)
|
||||||
const outWriter = out.writer();
|
const outWriter = out.writer()
|
||||||
const errWriter = err.writer();
|
const errWriter = err.writer()
|
||||||
write["out"] = (msg) => {
|
write["out"] = (msg) => {
|
||||||
outWriter.write(msg);
|
outWriter.write(msg)
|
||||||
outWriter.flush();
|
outWriter.flush()
|
||||||
};
|
}
|
||||||
write["err"] = (msg) => {
|
write["err"] = (msg) => {
|
||||||
errWriter.write(msg);
|
errWriter.write(msg)
|
||||||
errWriter.flush();
|
errWriter.flush()
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function create(tags?: Record<string, any>) {
|
export function create(tags?: Record<string, any>) {
|
||||||
tags = tags || {};
|
tags = tags || {}
|
||||||
|
|
||||||
function build(message: any, extra?: Record<string, any>) {
|
function build(message: any, extra?: Record<string, any>) {
|
||||||
const prefix = Object.entries({
|
const prefix = Object.entries({
|
||||||
|
@ -40,25 +40,28 @@ export namespace Log {
|
||||||
})
|
})
|
||||||
.filter(([_, value]) => value !== undefined && value !== null)
|
.filter(([_, value]) => value !== undefined && value !== null)
|
||||||
.map(([key, value]) => `${key}=${value}`)
|
.map(([key, value]) => `${key}=${value}`)
|
||||||
.join(" ");
|
.join(" ")
|
||||||
return [new Date().toISOString(), prefix, message].filter(Boolean).join(" ") + "\n";
|
return (
|
||||||
|
[new Date().toISOString(), prefix, message].filter(Boolean).join(" ") +
|
||||||
|
"\n"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
const result = {
|
const result = {
|
||||||
info(message?: any, extra?: Record<string, any>) {
|
info(message?: any, extra?: Record<string, any>) {
|
||||||
write.out(build(message, extra));
|
write.out(build(message, extra))
|
||||||
},
|
},
|
||||||
error(message?: any, extra?: Record<string, any>) {
|
error(message?: any, extra?: Record<string, any>) {
|
||||||
write.err(build(message, extra));
|
write.err(build(message, extra))
|
||||||
},
|
},
|
||||||
tag(key: string, value: string) {
|
tag(key: string, value: string) {
|
||||||
if (tags) tags[key] = value;
|
if (tags) tags[key] = value
|
||||||
return result;
|
return result
|
||||||
},
|
},
|
||||||
clone() {
|
clone() {
|
||||||
return Log.create({ ...tags });
|
return Log.create({ ...tags })
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
|
|
||||||
return result;
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
export const foo: string = "42";
|
export const foo: string = "42"
|
||||||
|
|
||||||
export function dummyFunction(): void {
|
export function dummyFunction(): void {
|
||||||
console.log("This is a dummy function");
|
console.log("This is a dummy function")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { describe, expect, test } from "bun:test";
|
import { describe, expect, test } from "bun:test"
|
||||||
import { App } from "../../src/app/app";
|
import { App } from "../../src/app/app"
|
||||||
import { glob } from "../../src/tool/glob";
|
import { glob } from "../../src/tool/glob"
|
||||||
import { ls } from "../../src/tool/ls";
|
import { ls } from "../../src/tool/ls"
|
||||||
|
|
||||||
describe("tool.glob", () => {
|
describe("tool.glob", () => {
|
||||||
test("truncate", async () => {
|
test("truncate", async () => {
|
||||||
|
@ -14,10 +14,10 @@ describe("tool.glob", () => {
|
||||||
toolCallId: "test",
|
toolCallId: "test",
|
||||||
messages: [],
|
messages: [],
|
||||||
},
|
},
|
||||||
);
|
)
|
||||||
expect(result.metadata.truncated).toBe(true);
|
expect(result.metadata.truncated).toBe(true)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
test("basic", async () => {
|
test("basic", async () => {
|
||||||
await App.provide({ directory: process.cwd() }, async () => {
|
await App.provide({ directory: process.cwd() }, async () => {
|
||||||
let result = await glob.execute(
|
let result = await glob.execute(
|
||||||
|
@ -28,14 +28,14 @@ describe("tool.glob", () => {
|
||||||
toolCallId: "test",
|
toolCallId: "test",
|
||||||
messages: [],
|
messages: [],
|
||||||
},
|
},
|
||||||
);
|
)
|
||||||
expect(result.metadata).toMatchObject({
|
expect(result.metadata).toMatchObject({
|
||||||
truncated: false,
|
truncated: false,
|
||||||
count: 2,
|
count: 2,
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe("tool.ls", () => {
|
describe("tool.ls", () => {
|
||||||
test("basic", async () => {
|
test("basic", async () => {
|
||||||
|
@ -48,8 +48,8 @@ describe("tool.ls", () => {
|
||||||
toolCallId: "test",
|
toolCallId: "test",
|
||||||
messages: [],
|
messages: [],
|
||||||
},
|
},
|
||||||
);
|
)
|
||||||
});
|
})
|
||||||
expect(result.output).toMatchSnapshot();
|
expect(result.output).toMatchSnapshot()
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
|
@ -1,69 +1,69 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
import { defineConfig } from "astro/config";
|
import { defineConfig } from "astro/config"
|
||||||
import starlight from "@astrojs/starlight";
|
import starlight from "@astrojs/starlight"
|
||||||
import solidJs from "@astrojs/solid-js";
|
import solidJs from "@astrojs/solid-js"
|
||||||
import theme from "toolbeam-docs-theme";
|
import theme from "toolbeam-docs-theme"
|
||||||
import { rehypeHeadingIds } from "@astrojs/markdown-remark";
|
import { rehypeHeadingIds } from "@astrojs/markdown-remark"
|
||||||
import rehypeAutolinkHeadings from "rehype-autolink-headings";
|
import rehypeAutolinkHeadings from "rehype-autolink-headings"
|
||||||
|
|
||||||
const discord = "https://discord.gg/sst";
|
const discord = "https://discord.gg/sst"
|
||||||
const github = "https://github.com/sst/opencode";
|
const github = "https://github.com/sst/opencode"
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
devToolbar: {
|
devToolbar: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
},
|
},
|
||||||
markdown: {
|
markdown: {
|
||||||
rehypePlugins: [
|
rehypePlugins: [
|
||||||
rehypeHeadingIds,
|
rehypeHeadingIds,
|
||||||
[rehypeAutolinkHeadings, { behavior: "wrap" }],
|
[rehypeAutolinkHeadings, { behavior: "wrap" }],
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
integrations: [
|
integrations: [
|
||||||
solidJs(),
|
solidJs(),
|
||||||
starlight({
|
starlight({
|
||||||
title: "OpenCode",
|
title: "OpenCode",
|
||||||
expressiveCode: { themes: ["github-light", "github-dark"] },
|
expressiveCode: { themes: ["github-light", "github-dark"] },
|
||||||
social: [
|
social: [
|
||||||
{ icon: "discord", label: "Discord", href: discord },
|
{ icon: "discord", label: "Discord", href: discord },
|
||||||
{ icon: "github", label: "GitHub", href: github },
|
{ icon: "github", label: "GitHub", href: github },
|
||||||
],
|
],
|
||||||
editLink: {
|
editLink: {
|
||||||
baseUrl: `${github}/edit/master/www/`,
|
baseUrl: `${github}/edit/master/www/`,
|
||||||
},
|
},
|
||||||
markdown: {
|
markdown: {
|
||||||
headingLinks: false,
|
headingLinks: false,
|
||||||
},
|
},
|
||||||
customCss: [
|
customCss: ["./src/styles/custom.css"],
|
||||||
"./src/styles/custom.css",
|
logo: {
|
||||||
],
|
light: "./src/assets/logo-light.svg",
|
||||||
logo: {
|
dark: "./src/assets/logo-dark.svg",
|
||||||
light: "./src/assets/logo-light.svg",
|
replacesTitle: true,
|
||||||
dark: "./src/assets/logo-dark.svg",
|
},
|
||||||
replacesTitle: true,
|
sidebar: [
|
||||||
},
|
"docs",
|
||||||
sidebar: [
|
"docs/cli",
|
||||||
"docs",
|
"docs/config",
|
||||||
"docs/cli",
|
"docs/models",
|
||||||
"docs/config",
|
"docs/themes",
|
||||||
"docs/models",
|
"docs/shortcuts",
|
||||||
"docs/themes",
|
"docs/lsp-servers",
|
||||||
"docs/shortcuts",
|
"docs/mcp-servers",
|
||||||
"docs/lsp-servers",
|
],
|
||||||
"docs/mcp-servers",
|
components: {
|
||||||
],
|
Hero: "./src/components/Hero.astro",
|
||||||
components: {
|
Header: "./src/components/Header.astro",
|
||||||
Hero: "./src/components/Hero.astro",
|
},
|
||||||
Header: "./src/components/Header.astro",
|
plugins: [
|
||||||
},
|
theme({
|
||||||
plugins: [theme({
|
// Optionally, add your own header links
|
||||||
// Optionally, add your own header links
|
headerLinks: [
|
||||||
headerLinks: [
|
{ name: "Home", url: "/" },
|
||||||
{ name: "Home", url: "/" },
|
{ name: "Docs", url: "/docs/" },
|
||||||
{ name: "Docs", url: "/docs/" },
|
],
|
||||||
],
|
}),
|
||||||
})],
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
});
|
})
|
||||||
|
|
|
@ -6,7 +6,7 @@ import {
|
||||||
createResource,
|
createResource,
|
||||||
} from "solid-js"
|
} from "solid-js"
|
||||||
import { codeToHtml } from "shiki"
|
import { codeToHtml } from "shiki"
|
||||||
import { transformerNotationDiff } from '@shikijs/transformers'
|
import { transformerNotationDiff } from "@shikijs/transformers"
|
||||||
|
|
||||||
interface CodeBlockProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
interface CodeBlockProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
||||||
code: string
|
code: string
|
||||||
|
@ -20,12 +20,10 @@ function CodeBlock(props: CodeBlockProps) {
|
||||||
return (await codeToHtml(local.code, {
|
return (await codeToHtml(local.code, {
|
||||||
lang: local.lang || "text",
|
lang: local.lang || "text",
|
||||||
themes: {
|
themes: {
|
||||||
light: 'github-light',
|
light: "github-light",
|
||||||
dark: 'github-dark',
|
dark: "github-dark",
|
||||||
},
|
},
|
||||||
transformers: [
|
transformers: [transformerNotationDiff()],
|
||||||
transformerNotationDiff(),
|
|
||||||
],
|
|
||||||
})) as string
|
})) as string
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -39,9 +37,7 @@ function CodeBlock(props: CodeBlockProps) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return <div ref={containerRef} {...rest}></div>
|
||||||
<div ref={containerRef} {...rest}></div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CodeBlock
|
export default CodeBlock
|
||||||
|
|
|
@ -31,11 +31,7 @@ const DiffView: Component<DiffViewProps> = (props) => {
|
||||||
diffRows.push({
|
diffRows.push({
|
||||||
left: chunk.removed ? line : chunk.added ? "" : line,
|
left: chunk.removed ? line : chunk.added ? "" : line,
|
||||||
right: chunk.added ? line : chunk.removed ? "" : line,
|
right: chunk.added ? line : chunk.removed ? "" : line,
|
||||||
type: chunk.added
|
type: chunk.added ? "added" : chunk.removed ? "removed" : "unchanged",
|
||||||
? "added"
|
|
||||||
: chunk.removed
|
|
||||||
? "removed"
|
|
||||||
: "unchanged",
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,11 +12,7 @@ import {
|
||||||
createSignal,
|
createSignal,
|
||||||
} from "solid-js"
|
} from "solid-js"
|
||||||
import { DateTime } from "luxon"
|
import { DateTime } from "luxon"
|
||||||
import {
|
import { IconOpenAI, IconGemini, IconAnthropic } from "./icons/custom"
|
||||||
IconOpenAI,
|
|
||||||
IconGemini,
|
|
||||||
IconAnthropic,
|
|
||||||
} from "./icons/custom"
|
|
||||||
import {
|
import {
|
||||||
IconCpuChip,
|
IconCpuChip,
|
||||||
IconSparkles,
|
IconSparkles,
|
||||||
|
@ -31,8 +27,12 @@ import styles from "./share.module.css"
|
||||||
import { type UIMessage } from "ai"
|
import { type UIMessage } from "ai"
|
||||||
import { createStore, reconcile } from "solid-js/store"
|
import { createStore, reconcile } from "solid-js/store"
|
||||||
|
|
||||||
type Status = "disconnected" | "connecting" | "connected" | "error" | "reconnecting"
|
type Status =
|
||||||
|
| "disconnected"
|
||||||
|
| "connecting"
|
||||||
|
| "connected"
|
||||||
|
| "error"
|
||||||
|
| "reconnecting"
|
||||||
|
|
||||||
type SessionMessage = UIMessage<{
|
type SessionMessage = UIMessage<{
|
||||||
time: {
|
time: {
|
||||||
|
@ -40,23 +40,26 @@ type SessionMessage = UIMessage<{
|
||||||
completed?: number
|
completed?: number
|
||||||
}
|
}
|
||||||
assistant?: {
|
assistant?: {
|
||||||
modelID: string;
|
modelID: string
|
||||||
providerID: string;
|
providerID: string
|
||||||
cost: number;
|
cost: number
|
||||||
tokens: {
|
tokens: {
|
||||||
input: number;
|
input: number
|
||||||
output: number;
|
output: number
|
||||||
reasoning: number;
|
reasoning: number
|
||||||
};
|
|
||||||
};
|
|
||||||
sessionID: string
|
|
||||||
tool: Record<string, {
|
|
||||||
properties: Record<string, any>
|
|
||||||
time: {
|
|
||||||
start: number
|
|
||||||
end: number
|
|
||||||
}
|
}
|
||||||
}>
|
}
|
||||||
|
sessionID: string
|
||||||
|
tool: Record<
|
||||||
|
string,
|
||||||
|
{
|
||||||
|
properties: Record<string, any>
|
||||||
|
time: {
|
||||||
|
start: number
|
||||||
|
end: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
}>
|
}>
|
||||||
|
|
||||||
type SessionInfo = {
|
type SessionInfo = {
|
||||||
|
@ -65,48 +68,47 @@ type SessionInfo = {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFileType(path: string) {
|
function getFileType(path: string) {
|
||||||
return path.split('.').pop()
|
return path.split(".").pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Converts `{a:{b:{c:1}}` to `[['a.b.c', 1]]`
|
// Converts `{a:{b:{c:1}}` to `[['a.b.c', 1]]`
|
||||||
function flattenToolArgs(obj: any, prefix: string = ""): Array<[string, any]> {
|
function flattenToolArgs(obj: any, prefix: string = ""): Array<[string, any]> {
|
||||||
const entries: Array<[string, any]> = [];
|
const entries: Array<[string, any]> = []
|
||||||
|
|
||||||
for (const [key, value] of Object.entries(obj)) {
|
for (const [key, value] of Object.entries(obj)) {
|
||||||
const path = prefix ? `${prefix}.${key}` : key;
|
const path = prefix ? `${prefix}.${key}` : key
|
||||||
|
|
||||||
if (
|
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
||||||
value !== null &&
|
entries.push(...flattenToolArgs(value, path))
|
||||||
typeof value === "object" &&
|
} else {
|
||||||
!Array.isArray(value)
|
entries.push([path, value])
|
||||||
) {
|
|
||||||
entries.push(...flattenToolArgs(value, path));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
entries.push([path, value]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return entries;
|
return entries
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStatusText(status: [Status, string?]): string {
|
function getStatusText(status: [Status, string?]): string {
|
||||||
switch (status[0]) {
|
switch (status[0]) {
|
||||||
case "connected": return "Connected"
|
case "connected":
|
||||||
case "connecting": return "Connecting..."
|
return "Connected"
|
||||||
case "disconnected": return "Disconnected"
|
case "connecting":
|
||||||
case "reconnecting": return "Reconnecting..."
|
return "Connecting..."
|
||||||
case "error": return status[1] || "Error"
|
case "disconnected":
|
||||||
default: return "Unknown"
|
return "Disconnected"
|
||||||
|
case "reconnecting":
|
||||||
|
return "Reconnecting..."
|
||||||
|
case "error":
|
||||||
|
return status[1] || "Error"
|
||||||
|
default:
|
||||||
|
return "Unknown"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function ProviderIcon(props: { provider: string, size?: number }) {
|
function ProviderIcon(props: { provider: string; size?: number }) {
|
||||||
const size = props.size || 16
|
const size = props.size || 16
|
||||||
return (
|
return (
|
||||||
<Switch fallback={
|
<Switch fallback={<IconSparkles width={size} height={size} />}>
|
||||||
<IconSparkles width={size} height={size} />
|
|
||||||
}>
|
|
||||||
<Match when={props.provider === "openai"}>
|
<Match when={props.provider === "openai"}>
|
||||||
<IconOpenAI width={size} height={size} />
|
<IconOpenAI width={size} height={size} />
|
||||||
</Match>
|
</Match>
|
||||||
|
@ -132,15 +134,11 @@ function ResultsButton(props: ResultsButtonProps) {
|
||||||
data-element-button-more
|
data-element-button-more
|
||||||
{...rest}
|
{...rest}
|
||||||
>
|
>
|
||||||
<span>
|
<span>{local.results ? "Hide results" : "Show results"}</span>
|
||||||
{local.results ? "Hide results" : "Show results"}
|
|
||||||
</span>
|
|
||||||
<span data-button-icon>
|
<span data-button-icon>
|
||||||
<Show
|
<Show
|
||||||
when={local.results}
|
when={local.results}
|
||||||
fallback={
|
fallback={<IconChevronRight width={10} height={10} />}
|
||||||
<IconChevronRight width={10} height={10} />
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<IconChevronDown width={10} height={10} />
|
<IconChevronDown width={10} height={10} />
|
||||||
</Show>
|
</Show>
|
||||||
|
@ -187,16 +185,16 @@ function TextPart(props: TextPartProps) {
|
||||||
data-expanded={expanded() || local.expand === true}
|
data-expanded={expanded() || local.expand === true}
|
||||||
{...rest}
|
{...rest}
|
||||||
>
|
>
|
||||||
<pre ref={el => (preEl = el)}>{local.text}</pre>
|
<pre ref={(el) => (preEl = el)}>{local.text}</pre>
|
||||||
{overflowed() &&
|
{overflowed() && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
data-element-button-text
|
data-element-button-text
|
||||||
onClick={() => setExpanded(e => !e)}
|
onClick={() => setExpanded((e) => !e)}
|
||||||
>
|
>
|
||||||
{expanded() ? "Show less" : "Show more"}
|
{expanded() ? "Show less" : "Show more"}
|
||||||
</button>
|
</button>
|
||||||
}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -205,13 +203,13 @@ function PartFooter(props: { time: number }) {
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
data-part-footer
|
data-part-footer
|
||||||
title={
|
title={DateTime.fromMillis(props.time).toLocaleString(
|
||||||
DateTime.fromMillis(props.time).toLocaleString(
|
DateTime.DATETIME_FULL_WITH_SECONDS,
|
||||||
DateTime.DATETIME_FULL_WITH_SECONDS
|
)}
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{DateTime.fromMillis(props.time).toLocaleString(DateTime.TIME_WITH_SECONDS)}
|
{DateTime.fromMillis(props.time).toLocaleString(
|
||||||
|
DateTime.TIME_WITH_SECONDS,
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -226,8 +224,12 @@ export default function Share(props: { api: string }) {
|
||||||
}>({
|
}>({
|
||||||
messages: {},
|
messages: {},
|
||||||
})
|
})
|
||||||
const messages = createMemo(() => Object.values(store.messages).toSorted((a, b) => a.id?.localeCompare(b.id)))
|
const messages = createMemo(() =>
|
||||||
const [connectionStatus, setConnectionStatus] = createSignal<[Status, string?]>(["disconnected", "Disconnected"])
|
Object.values(store.messages).toSorted((a, b) => a.id?.localeCompare(b.id)),
|
||||||
|
)
|
||||||
|
const [connectionStatus, setConnectionStatus] = createSignal<
|
||||||
|
[Status, string?]
|
||||||
|
>(["disconnected", "Disconnected"])
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const apiUrl = props.api
|
const apiUrl = props.api
|
||||||
|
@ -326,7 +328,10 @@ export default function Share(props: { api: string }) {
|
||||||
const result: string[][] = []
|
const result: string[][] = []
|
||||||
for (const msg of messages()) {
|
for (const msg of messages()) {
|
||||||
if (msg.role === "assistant" && msg.metadata?.assistant) {
|
if (msg.role === "assistant" && msg.metadata?.assistant) {
|
||||||
result.push([msg.metadata.assistant.providerID, msg.metadata.assistant.modelID])
|
result.push([
|
||||||
|
msg.metadata.assistant.providerID,
|
||||||
|
msg.metadata.assistant.modelID,
|
||||||
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
@ -339,7 +344,7 @@ export default function Share(props: { api: string }) {
|
||||||
input: 0,
|
input: 0,
|
||||||
output: 0,
|
output: 0,
|
||||||
reasoning: 0,
|
reasoning: 0,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
for (const msg of messages()) {
|
for (const msg of messages()) {
|
||||||
const assistant = msg.metadata?.assistant
|
const assistant = msg.metadata?.assistant
|
||||||
|
@ -366,39 +371,39 @@ export default function Share(props: { api: string }) {
|
||||||
<ul data-section="stats">
|
<ul data-section="stats">
|
||||||
<li>
|
<li>
|
||||||
<span data-element-label>Cost</span>
|
<span data-element-label>Cost</span>
|
||||||
{metrics().cost !== undefined ?
|
{metrics().cost !== undefined ? (
|
||||||
<span>${metrics().cost.toFixed(2)}</span>
|
<span>${metrics().cost.toFixed(2)}</span>
|
||||||
:
|
) : (
|
||||||
<span data-placeholder>—</span>
|
<span data-placeholder>—</span>
|
||||||
}
|
)}
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<span data-element-label>Input Tokens</span>
|
<span data-element-label>Input Tokens</span>
|
||||||
{metrics().tokens.input ?
|
{metrics().tokens.input ? (
|
||||||
<span>{metrics().tokens.input}</span>
|
<span>{metrics().tokens.input}</span>
|
||||||
:
|
) : (
|
||||||
<span data-placeholder>—</span>
|
<span data-placeholder>—</span>
|
||||||
}
|
)}
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<span data-element-label>Output Tokens</span>
|
<span data-element-label>Output Tokens</span>
|
||||||
{metrics().tokens.output ?
|
{metrics().tokens.output ? (
|
||||||
<span>{metrics().tokens.output}</span>
|
<span>{metrics().tokens.output}</span>
|
||||||
:
|
) : (
|
||||||
<span data-placeholder>—</span>
|
<span data-placeholder>—</span>
|
||||||
}
|
)}
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<span data-element-label>Reasoning Tokens</span>
|
<span data-element-label>Reasoning Tokens</span>
|
||||||
{metrics().tokens.reasoning ?
|
{metrics().tokens.reasoning ? (
|
||||||
<span>{metrics().tokens.reasoning}</span>
|
<span>{metrics().tokens.reasoning}</span>
|
||||||
:
|
) : (
|
||||||
<span data-placeholder>—</span>
|
<span data-placeholder>—</span>
|
||||||
}
|
)}
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul data-section="stats" data-section-models>
|
<ul data-section="stats" data-section-models>
|
||||||
{models().length > 0 ?
|
{models().length > 0 ? (
|
||||||
<For each={Array.from(models())}>
|
<For each={Array.from(models())}>
|
||||||
{([provider, model]) => (
|
{([provider, model]) => (
|
||||||
<li>
|
<li>
|
||||||
|
@ -409,27 +414,29 @@ export default function Share(props: { api: string }) {
|
||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
:
|
) : (
|
||||||
<li>
|
<li>
|
||||||
<span data-element-label>Models</span>
|
<span data-element-label>Models</span>
|
||||||
<span data-placeholder>—</span>
|
<span data-placeholder>—</span>
|
||||||
</li>
|
</li>
|
||||||
}
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
<div data-section="date">
|
<div data-section="date">
|
||||||
{messages().length > 0 && messages()[0].metadata?.time.created ?
|
{messages().length > 0 && messages()[0].metadata?.time.created ? (
|
||||||
<span title={
|
<span
|
||||||
DateTime.fromMillis(
|
title={DateTime.fromMillis(
|
||||||
messages()[0].metadata?.time.created || 0
|
messages()[0].metadata?.time.created || 0,
|
||||||
).toLocaleString(DateTime.DATETIME_FULL_WITH_SECONDS)
|
).toLocaleString(DateTime.DATETIME_FULL_WITH_SECONDS)}
|
||||||
}>
|
>
|
||||||
{DateTime.fromMillis(
|
{DateTime.fromMillis(
|
||||||
messages()[0].metadata?.time.created || 0
|
messages()[0].metadata?.time.created || 0,
|
||||||
).toLocaleString(DateTime.DATE_MED)}
|
).toLocaleString(DateTime.DATE_MED)}
|
||||||
</span>
|
</span>
|
||||||
:
|
) : (
|
||||||
<span data-element-label data-placeholder>Started at —</span>
|
<span data-element-label data-placeholder>
|
||||||
}
|
Started at —
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -444,27 +451,32 @@ export default function Share(props: { api: string }) {
|
||||||
{(msg, msgIndex) => (
|
{(msg, msgIndex) => (
|
||||||
<For each={msg.parts}>
|
<For each={msg.parts}>
|
||||||
{(part, partIndex) => {
|
{(part, partIndex) => {
|
||||||
if (part.type === "step-start" && (partIndex() > 0 || !msg.metadata?.assistant)) return null
|
if (
|
||||||
|
part.type === "step-start" &&
|
||||||
|
(partIndex() > 0 || !msg.metadata?.assistant)
|
||||||
|
)
|
||||||
|
return null
|
||||||
|
|
||||||
const [results, showResults] = createSignal(false)
|
const [results, showResults] = createSignal(false)
|
||||||
const isLastPart = createMemo(() =>
|
const isLastPart = createMemo(
|
||||||
(messages().length === msgIndex() + 1)
|
() =>
|
||||||
&& (msg.parts.length === partIndex() + 1)
|
messages().length === msgIndex() + 1 &&
|
||||||
|
msg.parts.length === partIndex() + 1,
|
||||||
)
|
)
|
||||||
const time = msg.metadata?.time.completed
|
const time =
|
||||||
|| msg.metadata?.time.created
|
msg.metadata?.time.completed ||
|
||||||
|| 0
|
msg.metadata?.time.created ||
|
||||||
|
0
|
||||||
return (
|
return (
|
||||||
<Switch>
|
<Switch>
|
||||||
{ /* User text */}
|
{/* User text */}
|
||||||
<Match when={
|
<Match
|
||||||
msg.role === "user" && part.type === "text" && part
|
when={
|
||||||
}>
|
msg.role === "user" && part.type === "text" && part
|
||||||
{part =>
|
}
|
||||||
<div
|
>
|
||||||
data-section="part"
|
{(part) => (
|
||||||
data-part-type="user-text"
|
<div data-section="part" data-part-type="user-text">
|
||||||
>
|
|
||||||
<div data-section="decoration">
|
<div data-section="decoration">
|
||||||
<div>
|
<div>
|
||||||
<IconUserCircle width={18} height={18} />
|
<IconUserCircle width={18} height={18} />
|
||||||
|
@ -480,21 +492,22 @@ export default function Share(props: { api: string }) {
|
||||||
<PartFooter time={time} />
|
<PartFooter time={time} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
)}
|
||||||
</Match>
|
</Match>
|
||||||
{ /* AI text */}
|
{/* AI text */}
|
||||||
<Match when={
|
<Match
|
||||||
msg.role === "assistant"
|
when={
|
||||||
&& part.type === "text"
|
msg.role === "assistant" &&
|
||||||
&& part
|
part.type === "text" &&
|
||||||
}>
|
part
|
||||||
{part =>
|
}
|
||||||
<div
|
>
|
||||||
data-section="part"
|
{(part) => (
|
||||||
data-part-type="ai-text"
|
<div data-section="part" data-part-type="ai-text">
|
||||||
>
|
|
||||||
<div data-section="decoration">
|
<div data-section="decoration">
|
||||||
<div><IconSparkles width={18} height={18} /></div>
|
<div>
|
||||||
|
<IconSparkles width={18} height={18} />
|
||||||
|
</div>
|
||||||
<div></div>
|
<div></div>
|
||||||
</div>
|
</div>
|
||||||
<div data-section="content">
|
<div data-section="content">
|
||||||
|
@ -505,19 +518,18 @@ export default function Share(props: { api: string }) {
|
||||||
<PartFooter time={time} />
|
<PartFooter time={time} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
)}
|
||||||
</Match>
|
</Match>
|
||||||
{ /* AI model */}
|
{/* AI model */}
|
||||||
<Match when={
|
<Match
|
||||||
msg.role === "assistant"
|
when={
|
||||||
&& part.type === "step-start"
|
msg.role === "assistant" &&
|
||||||
&& msg.metadata?.assistant
|
part.type === "step-start" &&
|
||||||
}>
|
msg.metadata?.assistant
|
||||||
{assistant =>
|
}
|
||||||
<div
|
>
|
||||||
data-section="part"
|
{(assistant) => (
|
||||||
data-part-type="ai-model"
|
<div data-section="part" data-part-type="ai-model">
|
||||||
>
|
|
||||||
<div data-section="decoration">
|
<div data-section="decoration">
|
||||||
<div>
|
<div>
|
||||||
<ProviderIcon
|
<ProviderIcon
|
||||||
|
@ -542,15 +554,17 @@ export default function Share(props: { api: string }) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
)}
|
||||||
</Match>
|
</Match>
|
||||||
{ /* System text */}
|
{/* System text */}
|
||||||
<Match when={
|
<Match
|
||||||
msg.role === "system"
|
when={
|
||||||
&& part.type === "text"
|
msg.role === "system" &&
|
||||||
&& part
|
part.type === "text" &&
|
||||||
}>
|
part
|
||||||
{part =>
|
}
|
||||||
|
>
|
||||||
|
{(part) => (
|
||||||
<div
|
<div
|
||||||
data-section="part"
|
data-section="part"
|
||||||
data-part-type="system-text"
|
data-part-type="system-text"
|
||||||
|
@ -575,16 +589,18 @@ export default function Share(props: { api: string }) {
|
||||||
<PartFooter time={time} />
|
<PartFooter time={time} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
)}
|
||||||
</Match>
|
</Match>
|
||||||
{ /* Edit tool */}
|
{/* Edit tool */}
|
||||||
<Match when={
|
<Match
|
||||||
msg.role === "assistant"
|
when={
|
||||||
&& part.type === "tool-invocation"
|
msg.role === "assistant" &&
|
||||||
&& part.toolInvocation.toolName === "edit"
|
part.type === "tool-invocation" &&
|
||||||
&& part
|
part.toolInvocation.toolName === "edit" &&
|
||||||
}>
|
part
|
||||||
{part => {
|
}
|
||||||
|
>
|
||||||
|
{(part) => {
|
||||||
const args = part().toolInvocation.args
|
const args = part().toolInvocation.args
|
||||||
const filePath = args.filePath
|
const filePath = args.filePath
|
||||||
return (
|
return (
|
||||||
|
@ -618,20 +634,25 @@ export default function Share(props: { api: string }) {
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
</Match>
|
</Match>
|
||||||
{ /* Tool call */}
|
{/* Tool call */}
|
||||||
<Match when={
|
<Match
|
||||||
msg.role === "assistant"
|
when={
|
||||||
&& part.type === "tool-invocation"
|
msg.role === "assistant" &&
|
||||||
&& part
|
part.type === "tool-invocation" &&
|
||||||
}>
|
part
|
||||||
{part =>
|
}
|
||||||
|
>
|
||||||
|
{(part) => (
|
||||||
<div
|
<div
|
||||||
data-section="part"
|
data-section="part"
|
||||||
data-part-type="tool-fallback"
|
data-part-type="tool-fallback"
|
||||||
>
|
>
|
||||||
<div data-section="decoration">
|
<div data-section="decoration">
|
||||||
<div>
|
<div>
|
||||||
<IconWrenchScrewdriver width={18} height={18} />
|
<IconWrenchScrewdriver
|
||||||
|
width={18}
|
||||||
|
height={18}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div></div>
|
<div></div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -641,27 +662,32 @@ export default function Share(props: { api: string }) {
|
||||||
{part().toolInvocation.toolName}
|
{part().toolInvocation.toolName}
|
||||||
</span>
|
</span>
|
||||||
<div data-part-tool-args>
|
<div data-part-tool-args>
|
||||||
<For each={
|
<For
|
||||||
flattenToolArgs(part().toolInvocation.args)
|
each={flattenToolArgs(
|
||||||
}>
|
part().toolInvocation.args,
|
||||||
{([name, value]) =>
|
)}
|
||||||
|
>
|
||||||
|
{([name, value]) => (
|
||||||
<>
|
<>
|
||||||
<div></div>
|
<div></div>
|
||||||
<div>{name}</div>
|
<div>{name}</div>
|
||||||
<div>{value}</div>
|
<div>{value}</div>
|
||||||
</>
|
</>
|
||||||
}
|
)}
|
||||||
</For>
|
</For>
|
||||||
</div>
|
</div>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Match when={
|
<Match
|
||||||
part().toolInvocation.state === "result"
|
when={
|
||||||
&& part().toolInvocation.result
|
part().toolInvocation.state ===
|
||||||
}>
|
"result" &&
|
||||||
|
part().toolInvocation.result
|
||||||
|
}
|
||||||
|
>
|
||||||
<div data-part-tool-result>
|
<div data-part-tool-result>
|
||||||
<ResultsButton
|
<ResultsButton
|
||||||
results={results()}
|
results={results()}
|
||||||
onClick={() => showResults(e => !e)}
|
onClick={() => showResults((e) => !e)}
|
||||||
/>
|
/>
|
||||||
<Show when={results()}>
|
<Show when={results()}>
|
||||||
<TextPart
|
<TextPart
|
||||||
|
@ -673,9 +699,11 @@ export default function Share(props: { api: string }) {
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={
|
<Match
|
||||||
part().toolInvocation.state === "call"
|
when={
|
||||||
}>
|
part().toolInvocation.state === "call"
|
||||||
|
}
|
||||||
|
>
|
||||||
<TextPart
|
<TextPart
|
||||||
data-size="sm"
|
data-size="sm"
|
||||||
data-color="dimmed"
|
data-color="dimmed"
|
||||||
|
@ -687,20 +715,27 @@ export default function Share(props: { api: string }) {
|
||||||
<PartFooter time={time} />
|
<PartFooter time={time} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
)}
|
||||||
</Match>
|
</Match>
|
||||||
{ /* Fallback */}
|
{/* Fallback */}
|
||||||
<Match when={true}>
|
<Match when={true}>
|
||||||
<div
|
<div data-section="part" data-part-type="fallback">
|
||||||
data-section="part"
|
|
||||||
data-part-type="fallback"
|
|
||||||
>
|
|
||||||
<div data-section="decoration">
|
<div data-section="decoration">
|
||||||
<div>
|
<div>
|
||||||
<Switch fallback={
|
<Switch
|
||||||
<IconWrenchScrewdriver width={16} height={16} />
|
fallback={
|
||||||
}>
|
<IconWrenchScrewdriver
|
||||||
<Match when={msg.role === "assistant" && part.type !== "tool-invocation"}>
|
width={16}
|
||||||
|
height={16}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Match
|
||||||
|
when={
|
||||||
|
msg.role === "assistant" &&
|
||||||
|
part.type !== "tool-invocation"
|
||||||
|
}
|
||||||
|
>
|
||||||
<IconSparkles width={18} height={18} />
|
<IconSparkles width={18} height={18} />
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={msg.role === "system"}>
|
<Match when={msg.role === "system"}>
|
||||||
|
@ -718,7 +753,9 @@ export default function Share(props: { api: string }) {
|
||||||
<span data-element-label data-part-title>
|
<span data-element-label data-part-title>
|
||||||
{part.type}
|
{part.type}
|
||||||
</span>
|
</span>
|
||||||
<TextPart text={JSON.stringify(part, null, 2)} />
|
<TextPart
|
||||||
|
text={JSON.stringify(part, null, 2)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<PartFooter time={time} />
|
<PartFooter time={time} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -767,6 +804,6 @@ export default function Share(props: { api: string }) {
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main >
|
</main>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.column {
|
.column {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
|
|
@ -3,20 +3,35 @@ import { type JSX } from "solid-js"
|
||||||
// https://icones.js.org/collection/ri?s=openai&icon=ri:openai-fill
|
// https://icones.js.org/collection/ri?s=openai&icon=ri:openai-fill
|
||||||
export function IconOpenAI(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
export function IconOpenAI(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
return (
|
return (
|
||||||
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M20.562 10.188c.25-.688.313-1.376.25-2.063c-.062-.687-.312-1.375-.625-2c-.562-.937-1.375-1.687-2.312-2.125c-1-.437-2.063-.562-3.125-.312c-.5-.5-1.063-.938-1.688-1.25S11.687 2 11 2a5.17 5.17 0 0 0-3 .938c-.875.624-1.5 1.5-1.813 2.5c-.75.187-1.375.5-2 .875c-.562.437-1 1-1.375 1.562c-.562.938-.75 2-.625 3.063a5.44 5.44 0 0 0 1.25 2.874a4.7 4.7 0 0 0-.25 2.063c.063.688.313 1.375.625 2c.563.938 1.375 1.688 2.313 2.125c1 .438 2.062.563 3.125.313c.5.5 1.062.937 1.687 1.25S12.312 22 13 22a5.17 5.17 0 0 0 3-.937c.875-.625 1.5-1.5 1.812-2.5a4.54 4.54 0 0 0 1.938-.875c.562-.438 1.062-.938 1.375-1.563c.562-.937.75-2 .625-3.062c-.125-1.063-.5-2.063-1.188-2.876m-7.5 10.5c-1 0-1.75-.313-2.437-.875c0 0 .062-.063.125-.063l4-2.312a.5.5 0 0 0 .25-.25a.57.57 0 0 0 .062-.313V11.25l1.688 1v4.625a3.685 3.685 0 0 1-3.688 3.813M5 17.25c-.438-.75-.625-1.625-.438-2.5c0 0 .063.063.125.063l4 2.312a.56.56 0 0 0 .313.063c.125 0 .25 0 .312-.063l4.875-2.812v1.937l-4.062 2.375A3.7 3.7 0 0 1 7.312 19c-1-.25-1.812-.875-2.312-1.75M3.937 8.563a3.8 3.8 0 0 1 1.938-1.626v4.751c0 .124 0 .25.062.312a.5.5 0 0 0 .25.25l4.875 2.813l-1.687 1l-4-2.313a3.7 3.7 0 0 1-1.75-2.25c-.25-.937-.188-2.062.312-2.937M17.75 11.75l-4.875-2.812l1.687-1l4 2.312c.625.375 1.125.875 1.438 1.5s.5 1.313.437 2.063a3.7 3.7 0 0 1-.75 1.937c-.437.563-1 1-1.687 1.25v-4.75c0-.125 0-.25-.063-.312c0 0-.062-.126-.187-.188m1.687-2.5s-.062-.062-.125-.062l-4-2.313c-.125-.062-.187-.062-.312-.062s-.25 0-.313.062L9.812 9.688V7.75l4.063-2.375c.625-.375 1.312-.5 2.062-.5c.688 0 1.375.25 2 .688c.563.437 1.063 1 1.313 1.625s.312 1.375.187 2.062m-10.5 3.5l-1.687-1V7.063c0-.688.187-1.438.562-2C8.187 4.438 8.75 4 9.375 3.688a3.37 3.37 0 0 1 2.062-.313c.688.063 1.375.375 1.938.813c0 0-.063.062-.125.062l-4 2.313a.5.5 0 0 0-.25.25c-.063.125-.063.187-.063.312zm.875-2L12 9.5l2.187 1.25v2.5L12 14.5l-2.188-1.25z" /></svg>
|
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M20.562 10.188c.25-.688.313-1.376.25-2.063c-.062-.687-.312-1.375-.625-2c-.562-.937-1.375-1.687-2.312-2.125c-1-.437-2.063-.562-3.125-.312c-.5-.5-1.063-.938-1.688-1.25S11.687 2 11 2a5.17 5.17 0 0 0-3 .938c-.875.624-1.5 1.5-1.813 2.5c-.75.187-1.375.5-2 .875c-.562.437-1 1-1.375 1.562c-.562.938-.75 2-.625 3.063a5.44 5.44 0 0 0 1.25 2.874a4.7 4.7 0 0 0-.25 2.063c.063.688.313 1.375.625 2c.563.938 1.375 1.688 2.313 2.125c1 .438 2.062.563 3.125.313c.5.5 1.062.937 1.687 1.25S12.312 22 13 22a5.17 5.17 0 0 0 3-.937c.875-.625 1.5-1.5 1.812-2.5a4.54 4.54 0 0 0 1.938-.875c.562-.438 1.062-.938 1.375-1.563c.562-.937.75-2 .625-3.062c-.125-1.063-.5-2.063-1.188-2.876m-7.5 10.5c-1 0-1.75-.313-2.437-.875c0 0 .062-.063.125-.063l4-2.312a.5.5 0 0 0 .25-.25a.57.57 0 0 0 .062-.313V11.25l1.688 1v4.625a3.685 3.685 0 0 1-3.688 3.813M5 17.25c-.438-.75-.625-1.625-.438-2.5c0 0 .063.063.125.063l4 2.312a.56.56 0 0 0 .313.063c.125 0 .25 0 .312-.063l4.875-2.812v1.937l-4.062 2.375A3.7 3.7 0 0 1 7.312 19c-1-.25-1.812-.875-2.312-1.75M3.937 8.563a3.8 3.8 0 0 1 1.938-1.626v4.751c0 .124 0 .25.062.312a.5.5 0 0 0 .25.25l4.875 2.813l-1.687 1l-4-2.313a3.7 3.7 0 0 1-1.75-2.25c-.25-.937-.188-2.062.312-2.937M17.75 11.75l-4.875-2.812l1.687-1l4 2.312c.625.375 1.125.875 1.438 1.5s.5 1.313.437 2.063a3.7 3.7 0 0 1-.75 1.937c-.437.563-1 1-1.687 1.25v-4.75c0-.125 0-.25-.063-.312c0 0-.062-.126-.187-.188m1.687-2.5s-.062-.062-.125-.062l-4-2.313c-.125-.062-.187-.062-.312-.062s-.25 0-.313.062L9.812 9.688V7.75l4.063-2.375c.625-.375 1.312-.5 2.062-.5c.688 0 1.375.25 2 .688c.563.437 1.063 1 1.313 1.625s.312 1.375.187 2.062m-10.5 3.5l-1.687-1V7.063c0-.688.187-1.438.562-2C8.187 4.438 8.75 4 9.375 3.688a3.37 3.37 0 0 1 2.062-.313c.688.063 1.375.375 1.938.813c0 0-.063.062-.125.062l-4 2.313a.5.5 0 0 0-.25.25c-.063.125-.063.187-.063.312zm.875-2L12 9.5l2.187 1.25v2.5L12 14.5l-2.188-1.25z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://icones.js.org/collection/ri?s=anthropic&icon=ri:anthropic-fill
|
// https://icones.js.org/collection/ri?s=anthropic&icon=ri:anthropic-fill
|
||||||
export function IconAnthropic(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
export function IconAnthropic(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
return (
|
return (
|
||||||
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M16.765 5h-3.308l5.923 15h3.23zM7.226 5L1.38 20h3.308l1.307-3.154h6.154l1.23 3.077h3.309L10.688 5zm-.308 9.077l2-5.308l2.077 5.308z" /></svg>
|
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M16.765 5h-3.308l5.923 15h3.23zM7.226 5L1.38 20h3.308l1.307-3.154h6.154l1.23 3.077h3.309L10.688 5zm-.308 9.077l2-5.308l2.077 5.308z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://icones.js.org/collection/ri?s=gemini&icon=ri:gemini-fill
|
// https://icones.js.org/collection/ri?s=gemini&icon=ri:gemini-fill
|
||||||
export function IconGemini(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
export function IconGemini(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
return (
|
return (
|
||||||
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M24 12.024c-6.437.388-11.59 5.539-11.977 11.976h-.047C11.588 17.563 6.436 12.412 0 12.024v-.047C6.437 11.588 11.588 6.437 11.976 0h.047c.388 6.437 5.54 11.588 11.977 11.977z" /></svg>
|
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M24 12.024c-6.437.388-11.59 5.539-11.977 11.976h-.047C11.588 17.563 6.436 12.412 0 12.024v-.047C6.437 11.588 11.588 6.437 11.976 0h.047c.388 6.437 5.54 11.588 11.977 11.977z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ export function IconAcademicCap(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconAdjustmentsHorizontal(
|
export function IconAdjustmentsHorizontal(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -42,7 +42,7 @@ export function IconAdjustmentsHorizontal(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconAdjustmentsVertical(
|
export function IconAdjustmentsVertical(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -63,7 +63,7 @@ export function IconAdjustmentsVertical(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconArchiveBoxArrowDown(
|
export function IconArchiveBoxArrowDown(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -84,7 +84,7 @@ export function IconArchiveBoxArrowDown(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconArchiveBoxXMark(
|
export function IconArchiveBoxXMark(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -124,7 +124,7 @@ export function IconArchiveBox(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconArrowDownCircle(
|
export function IconArrowDownCircle(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -164,7 +164,7 @@ export function IconArrowDownLeft(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconArrowDownOnSquareStack(
|
export function IconArrowDownOnSquareStack(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -185,7 +185,7 @@ export function IconArrowDownOnSquareStack(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconArrowDownOnSquare(
|
export function IconArrowDownOnSquare(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -263,7 +263,7 @@ export function IconArrowDown(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconArrowLeftCircle(
|
export function IconArrowLeftCircle(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -284,7 +284,7 @@ export function IconArrowLeftCircle(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconArrowLeftOnRectangle(
|
export function IconArrowLeftOnRectangle(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -400,7 +400,7 @@ export function IconArrowLongUp(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconArrowPathRoundedSquare(
|
export function IconArrowPathRoundedSquare(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -440,7 +440,7 @@ export function IconArrowPath(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconArrowRightCircle(
|
export function IconArrowRightCircle(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -461,7 +461,7 @@ export function IconArrowRightCircle(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconArrowRightOnRectangle(
|
export function IconArrowRightOnRectangle(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -539,7 +539,7 @@ export function IconArrowSmallLeft(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconArrowSmallRight(
|
export function IconArrowSmallRight(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -579,7 +579,7 @@ export function IconArrowSmallUp(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconArrowTopRightOnSquare(
|
export function IconArrowTopRightOnSquare(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -600,7 +600,7 @@ export function IconArrowTopRightOnSquare(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconArrowTrendingDown(
|
export function IconArrowTrendingDown(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -621,7 +621,7 @@ export function IconArrowTrendingDown(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconArrowTrendingUp(
|
export function IconArrowTrendingUp(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -680,7 +680,7 @@ export function IconArrowUpLeft(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconArrowUpOnSquareStack(
|
export function IconArrowUpOnSquareStack(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -701,7 +701,7 @@ export function IconArrowUpOnSquareStack(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconArrowUpOnSquare(
|
export function IconArrowUpOnSquare(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -817,7 +817,7 @@ export function IconArrowUturnLeft(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconArrowUturnRight(
|
export function IconArrowUturnRight(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -857,7 +857,7 @@ export function IconArrowUturnUp(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconArrowsPointingIn(
|
export function IconArrowsPointingIn(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -878,7 +878,7 @@ export function IconArrowsPointingIn(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconArrowsPointingOut(
|
export function IconArrowsPointingOut(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -899,7 +899,7 @@ export function IconArrowsPointingOut(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconArrowsRightLeft(
|
export function IconArrowsRightLeft(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -1040,7 +1040,7 @@ export function IconBars2(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconBars3BottomLeft(
|
export function IconBars3BottomLeft(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -1061,7 +1061,7 @@ export function IconBars3BottomLeft(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconBars3BottomRight(
|
export function IconBars3BottomRight(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -1082,7 +1082,7 @@ export function IconBars3BottomRight(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconBars3CenterLeft(
|
export function IconBars3CenterLeft(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -1507,7 +1507,7 @@ export function IconBugAnt(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconBuildingLibrary(
|
export function IconBuildingLibrary(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -1528,7 +1528,7 @@ export function IconBuildingLibrary(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconBuildingOffice2(
|
export function IconBuildingOffice2(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -1568,7 +1568,7 @@ export function IconBuildingOffice(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconBuildingStorefront(
|
export function IconBuildingStorefront(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -1776,7 +1776,7 @@ export function IconChartPie(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconChatBubbleBottomCenterText(
|
export function IconChatBubbleBottomCenterText(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -1797,7 +1797,7 @@ export function IconChatBubbleBottomCenterText(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconChatBubbleBottomCenter(
|
export function IconChatBubbleBottomCenter(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -1818,7 +1818,7 @@ export function IconChatBubbleBottomCenter(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconChatBubbleLeftEllipsis(
|
export function IconChatBubbleLeftEllipsis(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -1839,7 +1839,7 @@ export function IconChatBubbleLeftEllipsis(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconChatBubbleLeftRight(
|
export function IconChatBubbleLeftRight(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -1879,7 +1879,7 @@ export function IconChatBubbleLeft(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconChatBubbleOvalLeftEllipsis(
|
export function IconChatBubbleOvalLeftEllipsis(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -1900,7 +1900,7 @@ export function IconChatBubbleOvalLeftEllipsis(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconChatBubbleOvalLeft(
|
export function IconChatBubbleOvalLeft(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -1978,7 +1978,7 @@ export function IconCheck(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconChevronDoubleDown(
|
export function IconChevronDoubleDown(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -1999,7 +1999,7 @@ export function IconChevronDoubleDown(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconChevronDoubleLeft(
|
export function IconChevronDoubleLeft(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -2020,7 +2020,7 @@ export function IconChevronDoubleLeft(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconChevronDoubleRight(
|
export function IconChevronDoubleRight(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -2041,7 +2041,7 @@ export function IconChevronDoubleRight(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconChevronDoubleUp(
|
export function IconChevronDoubleUp(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -2176,7 +2176,7 @@ export function IconCircleStack(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconClipboardDocumentCheck(
|
export function IconClipboardDocumentCheck(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -2197,7 +2197,7 @@ export function IconClipboardDocumentCheck(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconClipboardDocumentList(
|
export function IconClipboardDocumentList(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -2218,7 +2218,7 @@ export function IconClipboardDocumentList(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconClipboardDocument(
|
export function IconClipboardDocument(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -2334,7 +2334,7 @@ export function IconCloud(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconCodeBracketSquare(
|
export function IconCodeBracketSquare(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -2464,7 +2464,7 @@ export function IconCommandLine(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconComputerDesktop(
|
export function IconComputerDesktop(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -2523,7 +2523,7 @@ export function IconCreditCard(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconCubeTransparent(
|
export function IconCubeTransparent(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -2563,7 +2563,7 @@ export function IconCube(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconCurrencyBangladeshi(
|
export function IconCurrencyBangladeshi(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -2679,7 +2679,7 @@ export function IconCurrencyYen(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconCursorArrowRays(
|
export function IconCursorArrowRays(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -2700,7 +2700,7 @@ export function IconCursorArrowRays(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconCursorArrowRipple(
|
export function IconCursorArrowRipple(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -2721,7 +2721,7 @@ export function IconCursorArrowRipple(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconDevicePhoneMobile(
|
export function IconDevicePhoneMobile(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -2761,7 +2761,7 @@ export function IconDeviceTablet(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconDocumentArrowDown(
|
export function IconDocumentArrowDown(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -2782,7 +2782,7 @@ export function IconDocumentArrowDown(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconDocumentArrowUp(
|
export function IconDocumentArrowUp(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -2803,7 +2803,7 @@ export function IconDocumentArrowUp(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconDocumentChartBar(
|
export function IconDocumentChartBar(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -2843,7 +2843,7 @@ export function IconDocumentCheck(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconDocumentDuplicate(
|
export function IconDocumentDuplicate(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -2864,7 +2864,7 @@ export function IconDocumentDuplicate(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconDocumentMagnifyingGlass(
|
export function IconDocumentMagnifyingGlass(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -2961,7 +2961,7 @@ export function IconDocument(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconEllipsisHorizontalCircle(
|
export function IconEllipsisHorizontalCircle(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -2982,7 +2982,7 @@ export function IconEllipsisHorizontalCircle(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconEllipsisHorizontal(
|
export function IconEllipsisHorizontal(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -3017,7 +3017,7 @@ export function IconEllipsisHorizontal(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconEllipsisVertical(
|
export function IconEllipsisVertical(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -3103,7 +3103,7 @@ export function IconEnvelopeSolid(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconExclamationCircle(
|
export function IconExclamationCircle(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -3124,7 +3124,7 @@ export function IconExclamationCircle(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconExclamationTriangle(
|
export function IconExclamationTriangle(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -3330,7 +3330,7 @@ export function IconFlag(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconFolderArrowDown(
|
export function IconFolderArrowDown(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -3567,7 +3567,7 @@ export function IconGlobeAmericas(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconGlobeAsiaAustralia(
|
export function IconGlobeAsiaAustralia(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -3588,7 +3588,7 @@ export function IconGlobeAsiaAustralia(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconGlobeEuropeAfrica(
|
export function IconGlobeEuropeAfrica(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -3818,7 +3818,7 @@ export function IconInbox(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconInformationCircle(
|
export function IconInformationCircle(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -3991,7 +3991,7 @@ export function IconLockOpen(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconMagnifyingGlassCircle(
|
export function IconMagnifyingGlassCircle(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -4012,7 +4012,7 @@ export function IconMagnifyingGlassCircle(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconMagnifyingGlassMinus(
|
export function IconMagnifyingGlassMinus(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -4033,7 +4033,7 @@ export function IconMagnifyingGlassMinus(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconMagnifyingGlassPlus(
|
export function IconMagnifyingGlassPlus(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -4054,7 +4054,7 @@ export function IconMagnifyingGlassPlus(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconMagnifyingGlass(
|
export function IconMagnifyingGlass(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -4424,7 +4424,7 @@ export function IconPencil(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconPhoneArrowDownLeft(
|
export function IconPhoneArrowDownLeft(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -4445,7 +4445,7 @@ export function IconPhoneArrowDownLeft(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconPhoneArrowUpRight(
|
export function IconPhoneArrowUpRight(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -4663,7 +4663,7 @@ export function IconPower(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconPresentationChartBar(
|
export function IconPresentationChartBar(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -4684,7 +4684,7 @@ export function IconPresentationChartBar(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconPresentationChartLine(
|
export function IconPresentationChartLine(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -4832,7 +4832,7 @@ export function IconQrCode(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconQuestionMarkCircle(
|
export function IconQuestionMarkCircle(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -5133,7 +5133,7 @@ export function IconShieldCheck(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconShieldExclamation(
|
export function IconShieldExclamation(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -5780,7 +5780,7 @@ export function IconVariable(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconVideoCameraSlash(
|
export function IconVideoCameraSlash(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -5838,7 +5838,7 @@ export function IconViewColumns(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconViewfinderCircle(
|
export function IconViewfinderCircle(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -5916,7 +5916,7 @@ export function IconWindow(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export function IconWrenchScrewdriver(
|
export function IconWrenchScrewdriver(
|
||||||
props: JSX.SvgSVGAttributes<SVGSVGElement>
|
props: JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
|
|
@ -84,11 +84,21 @@
|
||||||
span:first-child {
|
span:first-child {
|
||||||
color: var(--sl-color-divider);
|
color: var(--sl-color-divider);
|
||||||
|
|
||||||
&[data-status="connected"] { color: var(--sl-color-green); }
|
&[data-status="connected"] {
|
||||||
&[data-status="connecting"] { color: var(--sl-color-orange); }
|
color: var(--sl-color-green);
|
||||||
&[data-status="disconnected"] { color: var(--sl-color-divider); }
|
}
|
||||||
&[data-status="reconnecting"] { color: var(--sl-color-orange); }
|
&[data-status="connecting"] {
|
||||||
&[data-status="error"] { color: var(--sl-color-red); }
|
color: var(--sl-color-orange);
|
||||||
|
}
|
||||||
|
&[data-status="disconnected"] {
|
||||||
|
color: var(--sl-color-divider);
|
||||||
|
}
|
||||||
|
&[data-status="reconnecting"] {
|
||||||
|
color: var(--sl-color-orange);
|
||||||
|
}
|
||||||
|
&[data-status="error"] {
|
||||||
|
color: var(--sl-color-red);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +116,7 @@
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
|
|
||||||
span[data-placeholder] {
|
span[data-placeholder] {
|
||||||
color: var(--sl-color-text-dimmed);
|
color: var(--sl-color-text-dimmed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -215,16 +225,15 @@
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
gap: 0.25rem 0.375rem;
|
gap: 0.25rem 0.375rem;
|
||||||
|
|
||||||
|
& > div:nth-child(3n + 1) {
|
||||||
& > div:nth-child(3n+1) {
|
|
||||||
width: 8px;
|
width: 8px;
|
||||||
height: 2px;
|
height: 2px;
|
||||||
border-radius: 1px;
|
border-radius: 1px;
|
||||||
background: var(--sl-color-divider);
|
background: var(--sl-color-divider);
|
||||||
}
|
}
|
||||||
|
|
||||||
& > div:nth-child(3n+2),
|
& > div:nth-child(3n + 2),
|
||||||
& > div:nth-child(3n+3) {
|
& > div:nth-child(3n + 3) {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
@ -232,7 +241,7 @@
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
& > div:nth-child(3n+3) {
|
& > div:nth-child(3n + 3) {
|
||||||
padding-left: 0.125rem;
|
padding-left: 0.125rem;
|
||||||
color: var(--sl-color-text-dimmed);
|
color: var(--sl-color-text-dimmed);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { defineCollection } from 'astro:content';
|
import { defineCollection } from "astro:content"
|
||||||
import { docsLoader } from '@astrojs/starlight/loaders';
|
import { docsLoader } from "@astrojs/starlight/loaders"
|
||||||
import { docsSchema } from '@astrojs/starlight/schema';
|
import { docsSchema } from "@astrojs/starlight/schema"
|
||||||
|
|
||||||
export const collections = {
|
export const collections = {
|
||||||
docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }),
|
docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }),
|
||||||
};
|
}
|
||||||
|
|
|
@ -20,21 +20,21 @@ Or start with a specific working directory.
|
||||||
opencode -c /path/to/project
|
opencode -c /path/to/project
|
||||||
```
|
```
|
||||||
|
|
||||||
## Flags
|
## Flags
|
||||||
|
|
||||||
The OpenCode CLI takes the following flags.
|
The OpenCode CLI takes the following flags.
|
||||||
|
|
||||||
| Flag | Short | Description |
|
| Flag | Short | Description |
|
||||||
| -- | -- | -- |
|
| ----------------- | ----- | -------------------------------------------------------- |
|
||||||
| `--help` | `-h` | Display help |
|
| `--help` | `-h` | Display help |
|
||||||
| `--debug` | `-d` | Enable debug mode |
|
| `--debug` | `-d` | Enable debug mode |
|
||||||
| `--cwd` | `-c` | Set current working directory |
|
| `--cwd` | `-c` | Set current working directory |
|
||||||
| `--prompt` | `-p` | Run a single prompt in non-interactive mode |
|
| `--prompt` | `-p` | Run a single prompt in non-interactive mode |
|
||||||
| `--output-format` | `-f` | Output format for non-interactive mode, `text` or `json` |
|
| `--output-format` | `-f` | Output format for non-interactive mode, `text` or `json` |
|
||||||
| `--quiet` | `-q` | Hide spinner in non-interactive mode |
|
| `--quiet` | `-q` | Hide spinner in non-interactive mode |
|
||||||
| `--verbose` | | Display logs to stderr in non-interactive mode |
|
| `--verbose` | | Display logs to stderr in non-interactive mode |
|
||||||
| `--allowedTools` | | Restrict the agent to only use specified tools |
|
| `--allowedTools` | | Restrict the agent to only use specified tools |
|
||||||
| `--excludedTools` | | Prevent the agent from using specified tools |
|
| `--excludedTools` | | Prevent the agent from using specified tools |
|
||||||
|
|
||||||
## Non-interactive
|
## Non-interactive
|
||||||
|
|
||||||
|
|
|
@ -73,16 +73,15 @@ The config file has the following structure.
|
||||||
|
|
||||||
For the providers, you can also specify the keys using environment variables.
|
For the providers, you can also specify the keys using environment variables.
|
||||||
|
|
||||||
| Environment Variable | Models |
|
| Environment Variable | Models |
|
||||||
| -------------------------- | ----------- |
|
| -------------------------- | ------------------------------------------ |
|
||||||
| `ANTHROPIC_API_KEY` | Claude |
|
| `ANTHROPIC_API_KEY` | Claude |
|
||||||
| `OPENAI_API_KEY` | OpenAI |
|
| `OPENAI_API_KEY` | OpenAI |
|
||||||
| `GEMINI_API_KEY` | Google Gemini |
|
| `GEMINI_API_KEY` | Google Gemini |
|
||||||
| `GROQ_API_KEY` | Groq |
|
| `GROQ_API_KEY` | Groq |
|
||||||
| `AWS_ACCESS_KEY_ID` | Amazon Bedrock |
|
| `AWS_ACCESS_KEY_ID` | Amazon Bedrock |
|
||||||
| `AWS_SECRET_ACCESS_KEY` | Amazon Bedrock |
|
| `AWS_SECRET_ACCESS_KEY` | Amazon Bedrock |
|
||||||
| `AWS_REGION` | Amazon Bedrock |
|
| `AWS_REGION` | Amazon Bedrock |
|
||||||
| `AZURE_OPENAI_ENDPOINT` | Azure OpenAI |
|
| `AZURE_OPENAI_ENDPOINT` | Azure OpenAI |
|
||||||
| `AZURE_OPENAI_API_KEY` | Azure OpenAI, optional when using Entra ID |
|
| `AZURE_OPENAI_API_KEY` | Azure OpenAI, optional when using Entra ID |
|
||||||
| `AZURE_OPENAI_API_VERSION` | Azure OpenAI |
|
| `AZURE_OPENAI_API_VERSION` | Azure OpenAI |
|
||||||
|
|
||||||
|
|
|
@ -55,14 +55,14 @@ You can create your own custom theme by setting the `theme: custom` and providin
|
||||||
|
|
||||||
You can define any of the following color keys in your `customTheme`.
|
You can define any of the following color keys in your `customTheme`.
|
||||||
|
|
||||||
| Type | Color keys |
|
| Type | Color keys |
|
||||||
| --- | --- |
|
| ----------------- | ------------------------------------------------------- |
|
||||||
| Base colors | `primary`, `secondary`, `accent` |
|
| Base colors | `primary`, `secondary`, `accent` |
|
||||||
| Status colors | `error`, `warning`, `success`, `info` |
|
| Status colors | `error`, `warning`, `success`, `info` |
|
||||||
| Text colors | `text`, `textMuted`, `textEmphasized` |
|
| Text colors | `text`, `textMuted`, `textEmphasized` |
|
||||||
| Background colors | `background`, `backgroundSecondary`, `backgroundDarker` |
|
| Background colors | `background`, `backgroundSecondary`, `backgroundDarker` |
|
||||||
| Border colors | `borderNormal`, `borderFocused`, `borderDim` |
|
| Border colors | `borderNormal`, `borderFocused`, `borderDim` |
|
||||||
| Diff view colors | `diffAdded`, `diffRemoved`, `diffContext`, etc. |
|
| Diff view colors | `diffAdded`, `diffRemoved`, `diffContext`, etc. |
|
||||||
|
|
||||||
You don't need to define all the color keys. Any undefined colors will fall back to the default `opencode` theme colors.
|
You don't need to define all the color keys. Any undefined colors will fall back to the default `opencode` theme colors.
|
||||||
|
|
||||||
|
|
2
packages/web/sst-env.d.ts
vendored
2
packages/web/sst-env.d.ts
vendored
|
@ -6,4 +6,4 @@
|
||||||
/// <reference path="../../sst-env.d.ts" />
|
/// <reference path="../../sst-env.d.ts" />
|
||||||
|
|
||||||
import "sst"
|
import "sst"
|
||||||
export {}
|
export {}
|
||||||
|
|
18
sst-env.d.ts
vendored
18
sst-env.d.ts
vendored
|
@ -5,20 +5,20 @@
|
||||||
|
|
||||||
declare module "sst" {
|
declare module "sst" {
|
||||||
export interface Resource {
|
export interface Resource {
|
||||||
"Api": {
|
Api: {
|
||||||
"type": "sst.cloudflare.Worker"
|
type: "sst.cloudflare.Worker"
|
||||||
"url": string
|
url: string
|
||||||
}
|
}
|
||||||
"Bucket": {
|
Bucket: {
|
||||||
"type": "sst.cloudflare.Bucket"
|
type: "sst.cloudflare.Bucket"
|
||||||
}
|
}
|
||||||
"Web": {
|
Web: {
|
||||||
"type": "sst.cloudflare.StaticSite"
|
type: "sst.cloudflare.StaticSite"
|
||||||
"url": string
|
url: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// <reference path="sst-env.d.ts" />
|
/// <reference path="sst-env.d.ts" />
|
||||||
|
|
||||||
import "sst"
|
import "sst"
|
||||||
export {}
|
export {}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue