mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-09-01 15:32:11 +08:00
merge: sync main into read permission fix
This commit is contained in:
@@ -90,9 +90,9 @@ Within each file, migrate git paths first where applicable.
|
||||
|
||||
Migrate git-centric call sites to `Process.git*` helpers:
|
||||
|
||||
- `src/file/index.ts`
|
||||
- `../core/src/filesystem.ts`
|
||||
- `src/project/vcs.ts`
|
||||
- `src/file/watcher.ts`
|
||||
- `../core/src/filesystem/watcher.ts`
|
||||
- `src/storage/storage.ts`
|
||||
- `src/cli/cmd/pr.ts`
|
||||
|
||||
@@ -102,7 +102,7 @@ Migrate residual non-git usages:
|
||||
|
||||
- `src/cli/cmd/tui/util/clipboard.ts`
|
||||
- `src/util/archive.ts`
|
||||
- `src/file/ripgrep.ts`
|
||||
- `../core/src/filesystem/ripgrep.ts`
|
||||
- `src/tool/bash.ts`
|
||||
- `src/cli/cmd/uninstall.ts`
|
||||
|
||||
|
||||
@@ -1,5 +1,64 @@
|
||||
# @kilocode/cli
|
||||
|
||||
## 7.4.7
|
||||
|
||||
## 7.4.6
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#12075](https://github.com/Kilo-Org/kilocode/pull/12075) [`1e0b25a`](https://github.com/Kilo-Org/kilocode/commit/1e0b25a134a11c03494d5871be3e43a6881f1d87) - Support configuring network destinations that sandboxed tools can reach while network access is otherwise restricted.
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#12073](https://github.com/Kilo-Org/kilocode/pull/12073) [`71aa54e`](https://github.com/Kilo-Org/kilocode/commit/71aa54e4131a9ac9b39d2d9585b2101da76d35ca) - Inherit the current model and reasoning variant when Agent Manager starts sessions without explicit overrides.
|
||||
|
||||
- [#12166](https://github.com/Kilo-Org/kilocode/pull/12166) [`4618f1b`](https://github.com/Kilo-Org/kilocode/commit/4618f1b092a948459374a733625f06d02447dc6e) - Preserve dynamic tool properties when removing unsupported regex lookarounds.
|
||||
|
||||
- [#12164](https://github.com/Kilo-Org/kilocode/pull/12164) [`039b73d`](https://github.com/Kilo-Org/kilocode/commit/039b73dfaefe93452501a48914eaeeb2f83c572b) - Wait for the primary codebase index before indexing a linked worktree, preventing large worktrees from consuming excessive CPU during startup.
|
||||
|
||||
- [#12106](https://github.com/Kilo-Org/kilocode/pull/12106) [`b6b55d1`](https://github.com/Kilo-Org/kilocode/commit/b6b55d1a3454bc057ddd24144b0f8d21f870ee55) - Make session model usage easier to scan with collapsible summary rows and aligned steps and cost columns.
|
||||
|
||||
- [#12093](https://github.com/Kilo-Org/kilocode/pull/12093) [`8b46601`](https://github.com/Kilo-Org/kilocode/commit/8b466010c58497acd35867c8a67292c063f3dac4) - Speed up VS Code settings saves by draining pending prompts and disposing worktree instances concurrently.
|
||||
|
||||
- [#12079](https://github.com/Kilo-Org/kilocode/pull/12079) [`0a64070`](https://github.com/Kilo-Org/kilocode/commit/0a640706adcf15968ebc5436e83c6a9c5b8cc4ad) - Resolve AWS Bedrock credentials from SSO profiles in packaged CLI builds.
|
||||
|
||||
- [#12101](https://github.com/Kilo-Org/kilocode/pull/12101) [`bf2b33b`](https://github.com/Kilo-Org/kilocode/commit/bf2b33b87bfc5c35de2173ea66c50e630458e2a5) Thanks [@Githubguy132010](https://github.com/Githubguy132010)! - Use the correct `filePath` argument name in the Gemini system prompt.
|
||||
|
||||
- [#12149](https://github.com/Kilo-Org/kilocode/pull/12149) [`05dadaa`](https://github.com/Kilo-Org/kilocode/commit/05dadaaaed29a04c93aa25f85bddea73a155139e) Thanks [@umi008](https://github.com/umi008)! - Fix Gemma 4 models failing with "thinkingLevel not supported" when using Google AI Studio.
|
||||
|
||||
- [#12148](https://github.com/Kilo-Org/kilocode/pull/12148) [`77f7983`](https://github.com/Kilo-Org/kilocode/commit/77f7983995bcf52debe03ed9209dc56ba3153c31) Thanks [@umi008](https://github.com/umi008)! - Install the latest stable CLI release when newer non-CLI or prerelease releases exist.
|
||||
|
||||
- [#12167](https://github.com/Kilo-Org/kilocode/pull/12167) [`988a92e`](https://github.com/Kilo-Org/kilocode/commit/988a92eae99e453f5a4fe260b0894d93b7271de9) - Fix `kilo upgrade` for curl installs resolving the wrong latest version
|
||||
|
||||
The upgrade command's version resolution for curl-detected installations used GitHub's `/releases/latest` endpoint, which now returns JetBrains plugin releases (e.g. `jetbrains/v7.0.4`) instead of the latest CLI release. This caused `kilo upgrade` to fail for curl installs. Version resolution now uses the npm `latest` dist-tag, matching the install script fix.
|
||||
|
||||
- [#11837](https://github.com/Kilo-Org/kilocode/pull/11837) [`654e10e`](https://github.com/Kilo-Org/kilocode/commit/654e10e25b320fc4518dec192e3fb63137b47182) Thanks [@mjnaderi](https://github.com/mjnaderi)! - Show the Kilo Gateway rate-limit message when login has too many pending authorization requests.
|
||||
|
||||
- [#12162](https://github.com/Kilo-Org/kilocode/pull/12162) [`3ee9144`](https://github.com/Kilo-Org/kilocode/commit/3ee91448eeadf353fc611d8e42ac1f5c8cb5eac0) - Show troubleshooting and migration guidance when Google Gemini rejects API credentials.
|
||||
|
||||
- [#11955](https://github.com/Kilo-Org/kilocode/pull/11955) [`cac82a3`](https://github.com/Kilo-Org/kilocode/commit/cac82a36cac448154c880a0ebdfd283b89559668) Thanks [@jstar0](https://github.com/jstar0)! - Prevent Gemini requests from failing when MCP tool schemas contain `required` fields without matching object properties.
|
||||
|
||||
- [#12153](https://github.com/Kilo-Org/kilocode/pull/12153) [`be15cf4`](https://github.com/Kilo-Org/kilocode/commit/be15cf4b556bea96aaef6de1b3c405b86c0d1a6c) - Allow GPT-5.6 models to use tools whose JSON schemas contain regex lookarounds.
|
||||
|
||||
- [#12168](https://github.com/Kilo-Org/kilocode/pull/12168) [`032f3bb`](https://github.com/Kilo-Org/kilocode/commit/032f3bb55f85ce2b2cc07cea54edf59b23abfcc4) - Block environment and out-of-project file substitutions in project markdown configuration.
|
||||
|
||||
- [#12040](https://github.com/Kilo-Org/kilocode/pull/12040) [`93c209b`](https://github.com/Kilo-Org/kilocode/commit/93c209bfd1f068b26b38ac4e9b7237d4c7f095e1) Thanks [@rakshith1928](https://github.com/rakshith1928)! - Hide gpt-5.5-pro from the model picker when using ChatGPT OAuth login, since Codex rejects it with HTTP 400.
|
||||
|
||||
- [#12087](https://github.com/Kilo-Org/kilocode/pull/12087) [`1f99fb2`](https://github.com/Kilo-Org/kilocode/commit/1f99fb2332b398f8f5066587c970454e7c9d49f9) - Stop explicitly directing GPT and Codex models to delegate tasks to subagents.
|
||||
|
||||
- [#12105](https://github.com/Kilo-Org/kilocode/pull/12105) [`e0bfed3`](https://github.com/Kilo-Org/kilocode/commit/e0bfed308ce7906e4d9ca923e82eda1c20cefd2b) - Shut down the headless `kilo serve` process automatically when the editor client that launched it exits without a clean signal, preventing orphaned CLI processes.
|
||||
|
||||
- [#12092](https://github.com/Kilo-Org/kilocode/pull/12092) [`94b553b`](https://github.com/Kilo-Org/kilocode/commit/94b553b91b130d996ce833e168e579df51a14957) - Show detailed GPT-5.6 reasoning summaries and avoid expandable blank panels when a provider returns only a summary title.
|
||||
|
||||
- Updated dependencies [[`039b73d`](https://github.com/Kilo-Org/kilocode/commit/039b73dfaefe93452501a48914eaeeb2f83c572b), [`1e0b25a`](https://github.com/Kilo-Org/kilocode/commit/1e0b25a134a11c03494d5871be3e43a6881f1d87)]:
|
||||
- @kilocode/kilo-indexing@7.4.6
|
||||
- @kilocode/sdk@7.5.0
|
||||
- @kilocode/plugin@7.4.6
|
||||
- @opencode-ai/ui@7.4.6
|
||||
- @kilocode/kilo-gateway@7.4.6
|
||||
- @kilocode/plugin-atomic-chat@7.4.6
|
||||
- @kilocode/kilo-telemetry@7.4.6
|
||||
|
||||
## 7.4.4
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
import { defineConfig } from "drizzle-kit"
|
||||
|
||||
export default defineConfig({
|
||||
dialect: "sqlite",
|
||||
schema: "./src/**/*.sql.ts",
|
||||
out: "./migration",
|
||||
dbCredentials: {
|
||||
url: "/home/thdxr/.local/share/kilo/kilo.db",
|
||||
},
|
||||
})
|
||||
@@ -1,90 +0,0 @@
|
||||
CREATE TABLE `project` (
|
||||
`id` text PRIMARY KEY,
|
||||
`worktree` text NOT NULL,
|
||||
`vcs` text,
|
||||
`name` text,
|
||||
`icon_url` text,
|
||||
`icon_color` text,
|
||||
`time_created` integer NOT NULL,
|
||||
`time_updated` integer NOT NULL,
|
||||
`time_initialized` integer,
|
||||
`sandboxes` text NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `message` (
|
||||
`id` text PRIMARY KEY,
|
||||
`session_id` text NOT NULL,
|
||||
`time_created` integer NOT NULL,
|
||||
`time_updated` integer NOT NULL,
|
||||
`data` text NOT NULL,
|
||||
CONSTRAINT `fk_message_session_id_session_id_fk` FOREIGN KEY (`session_id`) REFERENCES `session`(`id`) ON DELETE CASCADE
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `part` (
|
||||
`id` text PRIMARY KEY,
|
||||
`message_id` text NOT NULL,
|
||||
`session_id` text NOT NULL,
|
||||
`time_created` integer NOT NULL,
|
||||
`time_updated` integer NOT NULL,
|
||||
`data` text NOT NULL,
|
||||
CONSTRAINT `fk_part_message_id_message_id_fk` FOREIGN KEY (`message_id`) REFERENCES `message`(`id`) ON DELETE CASCADE
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `permission` (
|
||||
`project_id` text PRIMARY KEY,
|
||||
`time_created` integer NOT NULL,
|
||||
`time_updated` integer NOT NULL,
|
||||
`data` text NOT NULL,
|
||||
CONSTRAINT `fk_permission_project_id_project_id_fk` FOREIGN KEY (`project_id`) REFERENCES `project`(`id`) ON DELETE CASCADE
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `session` (
|
||||
`id` text PRIMARY KEY,
|
||||
`project_id` text NOT NULL,
|
||||
`parent_id` text,
|
||||
`slug` text NOT NULL,
|
||||
`directory` text NOT NULL,
|
||||
`title` text NOT NULL,
|
||||
`version` text NOT NULL,
|
||||
`share_url` text,
|
||||
`summary_additions` integer,
|
||||
`summary_deletions` integer,
|
||||
`summary_files` integer,
|
||||
`summary_diffs` text,
|
||||
`revert` text,
|
||||
`permission` text,
|
||||
`time_created` integer NOT NULL,
|
||||
`time_updated` integer NOT NULL,
|
||||
`time_compacting` integer,
|
||||
`time_archived` integer,
|
||||
CONSTRAINT `fk_session_project_id_project_id_fk` FOREIGN KEY (`project_id`) REFERENCES `project`(`id`) ON DELETE CASCADE
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `todo` (
|
||||
`session_id` text NOT NULL,
|
||||
`content` text NOT NULL,
|
||||
`status` text NOT NULL,
|
||||
`priority` text NOT NULL,
|
||||
`position` integer NOT NULL,
|
||||
`time_created` integer NOT NULL,
|
||||
`time_updated` integer NOT NULL,
|
||||
CONSTRAINT `todo_pk` PRIMARY KEY(`session_id`, `position`),
|
||||
CONSTRAINT `fk_todo_session_id_session_id_fk` FOREIGN KEY (`session_id`) REFERENCES `session`(`id`) ON DELETE CASCADE
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `session_share` (
|
||||
`session_id` text PRIMARY KEY,
|
||||
`id` text NOT NULL,
|
||||
`secret` text NOT NULL,
|
||||
`url` text NOT NULL,
|
||||
`time_created` integer NOT NULL,
|
||||
`time_updated` integer NOT NULL,
|
||||
CONSTRAINT `fk_session_share_session_id_session_id_fk` FOREIGN KEY (`session_id`) REFERENCES `session`(`id`) ON DELETE CASCADE
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE INDEX `message_session_idx` ON `message` (`session_id`);--> statement-breakpoint
|
||||
CREATE INDEX `part_message_idx` ON `part` (`message_id`);--> statement-breakpoint
|
||||
CREATE INDEX `part_session_idx` ON `part` (`session_id`);--> statement-breakpoint
|
||||
CREATE INDEX `session_project_idx` ON `session` (`project_id`);--> statement-breakpoint
|
||||
CREATE INDEX `session_parent_idx` ON `session` (`parent_id`);--> statement-breakpoint
|
||||
CREATE INDEX `todo_session_idx` ON `todo` (`session_id`);
|
||||
@@ -1,796 +0,0 @@
|
||||
{
|
||||
"version": "7",
|
||||
"dialect": "sqlite",
|
||||
"id": "068758ed-a97a-46f6-8a59-6c639ae7c20c",
|
||||
"prevIds": ["00000000-0000-0000-0000-000000000000"],
|
||||
"ddl": [
|
||||
{
|
||||
"name": "project",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "message",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "part",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "permission",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "session",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "todo",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "session_share",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "id",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "worktree",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "vcs",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "name",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "icon_url",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "icon_color",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_initialized",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "sandboxes",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "id",
|
||||
"entityType": "columns",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "session_id",
|
||||
"entityType": "columns",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "data",
|
||||
"entityType": "columns",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "id",
|
||||
"entityType": "columns",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "message_id",
|
||||
"entityType": "columns",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "session_id",
|
||||
"entityType": "columns",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "data",
|
||||
"entityType": "columns",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "project_id",
|
||||
"entityType": "columns",
|
||||
"table": "permission"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "permission"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "permission"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "data",
|
||||
"entityType": "columns",
|
||||
"table": "permission"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "id",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "project_id",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "parent_id",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "slug",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "directory",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "title",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "version",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "share_url",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "summary_additions",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "summary_deletions",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "summary_files",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "summary_diffs",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "revert",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "permission",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_compacting",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_archived",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "session_id",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "content",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "status",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "priority",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "position",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "session_id",
|
||||
"entityType": "columns",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "id",
|
||||
"entityType": "columns",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "secret",
|
||||
"entityType": "columns",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "url",
|
||||
"entityType": "columns",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"columns": ["session_id"],
|
||||
"tableTo": "session",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "CASCADE",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_message_session_id_session_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"columns": ["message_id"],
|
||||
"tableTo": "message",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "CASCADE",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_part_message_id_message_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"columns": ["project_id"],
|
||||
"tableTo": "project",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "CASCADE",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_permission_project_id_project_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "permission"
|
||||
},
|
||||
{
|
||||
"columns": ["project_id"],
|
||||
"tableTo": "project",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "CASCADE",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_session_project_id_project_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"columns": ["session_id"],
|
||||
"tableTo": "session",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "CASCADE",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_todo_session_id_session_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"columns": ["session_id"],
|
||||
"tableTo": "session",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "CASCADE",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_session_share_session_id_session_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"columns": ["session_id", "position"],
|
||||
"nameExplicit": false,
|
||||
"name": "todo_pk",
|
||||
"entityType": "pks",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"columns": ["id"],
|
||||
"nameExplicit": false,
|
||||
"name": "project_pk",
|
||||
"table": "project",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": ["id"],
|
||||
"nameExplicit": false,
|
||||
"name": "message_pk",
|
||||
"table": "message",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": ["id"],
|
||||
"nameExplicit": false,
|
||||
"name": "part_pk",
|
||||
"table": "part",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": ["project_id"],
|
||||
"nameExplicit": false,
|
||||
"name": "permission_pk",
|
||||
"table": "permission",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": ["id"],
|
||||
"nameExplicit": false,
|
||||
"name": "session_pk",
|
||||
"table": "session",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": ["session_id"],
|
||||
"nameExplicit": false,
|
||||
"name": "session_share_pk",
|
||||
"table": "session_share",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"value": "session_id",
|
||||
"isExpression": false
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"where": null,
|
||||
"origin": "manual",
|
||||
"name": "message_session_idx",
|
||||
"entityType": "indexes",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"value": "message_id",
|
||||
"isExpression": false
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"where": null,
|
||||
"origin": "manual",
|
||||
"name": "part_message_idx",
|
||||
"entityType": "indexes",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"value": "session_id",
|
||||
"isExpression": false
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"where": null,
|
||||
"origin": "manual",
|
||||
"name": "part_session_idx",
|
||||
"entityType": "indexes",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"value": "project_id",
|
||||
"isExpression": false
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"where": null,
|
||||
"origin": "manual",
|
||||
"name": "session_project_idx",
|
||||
"entityType": "indexes",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"value": "parent_id",
|
||||
"isExpression": false
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"where": null,
|
||||
"origin": "manual",
|
||||
"name": "session_parent_idx",
|
||||
"entityType": "indexes",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"value": "session_id",
|
||||
"isExpression": false
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"where": null,
|
||||
"origin": "manual",
|
||||
"name": "todo_session_idx",
|
||||
"entityType": "indexes",
|
||||
"table": "todo"
|
||||
}
|
||||
],
|
||||
"renames": []
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
ALTER TABLE `project` ADD `commands` text;
|
||||
@@ -1,806 +0,0 @@
|
||||
{
|
||||
"version": "7",
|
||||
"dialect": "sqlite",
|
||||
"id": "8bc2d11d-97fa-4ba8-8bfa-6c5956c49aeb",
|
||||
"prevIds": ["068758ed-a97a-46f6-8a59-6c639ae7c20c"],
|
||||
"ddl": [
|
||||
{
|
||||
"name": "project",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "message",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "part",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "permission",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "session",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "todo",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "session_share",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "id",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "worktree",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "vcs",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "name",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "icon_url",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "icon_color",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_initialized",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "sandboxes",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "commands",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "id",
|
||||
"entityType": "columns",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "session_id",
|
||||
"entityType": "columns",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "data",
|
||||
"entityType": "columns",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "id",
|
||||
"entityType": "columns",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "message_id",
|
||||
"entityType": "columns",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "session_id",
|
||||
"entityType": "columns",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "data",
|
||||
"entityType": "columns",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "project_id",
|
||||
"entityType": "columns",
|
||||
"table": "permission"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "permission"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "permission"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "data",
|
||||
"entityType": "columns",
|
||||
"table": "permission"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "id",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "project_id",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "parent_id",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "slug",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "directory",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "title",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "version",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "share_url",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "summary_additions",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "summary_deletions",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "summary_files",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "summary_diffs",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "revert",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "permission",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_compacting",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_archived",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "session_id",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "content",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "status",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "priority",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "position",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "session_id",
|
||||
"entityType": "columns",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "id",
|
||||
"entityType": "columns",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "secret",
|
||||
"entityType": "columns",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "url",
|
||||
"entityType": "columns",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"columns": ["session_id"],
|
||||
"tableTo": "session",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "CASCADE",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_message_session_id_session_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"columns": ["message_id"],
|
||||
"tableTo": "message",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "CASCADE",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_part_message_id_message_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"columns": ["project_id"],
|
||||
"tableTo": "project",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "CASCADE",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_permission_project_id_project_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "permission"
|
||||
},
|
||||
{
|
||||
"columns": ["project_id"],
|
||||
"tableTo": "project",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "CASCADE",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_session_project_id_project_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"columns": ["session_id"],
|
||||
"tableTo": "session",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "CASCADE",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_todo_session_id_session_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"columns": ["session_id"],
|
||||
"tableTo": "session",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "CASCADE",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_session_share_session_id_session_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"columns": ["session_id", "position"],
|
||||
"nameExplicit": false,
|
||||
"name": "todo_pk",
|
||||
"entityType": "pks",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"columns": ["id"],
|
||||
"nameExplicit": false,
|
||||
"name": "project_pk",
|
||||
"table": "project",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": ["id"],
|
||||
"nameExplicit": false,
|
||||
"name": "message_pk",
|
||||
"table": "message",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": ["id"],
|
||||
"nameExplicit": false,
|
||||
"name": "part_pk",
|
||||
"table": "part",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": ["project_id"],
|
||||
"nameExplicit": false,
|
||||
"name": "permission_pk",
|
||||
"table": "permission",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": ["id"],
|
||||
"nameExplicit": false,
|
||||
"name": "session_pk",
|
||||
"table": "session",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": ["session_id"],
|
||||
"nameExplicit": false,
|
||||
"name": "session_share_pk",
|
||||
"table": "session_share",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"value": "session_id",
|
||||
"isExpression": false
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"where": null,
|
||||
"origin": "manual",
|
||||
"name": "message_session_idx",
|
||||
"entityType": "indexes",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"value": "message_id",
|
||||
"isExpression": false
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"where": null,
|
||||
"origin": "manual",
|
||||
"name": "part_message_idx",
|
||||
"entityType": "indexes",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"value": "session_id",
|
||||
"isExpression": false
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"where": null,
|
||||
"origin": "manual",
|
||||
"name": "part_session_idx",
|
||||
"entityType": "indexes",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"value": "project_id",
|
||||
"isExpression": false
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"where": null,
|
||||
"origin": "manual",
|
||||
"name": "session_project_idx",
|
||||
"entityType": "indexes",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"value": "parent_id",
|
||||
"isExpression": false
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"where": null,
|
||||
"origin": "manual",
|
||||
"name": "session_parent_idx",
|
||||
"entityType": "indexes",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"value": "session_id",
|
||||
"isExpression": false
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"where": null,
|
||||
"origin": "manual",
|
||||
"name": "todo_session_idx",
|
||||
"entityType": "indexes",
|
||||
"table": "todo"
|
||||
}
|
||||
],
|
||||
"renames": []
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
CREATE TABLE `control_account` (
|
||||
`email` text NOT NULL,
|
||||
`url` text NOT NULL,
|
||||
`access_token` text NOT NULL,
|
||||
`refresh_token` text NOT NULL,
|
||||
`token_expiry` integer,
|
||||
`active` integer NOT NULL,
|
||||
`time_created` integer NOT NULL,
|
||||
`time_updated` integer NOT NULL,
|
||||
CONSTRAINT `control_account_pk` PRIMARY KEY(`email`, `url`)
|
||||
);
|
||||
@@ -1,897 +0,0 @@
|
||||
{
|
||||
"version": "7",
|
||||
"dialect": "sqlite",
|
||||
"id": "d2736e43-700f-4e9e-8151-9f2f0d967bc8",
|
||||
"prevIds": ["8bc2d11d-97fa-4ba8-8bfa-6c5956c49aeb"],
|
||||
"ddl": [
|
||||
{
|
||||
"name": "control_account",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "project",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "message",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "part",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "permission",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "session",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "todo",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "session_share",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "email",
|
||||
"entityType": "columns",
|
||||
"table": "control_account"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "url",
|
||||
"entityType": "columns",
|
||||
"table": "control_account"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "access_token",
|
||||
"entityType": "columns",
|
||||
"table": "control_account"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "refresh_token",
|
||||
"entityType": "columns",
|
||||
"table": "control_account"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "token_expiry",
|
||||
"entityType": "columns",
|
||||
"table": "control_account"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "active",
|
||||
"entityType": "columns",
|
||||
"table": "control_account"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "control_account"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "control_account"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "id",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "worktree",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "vcs",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "name",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "icon_url",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "icon_color",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_initialized",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "sandboxes",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "commands",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "id",
|
||||
"entityType": "columns",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "session_id",
|
||||
"entityType": "columns",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "data",
|
||||
"entityType": "columns",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "id",
|
||||
"entityType": "columns",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "message_id",
|
||||
"entityType": "columns",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "session_id",
|
||||
"entityType": "columns",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "data",
|
||||
"entityType": "columns",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "project_id",
|
||||
"entityType": "columns",
|
||||
"table": "permission"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "permission"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "permission"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "data",
|
||||
"entityType": "columns",
|
||||
"table": "permission"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "id",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "project_id",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "parent_id",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "slug",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "directory",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "title",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "version",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "share_url",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "summary_additions",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "summary_deletions",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "summary_files",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "summary_diffs",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "revert",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "permission",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_compacting",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_archived",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "session_id",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "content",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "status",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "priority",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "position",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "session_id",
|
||||
"entityType": "columns",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "id",
|
||||
"entityType": "columns",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "secret",
|
||||
"entityType": "columns",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "url",
|
||||
"entityType": "columns",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"columns": ["session_id"],
|
||||
"tableTo": "session",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "CASCADE",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_message_session_id_session_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"columns": ["message_id"],
|
||||
"tableTo": "message",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "CASCADE",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_part_message_id_message_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"columns": ["project_id"],
|
||||
"tableTo": "project",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "CASCADE",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_permission_project_id_project_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "permission"
|
||||
},
|
||||
{
|
||||
"columns": ["project_id"],
|
||||
"tableTo": "project",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "CASCADE",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_session_project_id_project_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"columns": ["session_id"],
|
||||
"tableTo": "session",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "CASCADE",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_todo_session_id_session_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"columns": ["session_id"],
|
||||
"tableTo": "session",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "CASCADE",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_session_share_session_id_session_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"columns": ["email", "url"],
|
||||
"nameExplicit": false,
|
||||
"name": "control_account_pk",
|
||||
"entityType": "pks",
|
||||
"table": "control_account"
|
||||
},
|
||||
{
|
||||
"columns": ["session_id", "position"],
|
||||
"nameExplicit": false,
|
||||
"name": "todo_pk",
|
||||
"entityType": "pks",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"columns": ["id"],
|
||||
"nameExplicit": false,
|
||||
"name": "project_pk",
|
||||
"table": "project",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": ["id"],
|
||||
"nameExplicit": false,
|
||||
"name": "message_pk",
|
||||
"table": "message",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": ["id"],
|
||||
"nameExplicit": false,
|
||||
"name": "part_pk",
|
||||
"table": "part",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": ["project_id"],
|
||||
"nameExplicit": false,
|
||||
"name": "permission_pk",
|
||||
"table": "permission",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": ["id"],
|
||||
"nameExplicit": false,
|
||||
"name": "session_pk",
|
||||
"table": "session",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": ["session_id"],
|
||||
"nameExplicit": false,
|
||||
"name": "session_share_pk",
|
||||
"table": "session_share",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"value": "session_id",
|
||||
"isExpression": false
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"where": null,
|
||||
"origin": "manual",
|
||||
"name": "message_session_idx",
|
||||
"entityType": "indexes",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"value": "message_id",
|
||||
"isExpression": false
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"where": null,
|
||||
"origin": "manual",
|
||||
"name": "part_message_idx",
|
||||
"entityType": "indexes",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"value": "session_id",
|
||||
"isExpression": false
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"where": null,
|
||||
"origin": "manual",
|
||||
"name": "part_session_idx",
|
||||
"entityType": "indexes",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"value": "project_id",
|
||||
"isExpression": false
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"where": null,
|
||||
"origin": "manual",
|
||||
"name": "session_project_idx",
|
||||
"entityType": "indexes",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"value": "parent_id",
|
||||
"isExpression": false
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"where": null,
|
||||
"origin": "manual",
|
||||
"name": "session_parent_idx",
|
||||
"entityType": "indexes",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"value": "session_id",
|
||||
"isExpression": false
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"where": null,
|
||||
"origin": "manual",
|
||||
"name": "todo_session_idx",
|
||||
"entityType": "indexes",
|
||||
"table": "todo"
|
||||
}
|
||||
],
|
||||
"renames": []
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
CREATE TABLE `workspace` (
|
||||
`id` text PRIMARY KEY,
|
||||
`branch` text,
|
||||
`project_id` text NOT NULL,
|
||||
`config` text NOT NULL,
|
||||
CONSTRAINT `fk_workspace_project_id_project_id_fk` FOREIGN KEY (`project_id`) REFERENCES `project`(`id`) ON DELETE CASCADE
|
||||
);
|
||||
@@ -1,959 +0,0 @@
|
||||
{
|
||||
"version": "7",
|
||||
"dialect": "sqlite",
|
||||
"id": "1f1dbf2d-bf66-4b25-8af4-4ba7633b7e40",
|
||||
"prevIds": ["d2736e43-700f-4e9e-8151-9f2f0d967bc8"],
|
||||
"ddl": [
|
||||
{
|
||||
"name": "workspace",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "control_account",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "project",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "message",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "part",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "permission",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "session",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "todo",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "session_share",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "id",
|
||||
"entityType": "columns",
|
||||
"table": "workspace"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "branch",
|
||||
"entityType": "columns",
|
||||
"table": "workspace"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "project_id",
|
||||
"entityType": "columns",
|
||||
"table": "workspace"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "config",
|
||||
"entityType": "columns",
|
||||
"table": "workspace"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "email",
|
||||
"entityType": "columns",
|
||||
"table": "control_account"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "url",
|
||||
"entityType": "columns",
|
||||
"table": "control_account"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "access_token",
|
||||
"entityType": "columns",
|
||||
"table": "control_account"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "refresh_token",
|
||||
"entityType": "columns",
|
||||
"table": "control_account"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "token_expiry",
|
||||
"entityType": "columns",
|
||||
"table": "control_account"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "active",
|
||||
"entityType": "columns",
|
||||
"table": "control_account"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "control_account"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "control_account"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "id",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "worktree",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "vcs",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "name",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "icon_url",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "icon_color",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_initialized",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "sandboxes",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "commands",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "id",
|
||||
"entityType": "columns",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "session_id",
|
||||
"entityType": "columns",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "data",
|
||||
"entityType": "columns",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "id",
|
||||
"entityType": "columns",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "message_id",
|
||||
"entityType": "columns",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "session_id",
|
||||
"entityType": "columns",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "data",
|
||||
"entityType": "columns",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "project_id",
|
||||
"entityType": "columns",
|
||||
"table": "permission"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "permission"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "permission"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "data",
|
||||
"entityType": "columns",
|
||||
"table": "permission"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "id",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "project_id",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "parent_id",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "slug",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "directory",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "title",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "version",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "share_url",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "summary_additions",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "summary_deletions",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "summary_files",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "summary_diffs",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "revert",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "permission",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_compacting",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_archived",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "session_id",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "content",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "status",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "priority",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "position",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "session_id",
|
||||
"entityType": "columns",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "id",
|
||||
"entityType": "columns",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "secret",
|
||||
"entityType": "columns",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "url",
|
||||
"entityType": "columns",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"columns": ["project_id"],
|
||||
"tableTo": "project",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "CASCADE",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_workspace_project_id_project_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "workspace"
|
||||
},
|
||||
{
|
||||
"columns": ["session_id"],
|
||||
"tableTo": "session",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "CASCADE",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_message_session_id_session_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"columns": ["message_id"],
|
||||
"tableTo": "message",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "CASCADE",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_part_message_id_message_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"columns": ["project_id"],
|
||||
"tableTo": "project",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "CASCADE",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_permission_project_id_project_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "permission"
|
||||
},
|
||||
{
|
||||
"columns": ["project_id"],
|
||||
"tableTo": "project",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "CASCADE",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_session_project_id_project_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"columns": ["session_id"],
|
||||
"tableTo": "session",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "CASCADE",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_todo_session_id_session_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"columns": ["session_id"],
|
||||
"tableTo": "session",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "CASCADE",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_session_share_session_id_session_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"columns": ["email", "url"],
|
||||
"nameExplicit": false,
|
||||
"name": "control_account_pk",
|
||||
"entityType": "pks",
|
||||
"table": "control_account"
|
||||
},
|
||||
{
|
||||
"columns": ["session_id", "position"],
|
||||
"nameExplicit": false,
|
||||
"name": "todo_pk",
|
||||
"entityType": "pks",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"columns": ["id"],
|
||||
"nameExplicit": false,
|
||||
"name": "workspace_pk",
|
||||
"table": "workspace",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": ["id"],
|
||||
"nameExplicit": false,
|
||||
"name": "project_pk",
|
||||
"table": "project",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": ["id"],
|
||||
"nameExplicit": false,
|
||||
"name": "message_pk",
|
||||
"table": "message",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": ["id"],
|
||||
"nameExplicit": false,
|
||||
"name": "part_pk",
|
||||
"table": "part",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": ["project_id"],
|
||||
"nameExplicit": false,
|
||||
"name": "permission_pk",
|
||||
"table": "permission",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": ["id"],
|
||||
"nameExplicit": false,
|
||||
"name": "session_pk",
|
||||
"table": "session",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": ["session_id"],
|
||||
"nameExplicit": false,
|
||||
"name": "session_share_pk",
|
||||
"table": "session_share",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"value": "session_id",
|
||||
"isExpression": false
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"where": null,
|
||||
"origin": "manual",
|
||||
"name": "message_session_idx",
|
||||
"entityType": "indexes",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"value": "message_id",
|
||||
"isExpression": false
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"where": null,
|
||||
"origin": "manual",
|
||||
"name": "part_message_idx",
|
||||
"entityType": "indexes",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"value": "session_id",
|
||||
"isExpression": false
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"where": null,
|
||||
"origin": "manual",
|
||||
"name": "part_session_idx",
|
||||
"entityType": "indexes",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"value": "project_id",
|
||||
"isExpression": false
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"where": null,
|
||||
"origin": "manual",
|
||||
"name": "session_project_idx",
|
||||
"entityType": "indexes",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"value": "parent_id",
|
||||
"isExpression": false
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"where": null,
|
||||
"origin": "manual",
|
||||
"name": "session_parent_idx",
|
||||
"entityType": "indexes",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"value": "session_id",
|
||||
"isExpression": false
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"where": null,
|
||||
"origin": "manual",
|
||||
"name": "todo_session_idx",
|
||||
"entityType": "indexes",
|
||||
"table": "todo"
|
||||
}
|
||||
],
|
||||
"renames": []
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
ALTER TABLE `session` ADD `workspace_id` text;--> statement-breakpoint
|
||||
CREATE INDEX `session_workspace_idx` ON `session` (`workspace_id`);
|
||||
@@ -1,983 +0,0 @@
|
||||
{
|
||||
"version": "7",
|
||||
"dialect": "sqlite",
|
||||
"id": "572fb732-56f4-4b1e-b981-77152c9980dd",
|
||||
"prevIds": ["1f1dbf2d-bf66-4b25-8af4-4ba7633b7e40"],
|
||||
"ddl": [
|
||||
{
|
||||
"name": "workspace",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "control_account",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "project",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "message",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "part",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "permission",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "session",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "todo",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"name": "session_share",
|
||||
"entityType": "tables"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "id",
|
||||
"entityType": "columns",
|
||||
"table": "workspace"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "branch",
|
||||
"entityType": "columns",
|
||||
"table": "workspace"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "project_id",
|
||||
"entityType": "columns",
|
||||
"table": "workspace"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "config",
|
||||
"entityType": "columns",
|
||||
"table": "workspace"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "email",
|
||||
"entityType": "columns",
|
||||
"table": "control_account"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "url",
|
||||
"entityType": "columns",
|
||||
"table": "control_account"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "access_token",
|
||||
"entityType": "columns",
|
||||
"table": "control_account"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "refresh_token",
|
||||
"entityType": "columns",
|
||||
"table": "control_account"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "token_expiry",
|
||||
"entityType": "columns",
|
||||
"table": "control_account"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "active",
|
||||
"entityType": "columns",
|
||||
"table": "control_account"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "control_account"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "control_account"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "id",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "worktree",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "vcs",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "name",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "icon_url",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "icon_color",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_initialized",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "sandboxes",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "commands",
|
||||
"entityType": "columns",
|
||||
"table": "project"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "id",
|
||||
"entityType": "columns",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "session_id",
|
||||
"entityType": "columns",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "data",
|
||||
"entityType": "columns",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "id",
|
||||
"entityType": "columns",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "message_id",
|
||||
"entityType": "columns",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "session_id",
|
||||
"entityType": "columns",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "data",
|
||||
"entityType": "columns",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "project_id",
|
||||
"entityType": "columns",
|
||||
"table": "permission"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "permission"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "permission"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "data",
|
||||
"entityType": "columns",
|
||||
"table": "permission"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "id",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "project_id",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "workspace_id",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "parent_id",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "slug",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "directory",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "title",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "version",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "share_url",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "summary_additions",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "summary_deletions",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "summary_files",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "summary_diffs",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "revert",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "permission",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_compacting",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_archived",
|
||||
"entityType": "columns",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "session_id",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "content",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "status",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "priority",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "position",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "session_id",
|
||||
"entityType": "columns",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "id",
|
||||
"entityType": "columns",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "secret",
|
||||
"entityType": "columns",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "url",
|
||||
"entityType": "columns",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_created",
|
||||
"entityType": "columns",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": null,
|
||||
"generated": null,
|
||||
"name": "time_updated",
|
||||
"entityType": "columns",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"columns": ["project_id"],
|
||||
"tableTo": "project",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "CASCADE",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_workspace_project_id_project_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "workspace"
|
||||
},
|
||||
{
|
||||
"columns": ["session_id"],
|
||||
"tableTo": "session",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "CASCADE",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_message_session_id_session_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"columns": ["message_id"],
|
||||
"tableTo": "message",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "CASCADE",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_part_message_id_message_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"columns": ["project_id"],
|
||||
"tableTo": "project",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "CASCADE",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_permission_project_id_project_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "permission"
|
||||
},
|
||||
{
|
||||
"columns": ["project_id"],
|
||||
"tableTo": "project",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "CASCADE",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_session_project_id_project_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"columns": ["session_id"],
|
||||
"tableTo": "session",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "CASCADE",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_todo_session_id_session_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"columns": ["session_id"],
|
||||
"tableTo": "session",
|
||||
"columnsTo": ["id"],
|
||||
"onUpdate": "NO ACTION",
|
||||
"onDelete": "CASCADE",
|
||||
"nameExplicit": false,
|
||||
"name": "fk_session_share_session_id_session_id_fk",
|
||||
"entityType": "fks",
|
||||
"table": "session_share"
|
||||
},
|
||||
{
|
||||
"columns": ["email", "url"],
|
||||
"nameExplicit": false,
|
||||
"name": "control_account_pk",
|
||||
"entityType": "pks",
|
||||
"table": "control_account"
|
||||
},
|
||||
{
|
||||
"columns": ["session_id", "position"],
|
||||
"nameExplicit": false,
|
||||
"name": "todo_pk",
|
||||
"entityType": "pks",
|
||||
"table": "todo"
|
||||
},
|
||||
{
|
||||
"columns": ["id"],
|
||||
"nameExplicit": false,
|
||||
"name": "workspace_pk",
|
||||
"table": "workspace",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": ["id"],
|
||||
"nameExplicit": false,
|
||||
"name": "project_pk",
|
||||
"table": "project",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": ["id"],
|
||||
"nameExplicit": false,
|
||||
"name": "message_pk",
|
||||
"table": "message",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": ["id"],
|
||||
"nameExplicit": false,
|
||||
"name": "part_pk",
|
||||
"table": "part",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": ["project_id"],
|
||||
"nameExplicit": false,
|
||||
"name": "permission_pk",
|
||||
"table": "permission",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": ["id"],
|
||||
"nameExplicit": false,
|
||||
"name": "session_pk",
|
||||
"table": "session",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": ["session_id"],
|
||||
"nameExplicit": false,
|
||||
"name": "session_share_pk",
|
||||
"table": "session_share",
|
||||
"entityType": "pks"
|
||||
},
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"value": "session_id",
|
||||
"isExpression": false
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"where": null,
|
||||
"origin": "manual",
|
||||
"name": "message_session_idx",
|
||||
"entityType": "indexes",
|
||||
"table": "message"
|
||||
},
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"value": "message_id",
|
||||
"isExpression": false
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"where": null,
|
||||
"origin": "manual",
|
||||
"name": "part_message_idx",
|
||||
"entityType": "indexes",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"value": "session_id",
|
||||
"isExpression": false
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"where": null,
|
||||
"origin": "manual",
|
||||
"name": "part_session_idx",
|
||||
"entityType": "indexes",
|
||||
"table": "part"
|
||||
},
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"value": "project_id",
|
||||
"isExpression": false
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"where": null,
|
||||
"origin": "manual",
|
||||
"name": "session_project_idx",
|
||||
"entityType": "indexes",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"value": "workspace_id",
|
||||
"isExpression": false
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"where": null,
|
||||
"origin": "manual",
|
||||
"name": "session_workspace_idx",
|
||||
"entityType": "indexes",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"value": "parent_id",
|
||||
"isExpression": false
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"where": null,
|
||||
"origin": "manual",
|
||||
"name": "session_parent_idx",
|
||||
"entityType": "indexes",
|
||||
"table": "session"
|
||||
},
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"value": "session_id",
|
||||
"isExpression": false
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"where": null,
|
||||
"origin": "manual",
|
||||
"name": "todo_session_idx",
|
||||
"entityType": "indexes",
|
||||
"table": "todo"
|
||||
}
|
||||
],
|
||||
"renames": []
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
CREATE TABLE `account` (
|
||||
`id` text PRIMARY KEY,
|
||||
`email` text NOT NULL,
|
||||
`url` text NOT NULL,
|
||||
`access_token` text NOT NULL,
|
||||
`refresh_token` text NOT NULL,
|
||||
`token_expiry` integer,
|
||||
`selected_org_id` text,
|
||||
`time_created` integer NOT NULL,
|
||||
`time_updated` integer NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `account_state` (
|
||||
`id` integer PRIMARY KEY NOT NULL,
|
||||
`active_account_id` text,
|
||||
FOREIGN KEY (`active_account_id`) REFERENCES `account`(`id`) ON UPDATE no action ON DELETE set null
|
||||
);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +0,0 @@
|
||||
ALTER TABLE `workspace` ADD `type` text NOT NULL;--> statement-breakpoint
|
||||
ALTER TABLE `workspace` ADD `name` text;--> statement-breakpoint
|
||||
ALTER TABLE `workspace` ADD `directory` text;--> statement-breakpoint
|
||||
ALTER TABLE `workspace` ADD `extra` text;--> statement-breakpoint
|
||||
ALTER TABLE `workspace` DROP COLUMN `config`;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
||||
ALTER TABLE `account_state` ADD `active_org_id` text;--> statement-breakpoint
|
||||
UPDATE `account_state` SET `active_org_id` = (SELECT `selected_org_id` FROM `account` WHERE `account`.`id` = `account_state`.`active_account_id`);--> statement-breakpoint
|
||||
ALTER TABLE `account` DROP COLUMN `selected_org_id`;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +0,0 @@
|
||||
DROP INDEX IF EXISTS `message_session_idx`;--> statement-breakpoint
|
||||
DROP INDEX IF EXISTS `part_message_idx`;--> statement-breakpoint
|
||||
CREATE INDEX `message_session_time_created_id_idx` ON `message` (`session_id`,`time_created`,`id`);--> statement-breakpoint
|
||||
CREATE INDEX `part_message_id_id_idx` ON `part` (`message_id`,`id`);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,13 +0,0 @@
|
||||
CREATE TABLE `event_sequence` (
|
||||
`aggregate_id` text PRIMARY KEY,
|
||||
`seq` integer NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `event` (
|
||||
`id` text PRIMARY KEY,
|
||||
`aggregate_id` text NOT NULL,
|
||||
`seq` integer NOT NULL,
|
||||
`type` text NOT NULL,
|
||||
`data` text NOT NULL,
|
||||
CONSTRAINT `fk_event_aggregate_id_event_sequence_aggregate_id_fk` FOREIGN KEY (`aggregate_id`) REFERENCES `event_sequence`(`aggregate_id`) ON DELETE CASCADE
|
||||
);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,16 +0,0 @@
|
||||
PRAGMA foreign_keys=OFF;--> statement-breakpoint
|
||||
CREATE TABLE `__new_workspace` (
|
||||
`id` text PRIMARY KEY,
|
||||
`type` text NOT NULL,
|
||||
`name` text DEFAULT '' NOT NULL,
|
||||
`branch` text,
|
||||
`directory` text,
|
||||
`extra` text,
|
||||
`project_id` text NOT NULL,
|
||||
CONSTRAINT `fk_workspace_project_id_project_id_fk` FOREIGN KEY (`project_id`) REFERENCES `project`(`id`) ON DELETE CASCADE
|
||||
);
|
||||
--> statement-breakpoint
|
||||
INSERT INTO `__new_workspace`(`id`, `type`, `branch`, `name`, `directory`, `extra`, `project_id`) SELECT `id`, `type`, `branch`, `name`, `directory`, `extra`, `project_id` FROM `workspace`;--> statement-breakpoint
|
||||
DROP TABLE `workspace`;--> statement-breakpoint
|
||||
ALTER TABLE `__new_workspace` RENAME TO `workspace`;--> statement-breakpoint
|
||||
PRAGMA foreign_keys=ON;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,13 +0,0 @@
|
||||
CREATE TABLE `session_entry` (
|
||||
`id` text PRIMARY KEY,
|
||||
`session_id` text NOT NULL,
|
||||
`type` text NOT NULL,
|
||||
`time_created` integer NOT NULL,
|
||||
`time_updated` integer NOT NULL,
|
||||
`data` text NOT NULL,
|
||||
CONSTRAINT `fk_session_entry_session_id_session_id_fk` FOREIGN KEY (`session_id`) REFERENCES `session`(`id`) ON DELETE CASCADE
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE INDEX `session_entry_session_idx` ON `session_entry` (`session_id`);--> statement-breakpoint
|
||||
CREATE INDEX `session_entry_session_type_idx` ON `session_entry` (`session_id`,`type`);--> statement-breakpoint
|
||||
CREATE INDEX `session_entry_time_created_idx` ON `session_entry` (`time_created`);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,2 +0,0 @@
|
||||
ALTER TABLE `project` ADD `icon_url_override` text;
|
||||
UPDATE `project` SET `icon_url_override` = `icon_url` WHERE `icon_url` IS NOT NULL;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,17 +0,0 @@
|
||||
CREATE TABLE `session_message` (
|
||||
`id` text PRIMARY KEY,
|
||||
`session_id` text NOT NULL,
|
||||
`type` text NOT NULL,
|
||||
`time_created` integer NOT NULL,
|
||||
`time_updated` integer NOT NULL,
|
||||
`data` text NOT NULL,
|
||||
CONSTRAINT `fk_session_message_session_id_session_id_fk` FOREIGN KEY (`session_id`) REFERENCES `session`(`id`) ON DELETE CASCADE
|
||||
);
|
||||
--> statement-breakpoint
|
||||
DROP INDEX IF EXISTS `session_entry_session_idx`;--> statement-breakpoint
|
||||
DROP INDEX IF EXISTS `session_entry_session_type_idx`;--> statement-breakpoint
|
||||
DROP INDEX IF EXISTS `session_entry_time_created_idx`;--> statement-breakpoint
|
||||
CREATE INDEX `session_message_session_idx` ON `session_message` (`session_id`);--> statement-breakpoint
|
||||
CREATE INDEX `session_message_session_type_idx` ON `session_message` (`session_id`,`type`);--> statement-breakpoint
|
||||
CREATE INDEX `session_message_time_created_idx` ON `session_message` (`time_created`);--> statement-breakpoint
|
||||
DROP TABLE `session_entry`;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
ALTER TABLE `session` ADD `path` text;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,2 +0,0 @@
|
||||
ALTER TABLE `session` ADD `agent` text;--> statement-breakpoint
|
||||
ALTER TABLE `session` ADD `model` text;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
ALTER TABLE `event_sequence` ADD `owner_id` text;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
ALTER TABLE `workspace` ADD `time_used` integer NOT NULL DEFAULT 0;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +0,0 @@
|
||||
ALTER TABLE `session` ADD `cost` real DEFAULT 0 NOT NULL;--> statement-breakpoint
|
||||
ALTER TABLE `session` ADD `tokens_input` integer DEFAULT 0 NOT NULL;--> statement-breakpoint
|
||||
ALTER TABLE `session` ADD `tokens_output` integer DEFAULT 0 NOT NULL;--> statement-breakpoint
|
||||
ALTER TABLE `session` ADD `tokens_reasoning` integer DEFAULT 0 NOT NULL;--> statement-breakpoint
|
||||
ALTER TABLE `session` ADD `tokens_cache_read` integer DEFAULT 0 NOT NULL;--> statement-breakpoint
|
||||
ALTER TABLE `session` ADD `tokens_cache_write` integer DEFAULT 0 NOT NULL;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +0,0 @@
|
||||
CREATE TABLE `data_migration` (
|
||||
`name` text PRIMARY KEY,
|
||||
`time_completed` integer NOT NULL
|
||||
);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "7.4.5",
|
||||
"version": "7.4.7",
|
||||
"name": "@kilocode/cli",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
@@ -13,10 +13,8 @@
|
||||
"bench:test": "bun run script/bench-test-suite.ts",
|
||||
"profile:test": "bun run script/profile-test-files.ts",
|
||||
"build": "bun run script/build.ts",
|
||||
"fix-node-pty": "bun run script/fix-node-pty.ts",
|
||||
"dev": "bun run --conditions=browser ./src/index.ts",
|
||||
"dev:temporary": "bun run --conditions=browser ./src/temporary.ts",
|
||||
"db": "bun drizzle-kit"
|
||||
"dev:temporary": "bun run --conditions=browser ./src/temporary.ts"
|
||||
},
|
||||
"bin": {
|
||||
"kilo": "./bin/kilo",
|
||||
@@ -30,11 +28,6 @@
|
||||
"bun": "./src/storage/db.bun.ts",
|
||||
"node": "./src/storage/db.node.ts",
|
||||
"default": "./src/storage/db.bun.ts"
|
||||
},
|
||||
"#pty": {
|
||||
"bun": "./src/pty/pty.bun.ts",
|
||||
"node": "./src/pty/pty.node.ts",
|
||||
"default": "./src/pty/pty.bun.ts"
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -68,22 +61,25 @@
|
||||
"vscode-languageserver-types": "3.17.5",
|
||||
"why-is-node-running": "3.2.2",
|
||||
"@types/npmcli__arborist": "6.3.3",
|
||||
"@opencode-ai/http-recorder": "workspace:*"
|
||||
"@opencode-ai/http-recorder": "workspace:*",
|
||||
"@babel/core": "7.28.4",
|
||||
"@standard-schema/spec": "1.0.0",
|
||||
"@types/babel__core": "7.20.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/core": "1.11.1",
|
||||
"@actions/github": "6.0.1",
|
||||
"@agentclientprotocol/sdk": "0.21.0",
|
||||
"@ai-sdk/alibaba": "1.0.17",
|
||||
"@ai-sdk/amazon-bedrock": "4.0.96",
|
||||
"@ai-sdk/amazon-bedrock": "4.0.112",
|
||||
"@ai-sdk/anthropic": "3.0.71",
|
||||
"@ai-sdk/azure": "3.0.49",
|
||||
"@ai-sdk/cerebras": "2.0.54",
|
||||
"@ai-sdk/cohere": "3.0.27",
|
||||
"@ai-sdk/deepinfra": "2.0.41",
|
||||
"@ai-sdk/gateway": "3.0.104",
|
||||
"@ai-sdk/google": "3.0.63",
|
||||
"@ai-sdk/google-vertex": "4.0.112",
|
||||
"@ai-sdk/google": "3.0.73",
|
||||
"@ai-sdk/google-vertex": "4.0.128",
|
||||
"@ai-sdk/groq": "3.0.31",
|
||||
"@ai-sdk/mistral": "3.0.27",
|
||||
"@ai-sdk/openai": "3.0.53",
|
||||
@@ -94,7 +90,7 @@
|
||||
"@ai-sdk/togetherai": "2.0.41",
|
||||
"@ai-sdk/vercel": "2.0.39",
|
||||
"@ai-sdk/xai": "3.0.92",
|
||||
"@aws-sdk/credential-providers": "3.1025.0",
|
||||
"@aws-sdk/credential-providers": "3.1057.0",
|
||||
"@clack/prompts": "1.0.0-alpha.1",
|
||||
"@effect/opentelemetry": "catalog:",
|
||||
"@effect/platform-node": "catalog:",
|
||||
@@ -170,14 +166,25 @@
|
||||
"tree-sitter-wasms": "^0.1.12",
|
||||
"turndown": "7.2.0",
|
||||
"ulid": "catalog:",
|
||||
"venice-ai-sdk-provider": "2.0.1",
|
||||
"venice-ai-sdk-provider": "2.0.2",
|
||||
"vscode-jsonrpc": "8.2.1",
|
||||
"web-tree-sitter": "0.25.10",
|
||||
"which": "6.0.1",
|
||||
"ws": "8.21.0",
|
||||
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
|
||||
"yargs": "18.0.0",
|
||||
"zod": "catalog:"
|
||||
"zod": "catalog:",
|
||||
"@openauthjs/openauth": "catalog:",
|
||||
"@opencode-ai/server": "workspace:*",
|
||||
"@opentelemetry/context-async-hooks": "2.6.1",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "0.214.0",
|
||||
"@pierre/diffs": "catalog:",
|
||||
"@standard-schema/spec": "1.0.0",
|
||||
"chokidar": "4.0.3",
|
||||
"glob": "13.0.5",
|
||||
"minimatch": "10.0.3",
|
||||
"partial-json": "0.1.7",
|
||||
"xdg-basedir": "5.1.0"
|
||||
},
|
||||
"overrides": {
|
||||
"drizzle-orm": "catalog:"
|
||||
|
||||
@@ -166,6 +166,16 @@ export default {
|
||||
// },
|
||||
// },
|
||||
},
|
||||
{
|
||||
filetype: "vue",
|
||||
wasm: "https://github.com/anomalyco/tree-sitter-vue/releases/download/v0.1.2/tree-sitter-vue.wasm",
|
||||
queries: {
|
||||
highlights: [
|
||||
"https://raw.githubusercontent.com/anomalyco/tree-sitter-vue/v0.1.2/queries/html_tags/highlights.scm",
|
||||
"https://raw.githubusercontent.com/anomalyco/tree-sitter-vue/v0.1.2/queries/vue/highlights.scm",
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
filetype: "hcl",
|
||||
wasm: "https://github.com/tree-sitter-grammars/tree-sitter-hcl/releases/download/v1.2.0/tree-sitter-hcl.wasm",
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#!/usr/bin/env bun
|
||||
|
||||
import { Script } from "@opencode-ai/script"
|
||||
import fs from "fs"
|
||||
import path from "path"
|
||||
import { fileURLToPath } from "url"
|
||||
|
||||
@@ -13,36 +12,6 @@ process.chdir(dir)
|
||||
|
||||
const generated = await import("./generate.ts")
|
||||
|
||||
// Load migrations from migration directories
|
||||
const migrationDirs = (
|
||||
await fs.promises.readdir(path.join(dir, "migration"), {
|
||||
withFileTypes: true,
|
||||
})
|
||||
)
|
||||
.filter((entry) => entry.isDirectory() && /^\d{4}\d{2}\d{2}\d{2}\d{2}\d{2}/.test(entry.name))
|
||||
.map((entry) => entry.name)
|
||||
.sort()
|
||||
|
||||
const migrations = await Promise.all(
|
||||
migrationDirs.map(async (name) => {
|
||||
const file = path.join(dir, "migration", name, "migration.sql")
|
||||
const sql = await Bun.file(file).text()
|
||||
const match = /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/.exec(name)
|
||||
const timestamp = match
|
||||
? Date.UTC(
|
||||
Number(match[1]),
|
||||
Number(match[2]) - 1,
|
||||
Number(match[3]),
|
||||
Number(match[4]),
|
||||
Number(match[5]),
|
||||
Number(match[6]),
|
||||
)
|
||||
: 0
|
||||
return { sql, timestamp, name }
|
||||
}),
|
||||
)
|
||||
console.log(`Loaded ${migrations.length} migrations`)
|
||||
|
||||
await Bun.build({
|
||||
target: "node",
|
||||
// kilocode_change start
|
||||
@@ -57,7 +26,6 @@ await Bun.build({
|
||||
sourcemap: "linked",
|
||||
external: ["jsonc-parser", "@lydell/node-pty"],
|
||||
define: {
|
||||
KILO_MIGRATIONS: JSON.stringify(migrations),
|
||||
KILO_MODELS_DEV: generated.modelsData,
|
||||
KILO_SANDBOX_MUTATION_WORKER_PATH: `'./kilo-sandbox-mutation-worker.js'`, // kilocode_change
|
||||
KILO_SANDBOX_NETWORK_RELAY_PATH: `'./kilo-sandbox-network-relay.js'`, // kilocode_change
|
||||
|
||||
@@ -26,36 +26,6 @@ import { KiloSandboxWorker } from "./kilocode/kilo-sandbox-worker"
|
||||
import { KiloSandboxNetwork } from "./kilocode/kilo-sandbox-network"
|
||||
// kilocode_change end
|
||||
|
||||
// Load migrations from migration directories
|
||||
const migrationDirs = (
|
||||
await fs.promises.readdir(path.join(dir, "migration"), {
|
||||
withFileTypes: true,
|
||||
})
|
||||
)
|
||||
.filter((entry) => entry.isDirectory() && /^\d{4}\d{2}\d{2}\d{2}\d{2}\d{2}/.test(entry.name))
|
||||
.map((entry) => entry.name)
|
||||
.sort()
|
||||
|
||||
const migrations = await Promise.all(
|
||||
migrationDirs.map(async (name) => {
|
||||
const file = path.join(dir, "migration", name, "migration.sql")
|
||||
const sql = await Bun.file(file).text()
|
||||
const match = /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/.exec(name)
|
||||
const timestamp = match
|
||||
? Date.UTC(
|
||||
Number(match[1]),
|
||||
Number(match[2]) - 1,
|
||||
Number(match[3]),
|
||||
Number(match[4]),
|
||||
Number(match[5]),
|
||||
Number(match[6]),
|
||||
)
|
||||
: 0
|
||||
return { sql, timestamp, name }
|
||||
}),
|
||||
)
|
||||
console.log(`Loaded ${migrations.length} migrations`)
|
||||
|
||||
const singleFlag = process.argv.includes("--single")
|
||||
const baselineFlag = process.argv.includes("--baseline")
|
||||
const skipInstall = process.argv.includes("--skip-install")
|
||||
@@ -330,7 +300,6 @@ for (const item of targets) {
|
||||
// kilocode_change end
|
||||
define: {
|
||||
KILO_VERSION: `'${Script.version}'`,
|
||||
KILO_MIGRATIONS: JSON.stringify(migrations),
|
||||
KILO_MODELS_DEV: generated.modelsData,
|
||||
OTUI_TREE_SITTER_WORKER_PATH: bunfsRoot + workerRelativePath,
|
||||
KILO_WORKER_PATH: workerPath,
|
||||
@@ -347,6 +316,7 @@ for (const item of targets) {
|
||||
KILO_BWRAP_SHA256: bwrap ? `'${bwrap}'` : "undefined",
|
||||
KILO_BUILD_KIND: Script.release ? `'release'` : `'source'`,
|
||||
// kilocode_change end
|
||||
...(item.os === "linux" ? { "process.env.OPENTUI_LIBC": JSON.stringify(item.abi ?? "glibc") } : {}),
|
||||
},
|
||||
})
|
||||
|
||||
@@ -432,6 +402,7 @@ for (const item of targets) {
|
||||
url: "https://github.com/Kilo-Org/kilocode",
|
||||
},
|
||||
// kilocode_change end
|
||||
...(item.abi ? { libc: [item.abi] } : {}),
|
||||
},
|
||||
null,
|
||||
2,
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
#!/usr/bin/env bun
|
||||
|
||||
import { $ } from "bun"
|
||||
|
||||
// drizzle-kit check compares schema to migrations, exits non-zero if drift
|
||||
const result = await $`bun drizzle-kit check`.quiet().nothrow()
|
||||
|
||||
if (result.exitCode !== 0) {
|
||||
console.error("Schema has changes not captured in migrations!")
|
||||
console.error("Run: bun drizzle-kit generate")
|
||||
console.error("")
|
||||
console.error(result.stderr.toString())
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
console.log("Migrations are up to date")
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/usr/bin/env bun
|
||||
|
||||
import fs from "fs/promises"
|
||||
import path from "path"
|
||||
import { fileURLToPath } from "url"
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
const __dirname = path.dirname(__filename)
|
||||
const dir = path.resolve(__dirname, "..")
|
||||
|
||||
if (process.platform !== "win32") {
|
||||
const root = path.join(dir, "node_modules", "node-pty", "prebuilds")
|
||||
const dirs = await fs.readdir(root, { withFileTypes: true }).catch(() => [])
|
||||
const files = dirs.filter((x) => x.isDirectory()).map((x) => path.join(root, x.name, "spawn-helper"))
|
||||
const result = await Promise.all(
|
||||
files.map(async (file) => {
|
||||
const stat = await fs.stat(file).catch(() => undefined)
|
||||
if (!stat) return
|
||||
if ((stat.mode & 0o111) === 0o111) return
|
||||
await fs.chmod(file, stat.mode | 0o755)
|
||||
return file
|
||||
}),
|
||||
)
|
||||
const fixed = result.filter(Boolean)
|
||||
if (fixed.length) {
|
||||
console.log(`fixed node-pty permissions for ${fixed.length} helper${fixed.length === 1 ? "" : "s"}`)
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,7 @@ export namespace TestProfile {
|
||||
"control-plane/workspace.test.ts",
|
||||
],
|
||||
filesystem: [
|
||||
"file/{index,path-traversal,ripgrep,watcher}.test.ts",
|
||||
"filesystem/filesystem.test.ts",
|
||||
"filesystem/*.test.ts",
|
||||
"fixture/fixture.test.ts",
|
||||
"git/*.test.ts",
|
||||
"image/*.test.ts",
|
||||
@@ -33,10 +32,12 @@ export namespace TestProfile {
|
||||
"kilocode/cli/cmd/serve.test.ts",
|
||||
"kilocode/cli/install-artifact.test.ts",
|
||||
"kilocode/config/config.test.ts",
|
||||
"kilocode/core-watcher.test.ts",
|
||||
"kilocode/sandbox/*.test.ts",
|
||||
"kilocode/server/{config-overlay,listener-runtime,tui-config,worktree-list}.test.ts",
|
||||
"kilocode/session-export/{e2e,sequence,worker,workspace-provider}.test.ts",
|
||||
"kilocode/session-export/worker/{storage,zstd}.test.ts",
|
||||
"kilocode/tool/repo_clone.test.ts",
|
||||
"kilocode/worktree*.test.ts",
|
||||
],
|
||||
process: ["session/prompt.test.ts"],
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env bun
|
||||
|
||||
import { Config } from "@/config/config"
|
||||
import { ConfigV1 } from "@opencode-ai/core/v1/config/config"
|
||||
import { Schema } from "effect"
|
||||
import { TuiInfo } from "../src/cli/cmd/tui/config/tui-schema"
|
||||
|
||||
@@ -68,7 +69,7 @@ const configFile = process.argv[2]
|
||||
const tuiFile = process.argv[3]
|
||||
|
||||
console.log(configFile)
|
||||
await Bun.write(configFile, JSON.stringify(generateEffect(Config.Info), null, 2))
|
||||
await Bun.write(configFile, JSON.stringify(generateEffect(ConfigV1.Info), null, 2))
|
||||
|
||||
if (tuiFile) {
|
||||
console.log(tuiFile)
|
||||
|
||||
@@ -32,7 +32,7 @@ Caller-heavy batch, all merged:
|
||||
|
||||
1. `src/config/config.ts`
|
||||
2. `src/provider/provider.ts`
|
||||
3. `src/file/index.ts`
|
||||
3. `../core/src/filesystem.ts`
|
||||
4. `src/lsp/index.ts`
|
||||
5. `src/mcp/index.ts`
|
||||
|
||||
@@ -168,7 +168,7 @@ Usually no.
|
||||
Prefer the direct form when there is only one expression:
|
||||
|
||||
```ts
|
||||
await AppRuntime.runPromise(File.Service.use((svc) => svc.read(path)))
|
||||
await Effect.runPromise(FileSystem.Service.use((svc) => svc.read({ path })))
|
||||
```
|
||||
|
||||
Use `Effect.gen(...)` when the workflow actually needs multiple yielded values or branching.
|
||||
@@ -179,7 +179,7 @@ These were the recurring mistakes and useful corrections from the first two batc
|
||||
|
||||
1. Tests should usually provide the specific service layer, not `AppRuntime`.
|
||||
2. If a test uses `provideTmpdirInstance(...)` and needs child processes, prefer `CrossSpawnSpawner.defaultLayer`.
|
||||
3. Instance-scoped services may need both the service layer and the right instance fixture. `File` tests, for example, needed `provideInstance(...)` plus `File.defaultLayer`.
|
||||
3. Location-scoped services may need both the service layer and the right location fixture. `FileSystem` tests, for example, provide `Location.Service` plus `FileSystem.locationLayer`.
|
||||
4. Do not wrap a single `Service.use(...)` call in `Effect.gen(...)` just to return it. Use the direct form.
|
||||
5. For CLI readability, extract file-local preload helpers when the handler starts doing config load + service load + batched effect fanout inline.
|
||||
6. When rebasing a facade branch after nearby merges, prefer the already-cleaned service/test version over older inline facade-era code.
|
||||
@@ -201,7 +201,7 @@ Most of the original facade-removal backlog is already done. The practical remai
|
||||
- [x] `src/worktree/index.ts` (`Worktree`) - service-local facades removed
|
||||
- [x] `src/plugin/index.ts` (`Plugin`) - service-local facades removed
|
||||
- [x] `src/snapshot/index.ts` (`Snapshot`) - service-local facades removed
|
||||
- [x] `src/file/index.ts` (`File`) - facades removed and merged
|
||||
- [x] `../core/src/filesystem.ts` (`FileSystem`) - legacy opencode service removed
|
||||
- [x] `src/lsp/index.ts` (`LSP`) - facades removed and merged
|
||||
- [x] `src/mcp/index.ts` (`MCP`) - facades removed and merged
|
||||
- [x] `src/config/config.ts` (`Config`) - facades removed and merged
|
||||
|
||||
@@ -70,7 +70,7 @@ mutable `Flag` or late `process.env` reads.
|
||||
Tests should vary behavior with explicit layer variants:
|
||||
|
||||
```ts
|
||||
const it = testEffect(MyService.defaultLayer.pipe(Layer.provide(RuntimeFlags.layer({ experimentalScout: true }))))
|
||||
const it = testEffect(MyService.defaultLayer.pipe(Layer.provide(RuntimeFlags.layer({ experimentalReferences: true }))))
|
||||
```
|
||||
|
||||
Do not mutate `process.env` or `Flag` after services/layers are built.
|
||||
@@ -179,7 +179,7 @@ Intentional boundaries:
|
||||
In effectified code, yield existing services instead of dropping to ad hoc
|
||||
platform APIs.
|
||||
|
||||
- Use `AppFileSystem.Service` instead of raw `fs/promises` for app file IO.
|
||||
- Use `FSUtil.Service` instead of raw `fs/promises` for app file IO.
|
||||
- Use `AppProcess.Service` instead of direct `ChildProcessSpawner.spawn` or
|
||||
legacy process helpers.
|
||||
- Use `HttpClient.HttpClient` instead of raw `fetch` inside Effect code.
|
||||
|
||||
@@ -14,7 +14,7 @@ Small follow-ups that do not fit neatly into the main facade, route, tool, or sc
|
||||
|
||||
- [ ] `config/paths.ts` - split pure helpers from effectful helpers.
|
||||
Keep `fileInDirectory(...)` as a plain function.
|
||||
- [ ] `config/paths.ts` - add a `ConfigPaths.Service` for the effectful operations so callers do not inherit `AppFileSystem.Service` directly.
|
||||
- [ ] `config/paths.ts` - add a `ConfigPaths.Service` for the effectful operations so callers do not inherit `FSUtil.Service` directly.
|
||||
Initial service surface should cover:
|
||||
- `projectFiles(...)`
|
||||
- `directories(...)`
|
||||
|
||||
@@ -33,7 +33,7 @@ genuinely outside `AppLayer`.
|
||||
|
||||
## Platform Edges
|
||||
|
||||
- Use `AppFileSystem.Service` instead of raw filesystem APIs in
|
||||
- Use `FSUtil.Service` instead of raw filesystem APIs in
|
||||
effectified services.
|
||||
- Use `AppProcess.Service` instead of raw process wrappers.
|
||||
- Use `HttpClient.HttpClient` instead of raw `fetch` in Effect code.
|
||||
|
||||
@@ -72,7 +72,7 @@ P6 OA
|
||||
- `PROC` AppProcess migration — prefer `AppProcess.Service` over raw
|
||||
process wrappers.
|
||||
Shrinks: direct spawn callsites and legacy process helpers.
|
||||
- `FS` AppFileSystem migration — prefer `AppFileSystem.Service` over raw
|
||||
- `FS` FSUtil migration — prefer `FSUtil.Service` over raw
|
||||
filesystem APIs.
|
||||
Shrinks: direct `fs` / `Bun.file` service callsites where inappropriate.
|
||||
- `RT` Runtime/facade cleanup — remove service-local `makeRuntime`
|
||||
@@ -172,7 +172,7 @@ Recently completed:
|
||||
- [x] Built-in websearch provider selection uses the same runtime flags as
|
||||
tool visibility.
|
||||
- [x] Removed global default-plugin disabling from test preload.
|
||||
- [x] `RF-1` Scout reads routed through runtime flags (#27318).
|
||||
- [x] `RF-1` Reference reads routed through runtime flags (#27318).
|
||||
- [x] `RF-2` Plan-mode prompt read routed through runtime flags (#27320).
|
||||
- [x] `RF-3` Event-system reads routed through runtime flags (#27323).
|
||||
- [x] `RF-4` Workspaces reads routed through runtime flags for session
|
||||
@@ -229,7 +229,7 @@ Current rules:
|
||||
|
||||
## Lower Priority Tracks
|
||||
|
||||
- `PROC` / `FS` — continue AppProcess and AppFileSystem migrations as
|
||||
- `PROC` / `FS` — continue AppProcess and FSUtil migrations as
|
||||
focused PRs when touching relevant files.
|
||||
- `RT` — remove service-local runtime facades only when they are not an
|
||||
intentional boundary.
|
||||
|
||||
@@ -11,7 +11,7 @@ The current exported tools in `src/tool` all use `Tool.define(...)` with Effect-
|
||||
So the remaining work is no longer "convert tools to Effect at all". The remaining work is mostly:
|
||||
|
||||
1. remove Promise and raw platform bridges inside individual tool bodies
|
||||
2. swap tool internals to Effect-native services like `AppFileSystem`, `HttpClient`, and `ChildProcessSpawner`
|
||||
2. swap tool internals to Effect-native services like `FSUtil`, `HttpClient`, and `ChildProcessSpawner`
|
||||
3. keep tests and callers aligned with `yield* info.init()` and real service graphs
|
||||
|
||||
## Current shape
|
||||
@@ -67,11 +67,11 @@ Most exported tools are already on the intended Effect-native shape. The remaini
|
||||
|
||||
Current spot cleanups worth tracking:
|
||||
|
||||
- [x] `read.ts` — streams through `AppFileSystem.Service.stream` with `Stream.splitLines`; the legacy Node stream / `readline` helper is gone
|
||||
- [x] `read.ts` — streams through `FSUtil.Service.stream` with `Stream.splitLines`; the legacy Node stream / `readline` helper is gone
|
||||
- [ ] `bash.ts` — already uses Effect child-process primitives; only keep tracking shell-specific platform bridges and parser/loading details as they come up
|
||||
- [ ] `webfetch.ts` — already uses `HttpClient`; remaining work is limited to smaller boundary helpers like HTML text extraction
|
||||
- [ ] `file/ripgrep.ts` — adjacent to tool migration; still has raw fs/process usage that affects `grep.ts` and file-search routes
|
||||
- [x] `patch/index.ts` — apply path now returns `Effect` over `AppFileSystem.Service`; the parser and chunk replacer stay pure
|
||||
- [x] `patch/index.ts` — apply path now returns `Effect` over `FSUtil.Service`; the parser and chunk replacer stay pure
|
||||
|
||||
Notable items that are already effectively on the target path and do not need separate migration bullets right now:
|
||||
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import { sqliteTable, text, integer, primaryKey } from "drizzle-orm/sqlite-core"
|
||||
|
||||
import { type AccessToken, type AccountID, type OrgID, type RefreshToken } from "./schema"
|
||||
import { Timestamps } from "../storage/schema.sql"
|
||||
|
||||
export const AccountTable = sqliteTable("account", {
|
||||
id: text().$type<AccountID>().primaryKey(),
|
||||
email: text().notNull(),
|
||||
url: text().notNull(),
|
||||
access_token: text().$type<AccessToken>().notNull(),
|
||||
refresh_token: text().$type<RefreshToken>().notNull(),
|
||||
token_expiry: integer(),
|
||||
...Timestamps,
|
||||
})
|
||||
|
||||
export const AccountStateTable = sqliteTable("account_state", {
|
||||
id: integer().primaryKey(),
|
||||
active_account_id: text()
|
||||
.$type<AccountID>()
|
||||
.references(() => AccountTable.id, { onDelete: "set null" }),
|
||||
active_org_id: text().$type<OrgID>(),
|
||||
})
|
||||
|
||||
// LEGACY
|
||||
export const ControlAccountTable = sqliteTable(
|
||||
"control_account",
|
||||
{
|
||||
email: text().notNull(),
|
||||
url: text().notNull(),
|
||||
access_token: text().$type<AccessToken>().notNull(),
|
||||
refresh_token: text().$type<RefreshToken>().notNull(),
|
||||
token_expiry: integer(),
|
||||
active: integer({ mode: "boolean" })
|
||||
.notNull()
|
||||
.$default(() => false),
|
||||
...Timestamps,
|
||||
},
|
||||
(table) => [primaryKey({ columns: [table.email, table.url] })],
|
||||
)
|
||||
@@ -454,6 +454,6 @@ export const layer: Layer.Layer<Service, never, AccountRepo.Service | HttpClient
|
||||
}),
|
||||
)
|
||||
|
||||
export const defaultLayer = layer.pipe(Layer.provide(AccountRepo.layer), Layer.provide(FetchHttpClient.layer))
|
||||
export const defaultLayer = layer.pipe(Layer.provide(AccountRepo.defaultLayer), Layer.provide(FetchHttpClient.layer))
|
||||
|
||||
export * as Account from "./account"
|
||||
|
||||
@@ -2,16 +2,13 @@ import { eq } from "drizzle-orm"
|
||||
import { serviceUse } from "@opencode-ai/core/effect/service-use"
|
||||
import { Effect, Layer, Option, Schema, Context } from "effect"
|
||||
|
||||
import { Database } from "@/storage/db"
|
||||
import { AccountStateTable, AccountTable } from "./account.sql"
|
||||
import { Database } from "@opencode-ai/core/database/database"
|
||||
import { AccountStateTable, AccountTable } from "@opencode-ai/core/account/sql"
|
||||
import { AccessToken, AccountID, AccountRepoError, Info, OrgID, RefreshToken } from "./schema"
|
||||
import { normalizeServerUrl } from "./url"
|
||||
|
||||
export type AccountRow = (typeof AccountTable)["$inferSelect"]
|
||||
|
||||
type DbClient = Parameters<typeof Database.use>[0] extends (db: infer T) => unknown ? T : never
|
||||
type DbTransactionCallback<A> = Parameters<typeof Database.transaction<A>>[0]
|
||||
|
||||
const ACCOUNT_STATE_ID = 1
|
||||
|
||||
export interface Interface {
|
||||
@@ -41,32 +38,24 @@ export class Service extends Context.Service<Service, Interface>()("@opencode/Ac
|
||||
|
||||
export const use = serviceUse(Service)
|
||||
|
||||
export const layer: Layer.Layer<Service> = Layer.effect(
|
||||
export const layer = Layer.effect(
|
||||
Service,
|
||||
Effect.gen(function* () {
|
||||
const { db } = yield* Database.Service
|
||||
const decode = Schema.decodeUnknownSync(Info)
|
||||
|
||||
const query = <A>(f: DbTransactionCallback<A>) =>
|
||||
Effect.try({
|
||||
try: () => Database.use(f),
|
||||
catch: (cause) => new AccountRepoError({ message: "Database operation failed", cause }),
|
||||
})
|
||||
const query = <A, E>(effect: Effect.Effect<A, E>) =>
|
||||
effect.pipe(Effect.mapError((cause) => new AccountRepoError({ message: "Database operation failed", cause })))
|
||||
|
||||
const tx = <A>(f: DbTransactionCallback<A>) =>
|
||||
Effect.try({
|
||||
try: () => Database.transaction(f),
|
||||
catch: (cause) => new AccountRepoError({ message: "Database operation failed", cause }),
|
||||
})
|
||||
|
||||
const current = (db: DbClient) => {
|
||||
const state = db.select().from(AccountStateTable).where(eq(AccountStateTable.id, ACCOUNT_STATE_ID)).get()
|
||||
const current = Effect.fnUntraced(function* () {
|
||||
const state = yield* db.select().from(AccountStateTable).where(eq(AccountStateTable.id, ACCOUNT_STATE_ID)).get()
|
||||
if (!state?.active_account_id) return
|
||||
const account = db.select().from(AccountTable).where(eq(AccountTable.id, state.active_account_id)).get()
|
||||
const account = yield* db.select().from(AccountTable).where(eq(AccountTable.id, state.active_account_id)).get()
|
||||
if (!account) return
|
||||
return { ...account, active_org_id: state.active_org_id ?? null }
|
||||
}
|
||||
})
|
||||
|
||||
const state = (db: DbClient, accountID: AccountID, orgID: Option.Option<OrgID>) => {
|
||||
const state = (accountID: AccountID, orgID: Option.Option<OrgID>) => {
|
||||
const id = Option.getOrNull(orgID)
|
||||
return db
|
||||
.insert(AccountStateTable)
|
||||
@@ -79,41 +68,46 @@ export const layer: Layer.Layer<Service> = Layer.effect(
|
||||
}
|
||||
|
||||
const active = Effect.fn("AccountRepo.active")(() =>
|
||||
query((db) => current(db)).pipe(Effect.map((row) => (row ? Option.some(decode(row)) : Option.none()))),
|
||||
query(current()).pipe(Effect.map((row) => (row ? Option.some(decode(row)) : Option.none()))),
|
||||
)
|
||||
|
||||
const list = Effect.fn("AccountRepo.list")(() =>
|
||||
query((db) =>
|
||||
query(
|
||||
db
|
||||
.select()
|
||||
.from(AccountTable)
|
||||
.all()
|
||||
.map((row: AccountRow) => decode({ ...row, active_org_id: null })),
|
||||
.pipe(Effect.map((rows) => rows.map((row: AccountRow) => decode({ ...row, active_org_id: null })))),
|
||||
),
|
||||
)
|
||||
|
||||
const remove = Effect.fn("AccountRepo.remove")((accountID: AccountID) =>
|
||||
tx((db) => {
|
||||
db.update(AccountStateTable)
|
||||
.set({ active_account_id: null, active_org_id: null })
|
||||
.where(eq(AccountStateTable.active_account_id, accountID))
|
||||
.run()
|
||||
db.delete(AccountTable).where(eq(AccountTable.id, accountID)).run()
|
||||
}).pipe(Effect.asVoid),
|
||||
query(
|
||||
db.transaction((tx) =>
|
||||
Effect.gen(function* () {
|
||||
yield* tx
|
||||
.update(AccountStateTable)
|
||||
.set({ active_account_id: null, active_org_id: null })
|
||||
.where(eq(AccountStateTable.active_account_id, accountID))
|
||||
.run()
|
||||
yield* tx.delete(AccountTable).where(eq(AccountTable.id, accountID)).run()
|
||||
}),
|
||||
),
|
||||
).pipe(Effect.asVoid),
|
||||
)
|
||||
|
||||
const use = Effect.fn("AccountRepo.use")((accountID: AccountID, orgID: Option.Option<OrgID>) =>
|
||||
query((db) => state(db, accountID, orgID)).pipe(Effect.asVoid),
|
||||
query(state(accountID, orgID)).pipe(Effect.asVoid),
|
||||
)
|
||||
|
||||
const getRow = Effect.fn("AccountRepo.getRow")((accountID: AccountID) =>
|
||||
query((db) => db.select().from(AccountTable).where(eq(AccountTable.id, accountID)).get()).pipe(
|
||||
query(db.select().from(AccountTable).where(eq(AccountTable.id, accountID)).get()).pipe(
|
||||
Effect.map(Option.fromNullishOr),
|
||||
),
|
||||
)
|
||||
|
||||
const persistToken = Effect.fn("AccountRepo.persistToken")((input) =>
|
||||
query((db) =>
|
||||
query(
|
||||
db
|
||||
.update(AccountTable)
|
||||
.set({
|
||||
@@ -127,31 +121,36 @@ export const layer: Layer.Layer<Service> = Layer.effect(
|
||||
)
|
||||
|
||||
const persistAccount = Effect.fn("AccountRepo.persistAccount")((input) =>
|
||||
tx((db) => {
|
||||
const url = normalizeServerUrl(input.url)
|
||||
query(
|
||||
db.transaction((tx) =>
|
||||
Effect.gen(function* () {
|
||||
const url = normalizeServerUrl(input.url)
|
||||
|
||||
db.insert(AccountTable)
|
||||
.values({
|
||||
id: input.id,
|
||||
email: input.email,
|
||||
url,
|
||||
access_token: input.accessToken,
|
||||
refresh_token: input.refreshToken,
|
||||
token_expiry: input.expiry,
|
||||
})
|
||||
.onConflictDoUpdate({
|
||||
target: AccountTable.id,
|
||||
set: {
|
||||
email: input.email,
|
||||
url,
|
||||
access_token: input.accessToken,
|
||||
refresh_token: input.refreshToken,
|
||||
token_expiry: input.expiry,
|
||||
},
|
||||
})
|
||||
.run()
|
||||
void state(db, input.id, input.orgID)
|
||||
}).pipe(Effect.asVoid),
|
||||
yield* tx
|
||||
.insert(AccountTable)
|
||||
.values({
|
||||
id: input.id,
|
||||
email: input.email,
|
||||
url,
|
||||
access_token: input.accessToken,
|
||||
refresh_token: input.refreshToken,
|
||||
token_expiry: input.expiry,
|
||||
})
|
||||
.onConflictDoUpdate({
|
||||
target: AccountTable.id,
|
||||
set: {
|
||||
email: input.email,
|
||||
url,
|
||||
access_token: input.accessToken,
|
||||
refresh_token: input.refreshToken,
|
||||
token_expiry: input.expiry,
|
||||
},
|
||||
})
|
||||
.run()
|
||||
yield* state(input.id, input.orgID)
|
||||
}),
|
||||
),
|
||||
).pipe(Effect.asVoid),
|
||||
)
|
||||
|
||||
return Service.of({
|
||||
@@ -166,4 +165,6 @@ export const layer: Layer.Layer<Service> = Layer.effect(
|
||||
}),
|
||||
)
|
||||
|
||||
export const defaultLayer = layer.pipe(Layer.provide(Database.defaultLayer))
|
||||
|
||||
export * as AccountRepo from "./repo"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { ContentBlock, ContentChunk, ResourceLink, Role } from "@agentclientprotocol/sdk"
|
||||
import path from "node:path"
|
||||
import { pathToFileURL } from "node:url"
|
||||
import type { MessageV2 } from "@/session/message-v2"
|
||||
import { SessionV1 } from "@opencode-ai/core/v1/session"
|
||||
|
||||
export type PromptPart = MessageV2.TextPartInput | MessageV2.FilePartInput
|
||||
export type PromptPart = SessionV1.TextPartInput | SessionV1.FilePartInput
|
||||
|
||||
export type ReplayPart =
|
||||
| {
|
||||
@@ -141,7 +141,7 @@ function uriToFilePart(
|
||||
uri: string,
|
||||
mime: string,
|
||||
filename?: string,
|
||||
): MessageV2.FilePartInput | MessageV2.TextPartInput {
|
||||
): SessionV1.FilePartInput | SessionV1.TextPartInput {
|
||||
try {
|
||||
if (uri.startsWith("file://")) {
|
||||
return {
|
||||
|
||||
@@ -2,15 +2,16 @@ import { Agent } from "@/agent/agent"
|
||||
import { Command } from "@/command"
|
||||
import { InstanceRef } from "@/effect/instance-ref"
|
||||
import { InstanceStore } from "@/project/instance-store"
|
||||
import { ModelID, ProviderID } from "@/provider/schema"
|
||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import { ModelV2 } from "@opencode-ai/core/model"
|
||||
import { Provider } from "@/provider/provider"
|
||||
import { Context, Effect, Layer, SynchronizedRef } from "effect"
|
||||
import type * as ACPError from "./error"
|
||||
|
||||
export type ModelOption = {
|
||||
readonly providerID: ProviderID
|
||||
readonly providerID: ProviderV2.ID
|
||||
readonly providerName: string
|
||||
readonly modelID: ModelID
|
||||
readonly modelID: ModelV2.ID
|
||||
readonly modelName: string
|
||||
}
|
||||
|
||||
@@ -23,13 +24,13 @@ export type ModeOption = {
|
||||
export type ModelVariants = NonNullable<Provider.Model["variants"]>
|
||||
|
||||
export type DefaultModel = {
|
||||
readonly providerID: ProviderID
|
||||
readonly modelID: ModelID
|
||||
readonly providerID: ProviderV2.ID
|
||||
readonly modelID: ModelV2.ID
|
||||
}
|
||||
|
||||
export type Snapshot = {
|
||||
readonly directory: string
|
||||
readonly providers: Record<ProviderID, Provider.Info>
|
||||
readonly providers: Record<ProviderV2.ID, Provider.Info>
|
||||
readonly modelOptions: readonly ModelOption[]
|
||||
readonly variantsByModel: Readonly<Record<string, ModelVariants>>
|
||||
readonly availableModes: readonly ModeOption[]
|
||||
@@ -58,7 +59,7 @@ export const variants = (snapshot: Snapshot, model: DefaultModel) => snapshot.va
|
||||
|
||||
export const build = (input: {
|
||||
readonly directory: string
|
||||
readonly providers: Record<ProviderID, Provider.Info>
|
||||
readonly providers: Record<ProviderV2.ID, Provider.Info>
|
||||
readonly modes: readonly ModeOption[]
|
||||
readonly defaultModeID: string
|
||||
readonly commands: readonly Command.Info[]
|
||||
|
||||
@@ -12,6 +12,7 @@ import type {
|
||||
import { Effect } from "effect"
|
||||
import { ACPSession } from "./session"
|
||||
import { ACPPermission } from "./permission"
|
||||
import { partsToContentChunks, type ReplayPart } from "./content"
|
||||
import {
|
||||
duplicateRunningToolUpdate,
|
||||
errorToolUpdate,
|
||||
@@ -87,7 +88,31 @@ export class Subscription {
|
||||
await this.recordFetchedPart(message.info.sessionID, message, part)
|
||||
if (part.type === "tool") {
|
||||
await this.handleToolPart(message.info.sessionID, part)
|
||||
continue
|
||||
}
|
||||
await this.replayContentPart(message, part)
|
||||
}
|
||||
}
|
||||
|
||||
private async replayContentPart(message: SessionMessageResponse, part: Part) {
|
||||
if (part.type !== "text" && part.type !== "file" && part.type !== "reasoning") return
|
||||
|
||||
const sessionUpdate =
|
||||
part.type === "reasoning"
|
||||
? "agent_thought_chunk"
|
||||
: message.info.role === "user"
|
||||
? "user_message_chunk"
|
||||
: "agent_message_chunk"
|
||||
|
||||
for (const chunk of partsToContentChunks([part as ReplayPart])) {
|
||||
await this.input.connection.sessionUpdate({
|
||||
sessionId: message.info.sessionID,
|
||||
update: {
|
||||
sessionUpdate,
|
||||
messageId: message.info.id,
|
||||
...chunk,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,8 @@ import { ACPEvent } from "./event"
|
||||
import { ACPSession } from "./session"
|
||||
import { UsageService } from "./usage"
|
||||
import { ACPProfile } from "./profile"
|
||||
import { ModelID, ProviderID } from "@/provider/schema"
|
||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import { ModelV2 } from "@opencode-ai/core/model"
|
||||
import { Provider } from "@/provider/provider"
|
||||
import type { Command } from "@/command"
|
||||
|
||||
@@ -215,11 +216,7 @@ export function make(input: {
|
||||
"session",
|
||||
)
|
||||
const messages = yield* request(
|
||||
() =>
|
||||
input.sdk.session.messages(
|
||||
{ directory: params.cwd, sessionID: params.sessionId, limit: 100 },
|
||||
{ throwOnError: true },
|
||||
),
|
||||
() => input.sdk.session.messages({ directory: params.cwd, sessionID: params.sessionId }, { throwOnError: true }),
|
||||
"session",
|
||||
)
|
||||
const restored = restoreFromMessages(messages.map((item) => item.info))
|
||||
@@ -332,25 +329,34 @@ export function make(input: {
|
||||
}
|
||||
})
|
||||
|
||||
const abortBackingSession = Effect.fn("ACP.abortBackingSession")(function* (current: ACPSession.Info) {
|
||||
yield* request(
|
||||
() => input.sdk.session.abort({ directory: current.cwd, sessionID: current.id }, { throwOnError: true }),
|
||||
"session",
|
||||
).pipe(
|
||||
Effect.catch((error) =>
|
||||
Effect.sync(() => {
|
||||
log.error("failed to abort ACP backing session", { error, sessionID: current.id })
|
||||
}),
|
||||
),
|
||||
)
|
||||
})
|
||||
|
||||
const closeSession = Effect.fn("ACP.closeSession")(function* (params: CloseSessionRequest) {
|
||||
const removed = yield* session.remove(params.sessionId)
|
||||
registeredMcp.delete(params.sessionId)
|
||||
sessionSnapshots.delete(params.sessionId)
|
||||
if (!removed) return {}
|
||||
|
||||
yield* request(
|
||||
() => input.sdk.session.abort({ directory: removed.cwd, sessionID: params.sessionId }, { throwOnError: true }),
|
||||
"session",
|
||||
).pipe(
|
||||
Effect.catch((error) =>
|
||||
Effect.sync(() => {
|
||||
log.error("failed to abort session while closing ACP session", { error, sessionID: params.sessionId })
|
||||
}),
|
||||
),
|
||||
)
|
||||
yield* abortBackingSession(removed)
|
||||
return {}
|
||||
})
|
||||
|
||||
const cancel = Effect.fn("ACP.cancel")(function* (params: CancelNotification) {
|
||||
const current = yield* session.get(params.sessionId)
|
||||
yield* abortBackingSession(current)
|
||||
})
|
||||
|
||||
const forkSession = Effect.fn("ACP.forkSession")(function* (params: ForkSessionRequest) {
|
||||
const snapshot = yield* directorySnapshot(params.cwd)
|
||||
const forked = yield* request(
|
||||
@@ -565,9 +571,7 @@ export function make(input: {
|
||||
yield* sendUsageUpdate(input.usage, input.sdk, input.connection, current.id, current.cwd)
|
||||
return promptResponse(undefined, params.messageId)
|
||||
}),
|
||||
cancel: Effect.fn("ACP.cancel")(function* (_input: CancelNotification) {
|
||||
return yield* new ACPError.UnsupportedOperationError({ method: "session/cancel" })
|
||||
}),
|
||||
cancel,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -605,7 +609,7 @@ function makeUsageService(sdk: KiloClient) {
|
||||
.then((response) => {
|
||||
const providers = Object.fromEntries(
|
||||
(response.data?.providers ?? []).map((provider) => [provider.id, provider]),
|
||||
) as Record<ProviderID, Provider.Info>
|
||||
) as Record<ProviderV2.ID, Provider.Info>
|
||||
return UsageService.findContextLimit(providers, params.providerID, params.modelID)
|
||||
})
|
||||
.catch((error: unknown) => {
|
||||
@@ -644,8 +648,8 @@ function makeUsageService(sdk: KiloClient) {
|
||||
|
||||
const size = yield* contextLimit({
|
||||
directory: params.directory,
|
||||
providerID: ProviderID.make(message.providerID),
|
||||
modelID: ModelID.make(message.modelID),
|
||||
providerID: ProviderV2.ID.make(message.providerID),
|
||||
modelID: ModelV2.ID.make(message.modelID),
|
||||
})
|
||||
if (!size) return
|
||||
|
||||
@@ -747,7 +751,7 @@ async function loadDirectorySnapshot(sdk: KiloClient, directory: string) {
|
||||
const commandsData = commandsResponse.data!
|
||||
const skills = skillsResponse.data!
|
||||
const providers = Object.fromEntries(providersData.providers.map((provider) => [provider.id, provider])) as Record<
|
||||
ProviderID,
|
||||
ProviderV2.ID,
|
||||
Provider.Info
|
||||
>
|
||||
const defaultModelStarted = performance.now()
|
||||
@@ -786,7 +790,7 @@ async function loadDirectorySnapshot(sdk: KiloClient, directory: string) {
|
||||
|
||||
function defaultModelFromConfig(
|
||||
configuredModel: string | undefined,
|
||||
providers: Record<ProviderID, Provider.Info>,
|
||||
providers: Record<ProviderV2.ID, Provider.Info>,
|
||||
): Directory.DefaultModel | undefined {
|
||||
const configured = configuredModel ? Provider.parseModel(configuredModel) : undefined
|
||||
if (configured && providers[configured.providerID]?.models[configured.modelID]) return configured
|
||||
@@ -794,7 +798,7 @@ function defaultModelFromConfig(
|
||||
// First-session ACP startup must not scan historical sessions just to infer
|
||||
// a default. Configured model, opencode provider, then sorted best model keep
|
||||
// the protocol response deterministic without extra session/message reads.
|
||||
const kiloProvider = providers[ProviderID.kilo] // kilocode_change
|
||||
const kiloProvider = providers[ProviderV2.ID.make("kilo")] // kilocode_change
|
||||
const kiloModel = kiloProvider ? Provider.sort(Object.values(kiloProvider.models))[0] : undefined // kilocode_change
|
||||
if (kiloProvider && kiloModel) return { providerID: kiloProvider.id, modelID: kiloModel.id } // kilocode_change
|
||||
|
||||
@@ -807,7 +811,7 @@ function selectDefaultModel(snapshot: Directory.Snapshot) {
|
||||
if (snapshot.defaultModel) return snapshot.defaultModel
|
||||
const model = snapshot.modelOptions[0]
|
||||
if (model) return { providerID: model.providerID, modelID: model.modelID }
|
||||
return { providerID: "unknown" as ProviderID, modelID: "unknown" as ModelID }
|
||||
return { providerID: "unknown" as ProviderV2.ID, modelID: "unknown" as ModelV2.ID }
|
||||
}
|
||||
|
||||
function detectSlashCommand(parts: ReturnType<typeof promptContentToParts>) {
|
||||
@@ -866,8 +870,8 @@ function configOptions(snapshot: Directory.Snapshot, session: ConfigState) {
|
||||
|
||||
function parseSelectedModel(snapshot: Directory.Snapshot, modelId: string) {
|
||||
const selected = parseModelSelection(modelId, Object.values(snapshot.providers))
|
||||
const provider = snapshot.providers[ProviderID.make(selected.model.providerID)]
|
||||
const model = provider?.models[ModelID.make(selected.model.modelID)]
|
||||
const provider = snapshot.providers[ProviderV2.ID.make(selected.model.providerID)]
|
||||
const model = provider?.models[ModelV2.ID.make(selected.model.modelID)]
|
||||
if (!model) {
|
||||
return Effect.fail(
|
||||
new ACPError.InvalidModelError({
|
||||
@@ -995,7 +999,7 @@ function restoreFromMessages(messages: readonly MessageInfo[]) {
|
||||
)
|
||||
if (user?.model?.providerID && user.model.modelID) {
|
||||
return {
|
||||
model: { providerID: user.model.providerID as ProviderID, modelID: user.model.modelID as ModelID },
|
||||
model: { providerID: user.model.providerID as ProviderV2.ID, modelID: user.model.modelID as ModelV2.ID },
|
||||
variant: user.model.variant,
|
||||
modeId: user.agent,
|
||||
}
|
||||
@@ -1004,7 +1008,7 @@ function restoreFromMessages(messages: readonly MessageInfo[]) {
|
||||
const assistant = messages.findLast((message) => message.providerID && message.modelID)
|
||||
if (assistant?.providerID && assistant.modelID) {
|
||||
return {
|
||||
model: { providerID: assistant.providerID as ProviderID, modelID: assistant.modelID as ModelID },
|
||||
model: { providerID: assistant.providerID as ProviderV2.ID, modelID: assistant.modelID as ModelV2.ID },
|
||||
variant: assistant.variant,
|
||||
modeId: assistant.mode ?? assistant.agent,
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import type { McpServer } from "@agentclientprotocol/sdk"
|
||||
import type { Message, Part } from "@kilocode/sdk/v2"
|
||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import { ModelV2 } from "@opencode-ai/core/model"
|
||||
import { Context, Effect, Layer, Ref } from "effect"
|
||||
import type { ModelID, ProviderID } from "../provider/schema"
|
||||
import * as ACPError from "./error"
|
||||
|
||||
export type SelectedModel = {
|
||||
providerID: ProviderID
|
||||
modelID: ModelID
|
||||
providerID: ProviderV2.ID
|
||||
modelID: ModelV2.ID
|
||||
}
|
||||
|
||||
export type KnownMessagePartMetadata = {
|
||||
|
||||
@@ -46,14 +46,13 @@ export function toToolKind(toolName: string): ToolKind {
|
||||
return "fetch"
|
||||
|
||||
case "edit":
|
||||
case "apply_patch":
|
||||
case "patch":
|
||||
case "write":
|
||||
return "edit"
|
||||
|
||||
case "grep":
|
||||
case "glob":
|
||||
case "repo_clone":
|
||||
case "repo_overview":
|
||||
case "context":
|
||||
case "context7_resolve_library_id":
|
||||
case "context7_get_library_docs":
|
||||
@@ -62,6 +61,9 @@ export function toToolKind(toolName: string): ToolKind {
|
||||
case "read":
|
||||
return "read"
|
||||
|
||||
case "task":
|
||||
return "think"
|
||||
|
||||
default:
|
||||
return "other"
|
||||
}
|
||||
@@ -76,10 +78,11 @@ export function toLocations(toolName: string, input: ToolInput): ToolCallLocatio
|
||||
case "write":
|
||||
return locationFrom(input.filePath ?? input.filepath)
|
||||
|
||||
case "external_directory":
|
||||
return locationFrom(input.filePath ?? input.filepath, input.parentDir, input.directories)
|
||||
|
||||
case "grep":
|
||||
case "glob":
|
||||
case "repo_clone":
|
||||
case "repo_overview":
|
||||
case "context":
|
||||
case "context7_resolve_library_id":
|
||||
case "context7_get_library_docs":
|
||||
@@ -95,12 +98,14 @@ export function toLocations(toolName: string, input: ToolInput): ToolCallLocatio
|
||||
}
|
||||
|
||||
export function completedToolContent(toolName: string, state: CompletedToolState): ToolCallContent[] {
|
||||
const text =
|
||||
toolName.toLocaleLowerCase() === "read" ? (readDisplayText(state.metadata) ?? state.output) : state.output
|
||||
const content: ToolCallContent[] = [
|
||||
{
|
||||
type: "content",
|
||||
content: {
|
||||
type: "text",
|
||||
text: state.output,
|
||||
text,
|
||||
},
|
||||
},
|
||||
]
|
||||
@@ -255,9 +260,19 @@ export const buildDuplicateRunningToolUpdate = duplicateRunningToolUpdate
|
||||
export const buildCompletedToolUpdate = completedToolUpdate
|
||||
export const buildErrorToolUpdate = errorToolUpdate
|
||||
|
||||
function locationFrom(value: unknown): ToolCallLocation[] {
|
||||
const path = stringValue(value)
|
||||
return path ? [{ path }] : []
|
||||
function locationFrom(...values: unknown[]): ToolCallLocation[] {
|
||||
return Array.from(
|
||||
new Set(
|
||||
values.flatMap((value): string[] => {
|
||||
if (Array.isArray(value)) {
|
||||
return value.filter((item): item is string => typeof item === "string" && item.length > 0)
|
||||
}
|
||||
const path = stringValue(value)
|
||||
return path ? [path] : []
|
||||
}),
|
||||
),
|
||||
(path) => ({ path }),
|
||||
)
|
||||
}
|
||||
|
||||
function diffContent(input: ToolInput): ToolCallContent[] {
|
||||
@@ -275,6 +290,18 @@ function diffContent(input: ToolInput): ToolCallContent[] {
|
||||
]
|
||||
}
|
||||
|
||||
function readDisplayText(metadata: unknown) {
|
||||
if (!metadata || typeof metadata !== "object") return undefined
|
||||
const display = (metadata as Record<string, unknown>).display
|
||||
if (!display || typeof display !== "object") return undefined
|
||||
const info = display as Record<string, unknown>
|
||||
if (info.type === "file") return stringValue(info.text)
|
||||
if (info.type === "directory" && Array.isArray(info.entries)) {
|
||||
return info.entries.filter((item): item is string => typeof item === "string").join("\n")
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
function dataUrlImage(attachment: ToolAttachment) {
|
||||
const match = stringValue(attachment.url)?.match(/^data:([^;,]+)(?:;[^,]*)*;base64,(.*)$/)
|
||||
const mime = match?.[1] ?? stringValue(attachment.mime)
|
||||
|
||||
@@ -3,7 +3,8 @@ import * as Log from "@opencode-ai/core/util/log"
|
||||
import type { AssistantMessage as OpenCodeAssistantMessage, Message } from "@kilocode/sdk/v2"
|
||||
import { InstanceRef } from "@/effect/instance-ref"
|
||||
import { InstanceStore } from "@/project/instance-store"
|
||||
import { ModelID, ProviderID } from "@/provider/schema"
|
||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import { ModelV2 } from "@opencode-ai/core/model"
|
||||
import { Provider } from "@/provider/provider"
|
||||
import { Context, Effect, Layer, SynchronizedRef } from "effect"
|
||||
|
||||
@@ -38,7 +39,7 @@ export interface MessageLoaderInterface {
|
||||
}
|
||||
|
||||
export interface ContextLimitLoaderInterface {
|
||||
readonly providers: (directory: string) => Effect.Effect<Record<ProviderID, Provider.Info>, unknown>
|
||||
readonly providers: (directory: string) => Effect.Effect<Record<ProviderV2.ID, Provider.Info>, unknown>
|
||||
}
|
||||
|
||||
export type UsageConnection = Pick<AgentSideConnection, "sessionUpdate">
|
||||
@@ -49,8 +50,8 @@ export interface Interface {
|
||||
readonly totalSessionCost: (messages: readonly SessionMessage[]) => number
|
||||
readonly contextLimit: (input: {
|
||||
readonly directory: string
|
||||
readonly providerID: ProviderID
|
||||
readonly modelID: ModelID
|
||||
readonly providerID: ProviderV2.ID
|
||||
readonly modelID: ModelV2.ID
|
||||
}) => Effect.Effect<number | undefined>
|
||||
readonly sendUpdate: (input: {
|
||||
readonly connection: UsageConnection
|
||||
@@ -110,9 +111,9 @@ export function totalSessionCost(messages: readonly SessionMessage[]): number {
|
||||
}
|
||||
|
||||
export function findContextLimit(
|
||||
providers: Record<ProviderID, Provider.Info>,
|
||||
providerID: ProviderID,
|
||||
modelID: ModelID,
|
||||
providers: Record<ProviderV2.ID, Provider.Info>,
|
||||
providerID: ProviderV2.ID,
|
||||
modelID: ModelV2.ID,
|
||||
): number | undefined {
|
||||
return providers[providerID]?.models[modelID]?.limit.context
|
||||
}
|
||||
@@ -143,8 +144,8 @@ export const layer = Layer.effect(
|
||||
|
||||
const cachedLimit = Effect.fnUntraced(function* (input: {
|
||||
readonly directory: string
|
||||
readonly providerID: ProviderID
|
||||
readonly modelID: ModelID
|
||||
readonly providerID: ProviderV2.ID
|
||||
readonly modelID: ModelV2.ID
|
||||
}) {
|
||||
return yield* SynchronizedRef.modifyEffect(
|
||||
limits,
|
||||
@@ -170,8 +171,8 @@ export const layer = Layer.effect(
|
||||
|
||||
const contextLimit = Effect.fn("ACPUsage.contextLimit")(function* (input: {
|
||||
readonly directory: string
|
||||
readonly providerID: ProviderID
|
||||
readonly modelID: ModelID
|
||||
readonly providerID: ProviderV2.ID
|
||||
readonly modelID: ModelV2.ID
|
||||
}) {
|
||||
return yield* yield* cachedLimit(input)
|
||||
})
|
||||
@@ -197,8 +198,8 @@ export const layer = Layer.effect(
|
||||
|
||||
const size = yield* contextLimit({
|
||||
directory: input.directory,
|
||||
providerID: ProviderID.make(message.providerID),
|
||||
modelID: ModelID.make(message.modelID),
|
||||
providerID: ProviderV2.ID.make(message.providerID),
|
||||
modelID: ModelV2.ID.make(message.modelID),
|
||||
})
|
||||
if (!size) return
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { PermissionV1 } from "@opencode-ai/core/v1/permission"
|
||||
import { Config } from "@/config/config"
|
||||
import { serviceUse } from "@opencode-ai/core/effect/service-use"
|
||||
import { Provider } from "@/provider/provider"
|
||||
import { ModelID, ProviderID } from "../provider/schema"
|
||||
|
||||
import { generateObject, streamObject, type ModelMessage } from "ai"
|
||||
import { Truncate } from "@/tool/truncate"
|
||||
import { Auth } from "../auth"
|
||||
@@ -10,7 +11,7 @@ import { ProviderTransform } from "@/provider/transform"
|
||||
import PROMPT_GENERATE from "./generate.txt"
|
||||
import PROMPT_COMPACTION from "./prompt/compaction.txt"
|
||||
import PROMPT_EXPLORE from "./prompt/explore.txt"
|
||||
import PROMPT_SCOUT from "./prompt/scout.txt"
|
||||
import PROMPT_SCOUT from "@/kilocode/agent/scout.txt" // kilocode_change
|
||||
import PROMPT_SUMMARY from "./prompt/summary.txt"
|
||||
import PROMPT_TITLE from "./prompt/title.txt"
|
||||
import { Permission } from "@/permission"
|
||||
@@ -22,6 +23,8 @@ import { Plugin } from "@/plugin"
|
||||
import { Skill } from "../skill"
|
||||
import { Effect, Context, Layer, Schema } from "effect"
|
||||
import { InstanceState } from "@/effect/instance-state"
|
||||
import * as Option from "effect/Option"
|
||||
import * as OtelTracer from "@effect/opentelemetry/Tracer"
|
||||
import { type DeepMutable } from "@opencode-ai/core/schema"
|
||||
import * as KiloAgent from "@/kilocode/agent" // kilocode_change
|
||||
import { RuntimeFlags } from "@/effect/runtime-flags"
|
||||
@@ -29,6 +32,8 @@ import { Reference } from "@/reference/reference" // kilocode_change
|
||||
import { ConfigReference } from "@/config/reference" // kilocode_change
|
||||
import * as AgentRequirements from "@/kilocode/agent-requirements" // kilocode_change
|
||||
import { MCP } from "@/mcp" // kilocode_change
|
||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import { ModelV2 } from "@opencode-ai/core/model"
|
||||
|
||||
export type RequirementBlockedError = InstanceType<typeof AgentRequirements.BlockedError> // kilocode_change
|
||||
|
||||
@@ -44,11 +49,11 @@ export const Info = Schema.Struct({
|
||||
topP: Schema.optional(Schema.Finite),
|
||||
temperature: Schema.optional(Schema.Finite),
|
||||
color: Schema.optional(Schema.String),
|
||||
permission: Permission.Ruleset,
|
||||
permission: PermissionV1.Ruleset,
|
||||
model: Schema.optional(
|
||||
Schema.Struct({
|
||||
modelID: ModelID,
|
||||
providerID: ProviderID,
|
||||
modelID: ModelV2.ID,
|
||||
providerID: ProviderV2.ID,
|
||||
}),
|
||||
),
|
||||
variant: Schema.optional(Schema.String),
|
||||
@@ -74,7 +79,7 @@ export interface Interface {
|
||||
readonly guardRequirements: (agent: Info) => Effect.Effect<void, RequirementBlockedError> // kilocode_change
|
||||
readonly generate: (input: {
|
||||
description: string
|
||||
model?: { providerID: ProviderID; modelID: ModelID }
|
||||
model?: { providerID: ProviderV2.ID; modelID: ModelV2.ID }
|
||||
}) => Effect.Effect<
|
||||
{
|
||||
identifier: string
|
||||
@@ -100,7 +105,7 @@ export const layer = Layer.effect(
|
||||
const skill = yield* Skill.Service
|
||||
const mcp = yield* MCP.Service // kilocode_change
|
||||
const provider = yield* Provider.Service
|
||||
const flags = yield* RuntimeFlags.Service
|
||||
const flags = yield* RuntimeFlags.Service // kilocode_change
|
||||
|
||||
const state = yield* InstanceState.make<State>(
|
||||
Effect.fn("Agent.state")(function* (ctx) {
|
||||
@@ -133,8 +138,8 @@ export const layer = Layer.effect(
|
||||
interactive_terminal: "deny", // kilocode_change - human-driven tools are primary-agent only
|
||||
plan_enter: "deny",
|
||||
plan_exit: "deny",
|
||||
repo_clone: "deny",
|
||||
repo_overview: "deny",
|
||||
repo_clone: "deny", // kilocode_change
|
||||
repo_overview: "deny", // kilocode_change
|
||||
// mirrors github.com/github/gitignore Node.gitignore pattern for .env files
|
||||
read: {
|
||||
"*": "allow",
|
||||
@@ -229,6 +234,7 @@ export const layer = Layer.effect(
|
||||
mode: "subagent",
|
||||
native: true,
|
||||
},
|
||||
// kilocode_change start - retain Kilo's opt-in repository research agent
|
||||
...(flags.experimentalScout
|
||||
? {
|
||||
scout: {
|
||||
@@ -259,6 +265,7 @@ export const layer = Layer.effect(
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
// kilocode_change end
|
||||
compaction: {
|
||||
name: "compaction",
|
||||
mode: "primary",
|
||||
@@ -539,7 +546,7 @@ export const layer = Layer.effect(
|
||||
guardRequirements, // kilocode_change
|
||||
generate: Effect.fn("Agent.generate")(function* (input: {
|
||||
description: string
|
||||
model?: { providerID: ProviderID; modelID: ModelID }
|
||||
model?: { providerID: ProviderV2.ID; modelID: ModelV2.ID }
|
||||
}) {
|
||||
const cfg = yield* config.get()
|
||||
const model = input.model ?? (yield* provider.defaultModel())
|
||||
@@ -603,7 +610,9 @@ export const layer = Layer.effect(
|
||||
}),
|
||||
)
|
||||
|
||||
export const defaultLayer = layer.pipe(
|
||||
// kilocode_change start - preserve the concrete layer type across Kilo's Agent/Skill cycle
|
||||
export const defaultLayer: Layer.Layer<Service> = layer.pipe(
|
||||
// kilocode_change end
|
||||
Layer.provide(Plugin.defaultLayer),
|
||||
Layer.provide(Provider.defaultLayer),
|
||||
Layer.provide(Auth.defaultLayer),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PermissionV1 } from "@opencode-ai/core/v1/permission"
|
||||
import type { Permission } from "../permission"
|
||||
import type { Agent } from "./agent"
|
||||
|
||||
@@ -15,10 +16,10 @@ import type { Agent } from "./agent"
|
||||
* doesn't already permit them.
|
||||
*/
|
||||
export function deriveSubagentSessionPermission(input: {
|
||||
parentSessionPermission: Permission.Ruleset
|
||||
parentSessionPermission: PermissionV1.Ruleset
|
||||
parentAgent: Agent.Info | undefined
|
||||
subagent: Agent.Info
|
||||
}): Permission.Ruleset {
|
||||
}): PermissionV1.Ruleset {
|
||||
const canTask = input.subagent.permission.some((rule) => rule.permission === "task")
|
||||
const canTodo = input.subagent.permission.some((rule) => rule.permission === "todowrite")
|
||||
const parentAgentDenies =
|
||||
|
||||
@@ -2,7 +2,7 @@ import path from "path"
|
||||
import { Effect, Layer, Record, Result, Schema, Context } from "effect"
|
||||
import { NonNegativeInt } from "@opencode-ai/core/schema"
|
||||
import { Global } from "@opencode-ai/core/global"
|
||||
import { AppFileSystem } from "@opencode-ai/core/filesystem"
|
||||
import { FSUtil } from "@opencode-ai/core/fs-util"
|
||||
import { Telemetry } from "@kilocode/kilo-telemetry" // kilocode_change
|
||||
|
||||
export const OAUTH_DUMMY_KEY = "kilo-oauth-dummy-key" // kilocode_change
|
||||
@@ -52,7 +52,7 @@ export class Service extends Context.Service<Service, Interface>()("@opencode/Au
|
||||
export const layer = Layer.effect(
|
||||
Service,
|
||||
Effect.gen(function* () {
|
||||
const fsys = yield* AppFileSystem.Service
|
||||
const fsys = yield* FSUtil.Service
|
||||
const decode = Schema.decodeUnknownOption(Info)
|
||||
|
||||
const all = Effect.fn("Auth.all")(function* () {
|
||||
@@ -99,6 +99,6 @@ export const layer = Layer.effect(
|
||||
}),
|
||||
)
|
||||
|
||||
export const defaultLayer = layer.pipe(Layer.provide(AppFileSystem.defaultLayer))
|
||||
export const defaultLayer = layer.pipe(Layer.provide(FSUtil.defaultLayer))
|
||||
|
||||
export * as Auth from "."
|
||||
|
||||
@@ -1,197 +1,33 @@
|
||||
import { BackgroundJob as CoreBackgroundJob } from "@opencode-ai/core/background-job"
|
||||
import { InstanceState } from "@/effect/instance-state"
|
||||
import { Identifier } from "@/id/id"
|
||||
import { Cause, Clock, Context, Deferred, Effect, Fiber, Layer, Scope, SynchronizedRef } from "effect"
|
||||
import { Effect, Layer } from "effect"
|
||||
|
||||
export type Status = "running" | "completed" | "error" | "cancelled"
|
||||
|
||||
export type Info = {
|
||||
id: string
|
||||
type: string
|
||||
title?: string
|
||||
status: Status
|
||||
started_at: number
|
||||
completed_at?: number
|
||||
output?: string
|
||||
error?: string
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
|
||||
type Active = {
|
||||
info: Info
|
||||
done: Deferred.Deferred<Info>
|
||||
fiber?: Fiber.Fiber<void, unknown>
|
||||
}
|
||||
|
||||
type State = {
|
||||
jobs: SynchronizedRef.SynchronizedRef<Map<string, Active>>
|
||||
scope: Scope.Scope
|
||||
}
|
||||
|
||||
type FinishResult = {
|
||||
info?: Info
|
||||
done?: Deferred.Deferred<Info>
|
||||
}
|
||||
|
||||
export type StartInput = {
|
||||
id?: string
|
||||
type: string
|
||||
title?: string
|
||||
metadata?: Record<string, unknown>
|
||||
run: Effect.Effect<string, unknown>
|
||||
}
|
||||
|
||||
export type WaitInput = {
|
||||
id: string
|
||||
timeout?: number
|
||||
}
|
||||
|
||||
export type WaitResult = {
|
||||
info?: Info
|
||||
timedOut: boolean
|
||||
}
|
||||
|
||||
export interface Interface {
|
||||
readonly list: () => Effect.Effect<Info[]>
|
||||
readonly get: (id: string) => Effect.Effect<Info | undefined>
|
||||
readonly start: (input: StartInput) => Effect.Effect<Info>
|
||||
readonly wait: (input: WaitInput) => Effect.Effect<WaitResult>
|
||||
readonly cancel: (id: string) => Effect.Effect<Info | undefined>
|
||||
}
|
||||
|
||||
export class Service extends Context.Service<Service, Interface>()("@opencode/BackgroundJob") {}
|
||||
|
||||
function snapshot(job: Active): Info {
|
||||
return {
|
||||
...job.info,
|
||||
...(job.info.metadata ? { metadata: { ...job.info.metadata } } : {}),
|
||||
}
|
||||
}
|
||||
|
||||
function errorText(error: unknown) {
|
||||
if (error instanceof Error) return error.message
|
||||
return String(error)
|
||||
}
|
||||
|
||||
export const layer = Layer.effect(
|
||||
export {
|
||||
Service,
|
||||
type ExtendInput,
|
||||
type Info,
|
||||
type Interface,
|
||||
type StartInput,
|
||||
type Status,
|
||||
type WaitInput,
|
||||
type WaitResult,
|
||||
} from "@opencode-ai/core/background-job"
|
||||
|
||||
/** Keeps the legacy service instance-scoped while sharing the core registry engine. */
|
||||
export const layer = Layer.effect(
|
||||
CoreBackgroundJob.Service,
|
||||
Effect.gen(function* () {
|
||||
const state = yield* InstanceState.make<State>(
|
||||
Effect.fn("BackgroundJob.state")(function* () {
|
||||
return {
|
||||
jobs: yield* SynchronizedRef.make(new Map()),
|
||||
scope: yield* Scope.Scope,
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
||||
const finish = Effect.fn("BackgroundJob.finish")(function* (
|
||||
id: string,
|
||||
status: Exclude<Status, "running">,
|
||||
data?: { output?: string; error?: string },
|
||||
) {
|
||||
const completed_at = yield* Clock.currentTimeMillis
|
||||
const result = yield* SynchronizedRef.modify(
|
||||
(yield* InstanceState.get(state)).jobs,
|
||||
(jobs): readonly [FinishResult, Map<string, Active>] => {
|
||||
const job = jobs.get(id)
|
||||
if (!job) return [{}, jobs]
|
||||
if (job.info.status !== "running") return [{ info: snapshot(job) }, jobs]
|
||||
const next = {
|
||||
...job,
|
||||
fiber: undefined,
|
||||
info: {
|
||||
...job.info,
|
||||
status,
|
||||
completed_at,
|
||||
...(data?.output !== undefined ? { output: data.output } : {}),
|
||||
...(data?.error !== undefined ? { error: data.error } : {}),
|
||||
},
|
||||
}
|
||||
return [{ info: snapshot(next), done: job.done }, new Map(jobs).set(id, next)]
|
||||
},
|
||||
)
|
||||
if (result.info && result.done) yield* Deferred.succeed(result.done, result.info).pipe(Effect.ignore)
|
||||
return result.info
|
||||
const state = yield* InstanceState.make(() => CoreBackgroundJob.make)
|
||||
return CoreBackgroundJob.Service.of({
|
||||
list: () => InstanceState.useEffect(state, (jobs) => jobs.list()),
|
||||
get: (id) => InstanceState.useEffect(state, (jobs) => jobs.get(id)),
|
||||
start: (input) => InstanceState.useEffect(state, (jobs) => jobs.start(input)),
|
||||
extend: (input) => InstanceState.useEffect(state, (jobs) => jobs.extend(input)),
|
||||
wait: (input) => InstanceState.useEffect(state, (jobs) => jobs.wait(input)),
|
||||
waitForPromotion: (id) => InstanceState.useEffect(state, (jobs) => jobs.waitForPromotion(id)),
|
||||
promote: (id) => InstanceState.useEffect(state, (jobs) => jobs.promote(id)),
|
||||
cancel: (id) => InstanceState.useEffect(state, (jobs) => jobs.cancel(id)),
|
||||
})
|
||||
|
||||
const list: Interface["list"] = Effect.fn("BackgroundJob.list")(function* () {
|
||||
return Array.from((yield* SynchronizedRef.get((yield* InstanceState.get(state)).jobs)).values())
|
||||
.map(snapshot)
|
||||
.toSorted((a, b) => a.started_at - b.started_at)
|
||||
})
|
||||
|
||||
const get: Interface["get"] = Effect.fn("BackgroundJob.get")(function* (id) {
|
||||
const job = (yield* SynchronizedRef.get((yield* InstanceState.get(state)).jobs)).get(id)
|
||||
if (!job) return
|
||||
return snapshot(job)
|
||||
})
|
||||
|
||||
const start: Interface["start"] = Effect.fn("BackgroundJob.start")(function* (input) {
|
||||
return yield* Effect.uninterruptibleMask((restore) =>
|
||||
Effect.gen(function* () {
|
||||
const s = yield* InstanceState.get(state)
|
||||
const id = input.id ?? Identifier.ascending("job")
|
||||
const started_at = yield* Clock.currentTimeMillis
|
||||
const done = yield* Deferred.make<Info>()
|
||||
return yield* SynchronizedRef.modifyEffect(
|
||||
s.jobs,
|
||||
Effect.fnUntraced(function* (jobs) {
|
||||
const existing = jobs.get(id)
|
||||
if (existing?.info.status === "running") return [snapshot(existing), jobs] as const
|
||||
const fiber = yield* restore(input.run).pipe(
|
||||
Effect.matchCauseEffect({
|
||||
onSuccess: (output) => finish(id, "completed", { output }),
|
||||
onFailure: (cause) =>
|
||||
finish(id, Cause.hasInterruptsOnly(cause) ? "cancelled" : "error", {
|
||||
error: errorText(Cause.squash(cause)),
|
||||
}),
|
||||
}),
|
||||
Effect.asVoid,
|
||||
Effect.forkIn(s.scope, { startImmediately: true }),
|
||||
)
|
||||
const job = {
|
||||
info: {
|
||||
id,
|
||||
type: input.type,
|
||||
title: input.title,
|
||||
status: "running" as const,
|
||||
started_at,
|
||||
metadata: input.metadata,
|
||||
},
|
||||
done,
|
||||
fiber,
|
||||
}
|
||||
return [snapshot(job), new Map(jobs).set(id, job)] as const
|
||||
}),
|
||||
)
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
const wait: Interface["wait"] = Effect.fn("BackgroundJob.wait")(function* (input) {
|
||||
const job = (yield* SynchronizedRef.get((yield* InstanceState.get(state)).jobs)).get(input.id)
|
||||
if (!job) return { timedOut: false }
|
||||
if (job.info.status !== "running") return { info: snapshot(job), timedOut: false }
|
||||
if (input.timeout === undefined) return { info: yield* Deferred.await(job.done), timedOut: false }
|
||||
if (input.timeout <= 0) return { info: snapshot(job), timedOut: true }
|
||||
const info = yield* Deferred.await(job.done).pipe(Effect.timeoutOption(input.timeout))
|
||||
if (info._tag === "Some") return { info: info.value, timedOut: false }
|
||||
return { info: snapshot(job), timedOut: true }
|
||||
})
|
||||
|
||||
const cancel: Interface["cancel"] = Effect.fn("BackgroundJob.cancel")(function* (id) {
|
||||
const job = (yield* SynchronizedRef.get((yield* InstanceState.get(state)).jobs)).get(id)
|
||||
if (!job) return
|
||||
if (job.info.status !== "running") return snapshot(job)
|
||||
if (job.fiber) {
|
||||
yield* Fiber.interrupt(job.fiber).pipe(Effect.ignore)
|
||||
yield* Fiber.await(job.fiber).pipe(Effect.ignore)
|
||||
}
|
||||
const info = yield* finish(id, "cancelled")
|
||||
return info
|
||||
})
|
||||
|
||||
return Service.of({ list, get, start, wait, cancel })
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// kilocode_change - Kilo compatibility layer. Upstream deleted this Bus (Effect PubSub) service in v1.16.2
|
||||
// in favour of EventV2; Kilo keeps it ONLY for existing Kilo-owned callers (kilocode/* features) that rely on
|
||||
// its eager-callback subscription + fork-atomicity semantics. Do NOT add new shared/upstream-shaped consumers.
|
||||
// Full migration of Kilo callers onto core EventV2 is tracked as a dedicated follow-up.
|
||||
import { Effect, Exit, Fiber, Layer, PubSub, Scope, Context, Stream, Schema } from "effect" // kilocode_change
|
||||
import { EffectBridge } from "@/effect/bridge"
|
||||
import * as Log from "@opencode-ai/core/util/log"
|
||||
|
||||
@@ -2,8 +2,6 @@ import * as Log from "@opencode-ai/core/util/log"
|
||||
import { Effect } from "effect"
|
||||
import { effectCmd } from "../effect-cmd"
|
||||
import { AgentSideConnection, ndJsonStream } from "@agentclientprotocol/sdk"
|
||||
import { ACP } from "@/acp/agent"
|
||||
import { Server } from "@/server/server"
|
||||
import { ServerAuth } from "@/server/auth"
|
||||
import { createKiloClient } from "@kilocode/sdk/v2"
|
||||
import { withNetworkOptions, resolveNetworkOptions } from "../network"
|
||||
@@ -22,6 +20,8 @@ export const AcpCommand = effectCmd({
|
||||
})
|
||||
},
|
||||
handler: Effect.fn("Cli.acp")(function* (args) {
|
||||
const { Server } = yield* Effect.promise(() => import("@/server/server"))
|
||||
const { ACP } = yield* Effect.promise(() => import("@/acp/agent"))
|
||||
ACPProfile.mark("cli.acp.handler")
|
||||
process.env.KILO_CLIENT = "acp"
|
||||
const opts = yield* resolveNetworkOptions(args)
|
||||
|
||||
@@ -2,13 +2,10 @@ import { cmd } from "./cmd"
|
||||
import * as prompts from "@clack/prompts"
|
||||
import { UI } from "../ui"
|
||||
import { Global } from "@opencode-ai/core/global"
|
||||
import { Agent } from "../../agent/agent"
|
||||
import { Provider } from "@/provider/provider"
|
||||
import path from "path"
|
||||
import fs from "fs/promises"
|
||||
import { Filesystem } from "@/util/filesystem"
|
||||
import matter from "gray-matter"
|
||||
import { InstanceRef } from "@/effect/instance-ref"
|
||||
import { EOL } from "os"
|
||||
import type { Argv } from "yargs"
|
||||
import { Effect } from "effect"
|
||||
@@ -62,6 +59,9 @@ const AgentCreateCommand = effectCmd({
|
||||
describe: "model to use in the format of provider/model",
|
||||
}),
|
||||
handler: Effect.fn("Cli.agent.create")(function* (args) {
|
||||
const { InstanceRef } = yield* Effect.promise(() => import("@/effect/instance-ref"))
|
||||
const { Agent } = yield* Effect.promise(() => import("../../agent/agent"))
|
||||
const { Provider } = yield* Effect.promise(() => import("@/provider/provider"))
|
||||
const maybeCtx = yield* InstanceRef
|
||||
if (!maybeCtx) return yield* Effect.die("InstanceRef not provided")
|
||||
const ctx = maybeCtx
|
||||
@@ -238,6 +238,7 @@ const AgentListCommand = effectCmd({
|
||||
command: "list",
|
||||
describe: "list all available agents",
|
||||
handler: Effect.fn("Cli.agent.list")(function* () {
|
||||
const { Agent } = yield* Effect.promise(() => import("../../agent/agent"))
|
||||
const agents = yield* Agent.Service.use((svc) => svc.list())
|
||||
const sortedAgents = agents.sort((a, b) => {
|
||||
if (a.native !== b.native) {
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
import type { Argv } from "yargs"
|
||||
import { spawn } from "child_process"
|
||||
import { Database } from "@/storage/db"
|
||||
import { drizzle } from "drizzle-orm/bun-sqlite"
|
||||
import { Database as BunDatabase } from "bun:sqlite"
|
||||
import { UI } from "../ui"
|
||||
import { cmd } from "./cmd"
|
||||
import { JsonMigration } from "@/storage/json-migration"
|
||||
import { EOL } from "os"
|
||||
import { errorMessage } from "../../util/error"
|
||||
import { Database } from "@opencode-ai/core/database/database"
|
||||
import { Effect } from "effect"
|
||||
import { sql } from "drizzle-orm"
|
||||
import { effectCmd } from "../effect-cmd"
|
||||
|
||||
const QueryCommand = cmd({
|
||||
const QueryCommand = effectCmd({
|
||||
command: "$0 [query]",
|
||||
describe: "open an interactive sqlite3 shell or run a query",
|
||||
instance: false,
|
||||
builder: (yargs: Argv) => {
|
||||
return yargs
|
||||
.positional("query", {
|
||||
@@ -25,97 +22,42 @@ const QueryCommand = cmd({
|
||||
describe: "Output format",
|
||||
})
|
||||
},
|
||||
handler: async (args: { query?: string; format: string }) => {
|
||||
handler: Effect.fn("Cli.db.query")(function* (args: { query?: string; format: string }) {
|
||||
const query = args.query as string | undefined
|
||||
if (query) {
|
||||
const db = new BunDatabase(Database.getPath(), { readonly: true })
|
||||
try {
|
||||
const result = db.query(query).all() as Record<string, unknown>[]
|
||||
if (args.format === "json") {
|
||||
console.log(JSON.stringify(result, null, 2))
|
||||
} else if (result.length > 0) {
|
||||
const keys = Object.keys(result[0])
|
||||
console.log(keys.join("\t"))
|
||||
for (const row of result) {
|
||||
console.log(keys.map((k) => row[k]).join("\t"))
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
UI.error(errorMessage(err))
|
||||
process.exit(1)
|
||||
const { db } = yield* Database.Service
|
||||
const result = yield* db.all<Record<string, unknown>>(sql.raw(query)).pipe(Effect.orDie)
|
||||
if (args.format === "json") console.log(JSON.stringify(result, null, 2))
|
||||
else if (result.length > 0) {
|
||||
const keys = Object.keys(result[0])
|
||||
console.log(keys.join("\t"))
|
||||
for (const row of result) console.log(keys.map((key) => row[key]).join("\t"))
|
||||
}
|
||||
db.close()
|
||||
return
|
||||
}
|
||||
const child = spawn("sqlite3", [Database.getPath()], {
|
||||
const child = spawn("sqlite3", [Database.path()], {
|
||||
stdio: "inherit",
|
||||
windowsHide: true, // kilocode_change - prevent CMD window flash on Windows
|
||||
})
|
||||
await new Promise((resolve) => child.on("close", resolve))
|
||||
},
|
||||
yield* Effect.promise(() => new Promise((resolve) => child.on("close", resolve)))
|
||||
}),
|
||||
})
|
||||
|
||||
const PathCommand = cmd({
|
||||
const PathCommand = effectCmd({
|
||||
command: "path",
|
||||
describe: "print the database path",
|
||||
handler: () => {
|
||||
console.log(Database.getPath())
|
||||
},
|
||||
instance: false,
|
||||
handler: Effect.fn("Cli.db.path")(function* () {
|
||||
console.log(Database.path())
|
||||
}),
|
||||
})
|
||||
|
||||
const MigrateCommand = cmd({
|
||||
command: "migrate",
|
||||
describe: "migrate JSON data to SQLite (merges with existing data)",
|
||||
handler: async () => {
|
||||
const sqlite = new BunDatabase(Database.getPath())
|
||||
const tty = process.stderr.isTTY
|
||||
const width = 36
|
||||
const orange = "\x1b[38;5;214m"
|
||||
const muted = "\x1b[0;2m"
|
||||
const reset = "\x1b[0m"
|
||||
let last = -1
|
||||
if (tty) process.stderr.write("\x1b[?25l")
|
||||
try {
|
||||
const stats = await JsonMigration.run(drizzle({ client: sqlite }), {
|
||||
progress: (event) => {
|
||||
const percent = Math.floor((event.current / event.total) * 100)
|
||||
if (percent === last) return
|
||||
last = percent
|
||||
if (tty) {
|
||||
const fill = Math.round((percent / 100) * width)
|
||||
const bar = `${"■".repeat(fill)}${"・".repeat(width - fill)}`
|
||||
process.stderr.write(
|
||||
`\r${orange}${bar} ${percent.toString().padStart(3)}%${reset} ${muted}${event.current}/${event.total}${reset} `,
|
||||
)
|
||||
} else {
|
||||
process.stderr.write(`sqlite-migration:${percent}${EOL}`)
|
||||
}
|
||||
},
|
||||
})
|
||||
if (tty) process.stderr.write("\n")
|
||||
if (tty) process.stderr.write("\x1b[?25h")
|
||||
else process.stderr.write(`sqlite-migration:done${EOL}`)
|
||||
UI.println(
|
||||
`Migration complete: ${stats.projects} projects, ${stats.sessions} sessions, ${stats.messages} messages`,
|
||||
)
|
||||
if (stats.errors.length > 0) {
|
||||
UI.println(`${stats.errors.length} errors occurred during migration`)
|
||||
}
|
||||
} catch (err) {
|
||||
if (tty) process.stderr.write("\x1b[?25h")
|
||||
UI.error(`Migration failed: ${errorMessage(err)}`)
|
||||
process.exit(1)
|
||||
} finally {
|
||||
sqlite.close()
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
export const DbCommand = cmd({
|
||||
export const DbCommand = effectCmd({
|
||||
command: "db",
|
||||
describe: "database tools",
|
||||
instance: false,
|
||||
builder: (yargs: Argv) => {
|
||||
return yargs.command(QueryCommand).command(PathCommand).command(MigrateCommand).demandCommand()
|
||||
return yargs.command(QueryCommand).command(PathCommand).demandCommand()
|
||||
},
|
||||
handler: () => {},
|
||||
handler: Effect.fn("Cli.db")(function* () {}),
|
||||
})
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
import { PermissionV1 } from "@opencode-ai/core/v1/permission"
|
||||
import { EOL } from "os"
|
||||
import { SessionV1 } from "@opencode-ai/core/v1/session"
|
||||
import { basename } from "path"
|
||||
import { Cause, Effect } from "effect"
|
||||
import { Agent } from "../../../agent/agent"
|
||||
import { Provider } from "@/provider/provider"
|
||||
import { Session } from "@/session/session"
|
||||
import type { MessageV2 } from "../../../session/message-v2"
|
||||
import { MessageID, PartID } from "../../../session/schema"
|
||||
import { ToolRegistry } from "@/tool/registry"
|
||||
import { Permission } from "../../../permission"
|
||||
import { iife } from "../../../util/iife"
|
||||
import { fail } from "../../effect-cmd"
|
||||
import { InstanceRef } from "@/effect/instance-ref"
|
||||
import type { InstanceContext } from "@/project/instance-context"
|
||||
|
||||
export const debugAgent = Effect.fn("Cli.debug.agent")(function* (args: {
|
||||
name: string
|
||||
tool?: string
|
||||
params?: string
|
||||
}) {
|
||||
const ctx = yield* InstanceRef
|
||||
if (!ctx) return
|
||||
return yield* run(args, ctx)
|
||||
})
|
||||
|
||||
const run = Effect.fn("Cli.debug.agent.body")(function* (
|
||||
args: { name: string; tool?: string; params?: string },
|
||||
ctx: InstanceContext,
|
||||
) {
|
||||
const agentName = args.name
|
||||
const agent = yield* Agent.Service.use((svc) => svc.get(agentName))
|
||||
if (!agent) {
|
||||
process.stderr.write(
|
||||
`Agent ${agentName} not found, run '${basename(process.execPath)} agent list' to get an agent list` + EOL,
|
||||
)
|
||||
return yield* fail("", 1)
|
||||
}
|
||||
const availableTools = yield* getAvailableTools(agent)
|
||||
const resolvedTools = resolveTools(agent, availableTools)
|
||||
const toolID = args.tool
|
||||
if (toolID) {
|
||||
const tool = availableTools.find((item) => item.id === toolID)
|
||||
if (!tool) {
|
||||
process.stderr.write(`Tool ${toolID} not found for agent ${agentName}` + EOL)
|
||||
return yield* fail("", 1)
|
||||
}
|
||||
if (resolvedTools[toolID] === false) {
|
||||
process.stderr.write(`Tool ${toolID} is disabled for agent ${agentName}` + EOL)
|
||||
return yield* fail("", 1)
|
||||
}
|
||||
const params = parseToolParams(args.params)
|
||||
const toolCtx = yield* createToolContext(agent, ctx)
|
||||
const result = yield* tool.execute(params, toolCtx)
|
||||
process.stdout.write(JSON.stringify({ tool: toolID, input: params, result }, null, 2) + EOL)
|
||||
return
|
||||
}
|
||||
|
||||
const output = {
|
||||
...agent,
|
||||
tools: resolvedTools,
|
||||
}
|
||||
process.stdout.write(JSON.stringify(output, null, 2) + EOL)
|
||||
})
|
||||
|
||||
const getAvailableTools = Effect.fn("Cli.debug.agent.getAvailableTools")(function* (agent: Agent.Info) {
|
||||
const provider = yield* Provider.Service
|
||||
const registry = yield* ToolRegistry.Service
|
||||
const model =
|
||||
agent.model ??
|
||||
(yield* provider.defaultModel().pipe(
|
||||
Effect.matchCauseEffect({
|
||||
onSuccess: Effect.succeed,
|
||||
onFailure: (cause) => {
|
||||
const error = Cause.squash(cause) as Provider.DefaultModelError
|
||||
if (error instanceof Provider.ModelNotFoundError) {
|
||||
return fail(`Model not found: ${error.providerID}/${error.modelID}`)
|
||||
}
|
||||
if (error instanceof Provider.NoModelsError) return fail(`No models found for provider ${error.providerID}`)
|
||||
return fail("No providers found")
|
||||
},
|
||||
}),
|
||||
))
|
||||
return yield* registry.tools({ ...model, agent })
|
||||
})
|
||||
|
||||
function resolveTools(agent: Agent.Info, availableTools: { id: string }[]) {
|
||||
const disabled = Permission.disabled(
|
||||
availableTools.map((tool) => tool.id),
|
||||
agent.permission,
|
||||
)
|
||||
const resolved: Record<string, boolean> = {}
|
||||
for (const tool of availableTools) {
|
||||
resolved[tool.id] = !disabled.has(tool.id)
|
||||
}
|
||||
return resolved
|
||||
}
|
||||
|
||||
function parseToolParams(input?: string) {
|
||||
if (!input) return {}
|
||||
const trimmed = input.trim()
|
||||
if (trimmed.length === 0) return {}
|
||||
|
||||
const parsed = iife(() => {
|
||||
try {
|
||||
return JSON.parse(trimmed)
|
||||
} catch (jsonError) {
|
||||
try {
|
||||
return new Function(`return (${trimmed})`)()
|
||||
} catch (evalError) {
|
||||
throw new Error(
|
||||
`Failed to parse --params. Use JSON or a JS object literal. JSON error: ${jsonError}. Eval error: ${evalError}.`,
|
||||
{ cause: evalError },
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
||||
throw new Error("Tool params must be an object.")
|
||||
}
|
||||
return parsed as Record<string, unknown>
|
||||
}
|
||||
|
||||
const createToolContext = Effect.fn("Cli.debug.agent.createToolContext")(function* (
|
||||
agent: Agent.Info,
|
||||
ctx: InstanceContext,
|
||||
) {
|
||||
const sessionSvc = yield* Session.Service
|
||||
const session = yield* sessionSvc.create({ title: `Debug tool run (${agent.name})` })
|
||||
const messageID = MessageID.ascending()
|
||||
const model = agent.model
|
||||
? agent.model
|
||||
: yield* Effect.gen(function* () {
|
||||
const provider = yield* Provider.Service
|
||||
return yield* provider.defaultModel().pipe(
|
||||
Effect.matchCauseEffect({
|
||||
onSuccess: Effect.succeed,
|
||||
onFailure: (cause) => {
|
||||
const error = Cause.squash(cause) as Provider.DefaultModelError
|
||||
if (error instanceof Provider.ModelNotFoundError) {
|
||||
return fail(`Model not found: ${error.providerID}/${error.modelID}`)
|
||||
}
|
||||
if (error instanceof Provider.NoModelsError)
|
||||
return fail(`No models found for provider ${error.providerID}`)
|
||||
return fail("No providers found")
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
const now = Date.now()
|
||||
const message: SessionV1.Assistant = {
|
||||
id: messageID,
|
||||
sessionID: session.id,
|
||||
role: "assistant",
|
||||
time: { created: now },
|
||||
parentID: messageID,
|
||||
modelID: model.modelID,
|
||||
providerID: model.providerID,
|
||||
mode: "debug",
|
||||
agent: agent.name,
|
||||
path: {
|
||||
cwd: ctx.directory,
|
||||
root: ctx.worktree,
|
||||
},
|
||||
cost: 0,
|
||||
tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
|
||||
}
|
||||
yield* sessionSvc.updateMessage(message)
|
||||
|
||||
const ruleset = Permission.merge(agent.permission, session.permission ?? [])
|
||||
|
||||
return {
|
||||
sessionID: session.id,
|
||||
messageID,
|
||||
callID: PartID.ascending(),
|
||||
agent: agent.name,
|
||||
abort: new AbortController().signal,
|
||||
messages: [],
|
||||
metadata: () => Effect.void,
|
||||
ask(req: Omit<PermissionV1.Request, "id" | "sessionID" | "tool">) {
|
||||
return Effect.sync(() => {
|
||||
for (const pattern of req.patterns) {
|
||||
const rule = Permission.evaluate(req.permission, pattern, ruleset)
|
||||
if (rule.action === "deny") {
|
||||
throw new PermissionV1.DeniedError({ ruleset })
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
})
|
||||
@@ -1,17 +1,5 @@
|
||||
import { EOL } from "os"
|
||||
import { basename } from "path"
|
||||
import { Cause, Effect } from "effect"
|
||||
import { Agent } from "../../../agent/agent"
|
||||
import { Provider } from "@/provider/provider"
|
||||
import { Session } from "@/session/session"
|
||||
import type { MessageV2 } from "../../../session/message-v2"
|
||||
import { MessageID, PartID } from "../../../session/schema"
|
||||
import { ToolRegistry } from "@/tool/registry"
|
||||
import { Permission } from "../../../permission"
|
||||
import { iife } from "../../../util/iife"
|
||||
import { effectCmd, fail } from "../../effect-cmd"
|
||||
import { InstanceRef } from "@/effect/instance-ref"
|
||||
import type { InstanceContext } from "@/project/instance-context"
|
||||
import { Effect } from "effect"
|
||||
import { effectCmd } from "../../effect-cmd"
|
||||
|
||||
export const AgentCommand = effectCmd({
|
||||
command: "agent <name>",
|
||||
@@ -31,176 +19,9 @@ export const AgentCommand = effectCmd({
|
||||
type: "string",
|
||||
description: "Tool params as JSON or a JS object literal",
|
||||
}),
|
||||
handler: Effect.fn("Cli.debug.agent")(function* (args) {
|
||||
const ctx = yield* InstanceRef
|
||||
if (!ctx) return
|
||||
return yield* run(args, ctx)
|
||||
}),
|
||||
})
|
||||
|
||||
const run = Effect.fn("Cli.debug.agent.body")(function* (
|
||||
args: { name: string; tool?: string; params?: string },
|
||||
ctx: InstanceContext,
|
||||
) {
|
||||
const agentName = args.name
|
||||
const agent = yield* Agent.Service.use((svc) => svc.get(agentName))
|
||||
if (!agent) {
|
||||
process.stderr.write(
|
||||
`Agent ${agentName} not found, run '${basename(process.execPath)} agent list' to get an agent list` + EOL,
|
||||
)
|
||||
return yield* fail("", 1)
|
||||
}
|
||||
const availableTools = yield* getAvailableTools(agent)
|
||||
const resolvedTools = resolveTools(agent, availableTools)
|
||||
const toolID = args.tool
|
||||
if (toolID) {
|
||||
const tool = availableTools.find((item) => item.id === toolID)
|
||||
if (!tool) {
|
||||
process.stderr.write(`Tool ${toolID} not found for agent ${agentName}` + EOL)
|
||||
return yield* fail("", 1)
|
||||
}
|
||||
if (resolvedTools[toolID] === false) {
|
||||
process.stderr.write(`Tool ${toolID} is disabled for agent ${agentName}` + EOL)
|
||||
return yield* fail("", 1)
|
||||
}
|
||||
const params = parseToolParams(args.params)
|
||||
const toolCtx = yield* createToolContext(agent, ctx)
|
||||
const result = yield* tool.execute(params, toolCtx)
|
||||
process.stdout.write(JSON.stringify({ tool: toolID, input: params, result }, null, 2) + EOL)
|
||||
return
|
||||
}
|
||||
|
||||
const output = {
|
||||
...agent,
|
||||
tools: resolvedTools,
|
||||
}
|
||||
process.stdout.write(JSON.stringify(output, null, 2) + EOL)
|
||||
})
|
||||
|
||||
const getAvailableTools = Effect.fn("Cli.debug.agent.getAvailableTools")(function* (agent: Agent.Info) {
|
||||
const provider = yield* Provider.Service
|
||||
const registry = yield* ToolRegistry.Service
|
||||
const model =
|
||||
agent.model ??
|
||||
(yield* provider.defaultModel().pipe(
|
||||
Effect.matchCauseEffect({
|
||||
onSuccess: Effect.succeed,
|
||||
onFailure: (cause) => {
|
||||
const error = Cause.squash(cause) as Provider.DefaultModelError
|
||||
if (error instanceof Provider.ModelNotFoundError) {
|
||||
return fail(`Model not found: ${error.providerID}/${error.modelID}`)
|
||||
}
|
||||
if (error instanceof Provider.NoModelsError) return fail(`No models found for provider ${error.providerID}`)
|
||||
return fail("No providers found")
|
||||
},
|
||||
}),
|
||||
))
|
||||
return yield* registry.tools({ ...model, agent })
|
||||
})
|
||||
|
||||
function resolveTools(agent: Agent.Info, availableTools: { id: string }[]) {
|
||||
const disabled = Permission.disabled(
|
||||
availableTools.map((tool) => tool.id),
|
||||
agent.permission,
|
||||
)
|
||||
const resolved: Record<string, boolean> = {}
|
||||
for (const tool of availableTools) {
|
||||
resolved[tool.id] = !disabled.has(tool.id)
|
||||
}
|
||||
return resolved
|
||||
}
|
||||
|
||||
function parseToolParams(input?: string) {
|
||||
if (!input) return {}
|
||||
const trimmed = input.trim()
|
||||
if (trimmed.length === 0) return {}
|
||||
|
||||
const parsed = iife(() => {
|
||||
try {
|
||||
return JSON.parse(trimmed)
|
||||
} catch (jsonError) {
|
||||
try {
|
||||
return new Function(`return (${trimmed})`)()
|
||||
} catch (evalError) {
|
||||
throw new Error(
|
||||
`Failed to parse --params. Use JSON or a JS object literal. JSON error: ${jsonError}. Eval error: ${evalError}.`,
|
||||
{ cause: evalError },
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
||||
throw new Error("Tool params must be an object.")
|
||||
}
|
||||
return parsed as Record<string, unknown>
|
||||
}
|
||||
|
||||
const createToolContext = Effect.fn("Cli.debug.agent.createToolContext")(function* (
|
||||
agent: Agent.Info,
|
||||
ctx: InstanceContext,
|
||||
) {
|
||||
const sessionSvc = yield* Session.Service
|
||||
const session = yield* sessionSvc.create({ title: `Debug tool run (${agent.name})` })
|
||||
const messageID = MessageID.ascending()
|
||||
const model = agent.model
|
||||
? agent.model
|
||||
: yield* Effect.gen(function* () {
|
||||
const provider = yield* Provider.Service
|
||||
return yield* provider.defaultModel().pipe(
|
||||
Effect.matchCauseEffect({
|
||||
onSuccess: Effect.succeed,
|
||||
onFailure: (cause) => {
|
||||
const error = Cause.squash(cause) as Provider.DefaultModelError
|
||||
if (error instanceof Provider.ModelNotFoundError) {
|
||||
return fail(`Model not found: ${error.providerID}/${error.modelID}`)
|
||||
}
|
||||
if (error instanceof Provider.NoModelsError)
|
||||
return fail(`No models found for provider ${error.providerID}`)
|
||||
return fail("No providers found")
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
const now = Date.now()
|
||||
const message: MessageV2.Assistant = {
|
||||
id: messageID,
|
||||
sessionID: session.id,
|
||||
role: "assistant",
|
||||
time: { created: now },
|
||||
parentID: messageID,
|
||||
modelID: model.modelID,
|
||||
providerID: model.providerID,
|
||||
mode: "debug",
|
||||
agent: agent.name,
|
||||
path: {
|
||||
cwd: ctx.directory,
|
||||
root: ctx.worktree,
|
||||
},
|
||||
cost: 0,
|
||||
tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
|
||||
}
|
||||
yield* sessionSvc.updateMessage(message)
|
||||
|
||||
const ruleset = Permission.merge(agent.permission, session.permission ?? [])
|
||||
|
||||
return {
|
||||
sessionID: session.id,
|
||||
messageID,
|
||||
callID: PartID.ascending(),
|
||||
agent: agent.name,
|
||||
abort: new AbortController().signal,
|
||||
messages: [],
|
||||
metadata: () => Effect.void,
|
||||
ask(req: Omit<Permission.Request, "id" | "sessionID" | "tool">) {
|
||||
return Effect.sync(() => {
|
||||
for (const pattern of req.patterns) {
|
||||
const rule = Permission.evaluate(req.permission, pattern, ruleset)
|
||||
if (rule.action === "deny") {
|
||||
throw new Permission.DeniedError({ ruleset })
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
handler: (args) =>
|
||||
Effect.gen(function* () {
|
||||
const { debugAgent } = yield* Effect.promise(() => import("./agent.handler"))
|
||||
return yield* debugAgent(args)
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { EOL } from "os"
|
||||
import { Effect } from "effect"
|
||||
import { Config } from "@/config/config"
|
||||
import { effectCmd } from "../../effect-cmd"
|
||||
|
||||
export const ConfigCommand = effectCmd({
|
||||
@@ -8,6 +7,7 @@ export const ConfigCommand = effectCmd({
|
||||
describe: "show resolved configuration",
|
||||
builder: (yargs) => yargs,
|
||||
handler: Effect.fn("Cli.debug.config")(function* () {
|
||||
const { Config } = yield* Effect.promise(() => import("@/config/config"))
|
||||
const config = yield* Config.Service.use((cfg) => cfg.get())
|
||||
process.stdout.write(JSON.stringify(config, null, 2) + EOL)
|
||||
}),
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
import { EOL } from "os"
|
||||
import { Effect } from "effect"
|
||||
import { File } from "../../../file"
|
||||
import { Ripgrep } from "@/file/ripgrep"
|
||||
import { FileSystem } from "@opencode-ai/core/filesystem"
|
||||
import { LocationServiceMap } from "@opencode-ai/core/location-layer"
|
||||
import { Ripgrep } from "@opencode-ai/core/filesystem/ripgrep"
|
||||
import { AbsolutePath, RelativePath } from "@opencode-ai/core/schema"
|
||||
import { effectCmd } from "../../effect-cmd"
|
||||
import { cmd } from "../cmd"
|
||||
|
||||
const filesystem = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
|
||||
effect.pipe(
|
||||
Effect.provide(LocationServiceMap.get({ directory: AbsolutePath.make(process.cwd()) })),
|
||||
Effect.provide(LocationServiceMap.layer),
|
||||
)
|
||||
|
||||
const FileSearchCommand = effectCmd({
|
||||
command: "search <query>",
|
||||
describe: "search files by query",
|
||||
@@ -15,8 +23,8 @@ const FileSearchCommand = effectCmd({
|
||||
description: "Search query",
|
||||
}),
|
||||
handler: Effect.fn("Cli.debug.file.search")(function* (args) {
|
||||
const results = yield* File.Service.use((svc) => svc.search({ query: args.query }))
|
||||
process.stdout.write(results.join(EOL) + EOL)
|
||||
const results = yield* filesystem(FileSystem.Service.use((svc) => svc.find({ query: args.query })))
|
||||
process.stdout.write(results.map((item) => item.path).join(EOL) + EOL)
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -30,21 +38,11 @@ const FileReadCommand = effectCmd({
|
||||
description: "File path to read",
|
||||
}),
|
||||
handler: Effect.fn("Cli.debug.file.read")(function* (args) {
|
||||
const content = yield* File.Service.use((svc) => svc.read(args.path))
|
||||
const content = yield* filesystem(FileSystem.Service.use((svc) => svc.read({ path: RelativePath.make(args.path) })))
|
||||
process.stdout.write(JSON.stringify(content, null, 2) + EOL)
|
||||
}),
|
||||
})
|
||||
|
||||
const FileStatusCommand = effectCmd({
|
||||
command: "status",
|
||||
describe: "show file status information",
|
||||
builder: (yargs) => yargs,
|
||||
handler: Effect.fn("Cli.debug.file.status")(function* () {
|
||||
const status = yield* File.Service.use((svc) => svc.status())
|
||||
process.stdout.write(JSON.stringify(status, null, 2) + EOL)
|
||||
}),
|
||||
})
|
||||
|
||||
const FileListCommand = effectCmd({
|
||||
command: "list <path>",
|
||||
describe: "list files in a directory",
|
||||
@@ -55,7 +53,7 @@ const FileListCommand = effectCmd({
|
||||
description: "File path to list",
|
||||
}),
|
||||
handler: Effect.fn("Cli.debug.file.list")(function* (args) {
|
||||
const files = yield* File.Service.use((svc) => svc.list(args.path))
|
||||
const files = yield* filesystem(FileSystem.Service.use((svc) => svc.list({ path: RelativePath.make(args.path) })))
|
||||
process.stdout.write(JSON.stringify(files, null, 2) + EOL)
|
||||
}),
|
||||
})
|
||||
@@ -81,7 +79,6 @@ export const FileCommand = cmd({
|
||||
builder: (yargs) =>
|
||||
yargs
|
||||
.command(FileReadCommand)
|
||||
.command(FileStatusCommand)
|
||||
.command(FileListCommand)
|
||||
.command(FileSearchCommand)
|
||||
.command(FileTreeCommand)
|
||||
|
||||
@@ -3,8 +3,6 @@ import { InstallationVersion } from "@opencode-ai/core/installation/version"
|
||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
||||
import os from "os"
|
||||
import { Duration, Effect } from "effect"
|
||||
import { Config } from "@/config/config"
|
||||
import { ConfigPlugin } from "@/config/plugin"
|
||||
import { effectCmd } from "../../effect-cmd"
|
||||
import { cmd } from "../cmd"
|
||||
import { ConfigCommand } from "./config"
|
||||
@@ -52,6 +50,8 @@ const InfoCommand = effectCmd({
|
||||
command: "info",
|
||||
describe: "show debug information",
|
||||
handler: Effect.fn("Cli.debug.info")(function* () {
|
||||
const { Config } = yield* Effect.promise(() => import("@/config/config"))
|
||||
const { ConfigPlugin } = yield* Effect.promise(() => import("@/config/plugin"))
|
||||
const config = yield* Config.Service.use((cfg) => cfg.get())
|
||||
const termProgram = process.env.TERM_PROGRAM
|
||||
? `${process.env.TERM_PROGRAM}${process.env.TERM_PROGRAM_VERSION ? ` ${process.env.TERM_PROGRAM_VERSION}` : ""}`
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { EOL } from "os"
|
||||
import { Effect, Stream } from "effect"
|
||||
import { Ripgrep } from "../../../file/ripgrep"
|
||||
import { Ripgrep } from "@opencode-ai/core/filesystem/ripgrep"
|
||||
import { effectCmd } from "../../effect-cmd"
|
||||
import { cmd } from "../cmd"
|
||||
import { InstanceRef } from "@/effect/instance-ref"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { EOL } from "os"
|
||||
import { Project } from "@/project/project"
|
||||
import * as Log from "@opencode-ai/core/util/log"
|
||||
import { cmd } from "../cmd"
|
||||
|
||||
@@ -8,8 +7,11 @@ export const ScrapCommand = cmd({
|
||||
describe: "list all known projects",
|
||||
builder: (yargs) => yargs,
|
||||
async handler() {
|
||||
const { Project } = await import("@/project/project")
|
||||
const { makeRuntime } = await import("@opencode-ai/core/effect/runtime")
|
||||
const runtime = makeRuntime(Project.Service, Project.defaultLayer)
|
||||
const timer = Log.Default.time("scrap")
|
||||
const list = await Project.list()
|
||||
const list = await runtime.runPromise((project) => project.list())
|
||||
process.stdout.write(JSON.stringify(list, null, 2) + EOL)
|
||||
timer.stop()
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Session } from "@/session/session"
|
||||
import { SessionV1 } from "@opencode-ai/core/v1/session"
|
||||
import { MessageV2 } from "../../session/message-v2"
|
||||
import { SessionID } from "../../session/schema"
|
||||
import { effectCmd, fail } from "../effect-cmd"
|
||||
@@ -36,7 +37,7 @@ function diff(
|
||||
}))
|
||||
}
|
||||
|
||||
function source(part: MessageV2.FilePart) {
|
||||
function source(part: SessionV1.FilePart) {
|
||||
if (!part.source) return part.source
|
||||
if (part.source.type === "symbol") {
|
||||
return {
|
||||
@@ -61,7 +62,7 @@ function source(part: MessageV2.FilePart) {
|
||||
}
|
||||
}
|
||||
|
||||
function filepart(part: MessageV2.FilePart): MessageV2.FilePart {
|
||||
function filepart(part: SessionV1.FilePart): SessionV1.FilePart {
|
||||
return {
|
||||
...part,
|
||||
url: redact("file-url", part.id, part.url),
|
||||
@@ -70,7 +71,7 @@ function filepart(part: MessageV2.FilePart): MessageV2.FilePart {
|
||||
}
|
||||
}
|
||||
|
||||
function part(part: MessageV2.Part): MessageV2.Part {
|
||||
function part(part: SessionV1.Part): SessionV1.Part {
|
||||
switch (part.type) {
|
||||
case "text":
|
||||
return {
|
||||
@@ -164,7 +165,7 @@ function part(part: MessageV2.Part): MessageV2.Part {
|
||||
|
||||
const partFn = part
|
||||
|
||||
function sanitize(data: { info: Session.Info; messages: MessageV2.WithParts[] }) {
|
||||
function sanitize(data: { info: Session.Info; messages: SessionV1.WithParts[] }) {
|
||||
return {
|
||||
info: {
|
||||
...data.info,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Server } from "../../server/server"
|
||||
import type { CommandModule } from "yargs"
|
||||
|
||||
type Args = {}
|
||||
@@ -7,6 +6,7 @@ export const GenerateCommand = {
|
||||
command: "generate",
|
||||
builder: (yargs) => yargs,
|
||||
handler: async () => {
|
||||
const { Server } = await import("../../server/server")
|
||||
const specs = (await Server.openapi()) as {
|
||||
info: { title: string; description: string } // kilocode_change
|
||||
paths: Record<string, Record<string, any>>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,30 @@
|
||||
import type { SessionV1 } from "@opencode-ai/core/v1/session"
|
||||
|
||||
export { parseGitHubRemote } from "@/util/repository"
|
||||
|
||||
/**
|
||||
* Extracts displayable text from assistant response parts.
|
||||
* Returns null for non-text responses (signals summary needed).
|
||||
* Throws only for truly empty responses.
|
||||
*/
|
||||
export function extractResponseText(parts: SessionV1.Part[]): string | null {
|
||||
const textPart = parts.findLast((p) => p.type === "text")
|
||||
if (textPart) return textPart.text
|
||||
|
||||
// Non-text parts (tools, reasoning, step-start/step-finish, etc.) - signal summary needed
|
||||
if (parts.length > 0) return null
|
||||
|
||||
throw new Error("Failed to parse response: no parts returned")
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a PROMPT_TOO_LARGE error message with details about files in the prompt.
|
||||
* Content is base64 encoded, so we calculate original size by multiplying by 0.75.
|
||||
*/
|
||||
export function formatPromptTooLargeError(files: { filename: string; content: string }[]): string {
|
||||
const fileDetails =
|
||||
files.length > 0
|
||||
? `\n\nFiles in prompt:\n${files.map((f) => ` - ${f.filename} (${((f.content.length * 0.75) / 1024).toFixed(0)} KB)`).join("\n")}`
|
||||
: ""
|
||||
return `PROMPT_TOO_LARGE: The prompt exceeds the model's context limit.${fileDetails}`
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,21 +1,21 @@
|
||||
import type { Session as SDKSession, Message, Part } from "@kilocode/sdk/v2"
|
||||
import { SessionV1 } from "@opencode-ai/core/v1/session"
|
||||
import { Session } from "@/session/session"
|
||||
import { MessageV2 } from "../../session/message-v2"
|
||||
import { CliError, effectCmd } from "../effect-cmd"
|
||||
import { Database } from "@/storage/db"
|
||||
import { SessionTable, MessageTable, PartTable } from "../../session/session.sql"
|
||||
import { Database } from "@opencode-ai/core/database/database"
|
||||
import { SessionTable, MessageTable, PartTable } from "@opencode-ai/core/session/sql"
|
||||
import { InstanceRef } from "@/effect/instance-ref"
|
||||
import { EOL } from "os"
|
||||
import path from "path"
|
||||
import { AppFileSystem } from "@opencode-ai/core/filesystem"
|
||||
import { FSUtil } from "@opencode-ai/core/fs-util"
|
||||
import { Effect, Schema } from "effect"
|
||||
import * as Log from "@opencode-ai/core/util/log" // kilocode_change
|
||||
import type { InstanceContext } from "@/project/instance-context"
|
||||
|
||||
const log = Log.create({ service: "import" }) // kilocode_change
|
||||
|
||||
const decodeMessageInfo = Schema.decodeUnknownSync(MessageV2.Info)
|
||||
const decodePart = Schema.decodeUnknownSync(MessageV2.Part)
|
||||
const decodeMessageInfo = Schema.decodeUnknownSync(SessionV1.Info)
|
||||
const decodePart = Schema.decodeUnknownSync(SessionV1.Part)
|
||||
|
||||
/** Discriminated union returned by the ShareNext API (GET /api/shares/:id/data) */
|
||||
export type ShareData =
|
||||
@@ -138,7 +138,8 @@ export const ImportCommand = effectCmd({
|
||||
})
|
||||
|
||||
const runImport = Effect.fn("Cli.import.body")(function* (file: string, ctx: InstanceContext) {
|
||||
const fs = yield* AppFileSystem.Service
|
||||
const fs = yield* FSUtil.Service
|
||||
const { db } = yield* Database.Service
|
||||
|
||||
let exportData: ExportData | undefined
|
||||
|
||||
@@ -205,48 +206,45 @@ const runImport = Effect.fn("Cli.import.body")(function* (file: string, ctx: Ins
|
||||
path: path.relative(path.resolve(ctx.worktree), ctx.directory).replaceAll("\\", "/"),
|
||||
}) as Session.Info
|
||||
const row = Session.toRow(info)
|
||||
Database.use((db) =>
|
||||
db
|
||||
.insert(SessionTable)
|
||||
.values(row)
|
||||
.onConflictDoUpdate({
|
||||
target: SessionTable.id,
|
||||
set: { project_id: row.project_id, directory: row.directory, path: row.path },
|
||||
})
|
||||
.run(),
|
||||
)
|
||||
yield* db
|
||||
.insert(SessionTable)
|
||||
.values(row)
|
||||
.onConflictDoUpdate({
|
||||
target: SessionTable.id,
|
||||
set: { project_id: row.project_id, directory: row.directory, path: row.path },
|
||||
})
|
||||
.run()
|
||||
.pipe(Effect.orDie)
|
||||
|
||||
for (const msg of exportData.messages) {
|
||||
const msgInfo = decodeMessageInfo(msg.info) as MessageV2.Info
|
||||
const msgInfo = decodeMessageInfo(msg.info) as SessionV1.Info
|
||||
const { id, sessionID: _, ...msgData } = msgInfo
|
||||
Database.use((db) =>
|
||||
db
|
||||
.insert(MessageTable)
|
||||
.values({
|
||||
id,
|
||||
session_id: row.id,
|
||||
time_created: msgInfo.time?.created ?? Date.now(),
|
||||
data: msgData,
|
||||
})
|
||||
.onConflictDoNothing()
|
||||
.run(),
|
||||
)
|
||||
yield* db
|
||||
.insert(MessageTable)
|
||||
.values({
|
||||
id,
|
||||
session_id: row.id,
|
||||
time_created: msgInfo.time?.created ?? Date.now(),
|
||||
data: msgData as never,
|
||||
})
|
||||
.onConflictDoNothing()
|
||||
.run()
|
||||
.pipe(Effect.orDie)
|
||||
|
||||
for (const part of msg.parts) {
|
||||
const partInfo = decodePart(part) as MessageV2.Part
|
||||
const partInfo = decodePart(part) as SessionV1.Part
|
||||
const { id: partId, sessionID: _s, messageID, ...partData } = partInfo
|
||||
Database.use((db) =>
|
||||
db
|
||||
.insert(PartTable)
|
||||
.values({
|
||||
id: partId,
|
||||
message_id: messageID,
|
||||
session_id: row.id,
|
||||
data: partData,
|
||||
})
|
||||
.onConflictDoNothing()
|
||||
.run(),
|
||||
)
|
||||
yield* db
|
||||
.insert(PartTable)
|
||||
.values({
|
||||
id: partId,
|
||||
message_id: messageID,
|
||||
session_id: row.id,
|
||||
data: partData,
|
||||
})
|
||||
.onConflictDoNothing()
|
||||
.run()
|
||||
.pipe(Effect.orDie)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { cmd } from "./cmd"
|
||||
import { ConfigV1 } from "@opencode-ai/core/v1/config/config"
|
||||
import { effectCmd } from "../effect-cmd"
|
||||
import { Cause } from "effect"
|
||||
import { Client } from "@modelcontextprotocol/sdk/client/index.js"
|
||||
@@ -10,7 +11,7 @@ import { MCP } from "../../mcp"
|
||||
import { McpAuth } from "../../mcp/auth"
|
||||
import { McpOAuthProvider } from "../../mcp/oauth-provider"
|
||||
import { Config } from "@/config/config"
|
||||
import { ConfigMCP } from "../../config/mcp"
|
||||
import { ConfigMCPV1 } from "@opencode-ai/core/v1/config/mcp"
|
||||
import { InstanceRef } from "@/effect/instance-ref"
|
||||
import { InstallationVersion } from "@opencode-ai/core/installation/version"
|
||||
import path from "path"
|
||||
@@ -18,7 +19,8 @@ import { Global } from "@opencode-ai/core/global"
|
||||
import { modify, applyEdits } from "jsonc-parser"
|
||||
import { KilocodeMcpConfig } from "@/kilocode/cli/cmd/mcp" // kilocode_change
|
||||
import { Filesystem } from "@/util/filesystem"
|
||||
import { Bus } from "../../bus"
|
||||
import { EventV2Bridge } from "@/event-v2-bridge"
|
||||
import { EventV2 } from "@opencode-ai/core/event"
|
||||
import { Effect } from "effect"
|
||||
|
||||
function getAuthStatusIcon(status: MCP.AuthStatus): string {
|
||||
@@ -43,9 +45,9 @@ function getAuthStatusText(status: MCP.AuthStatus): string {
|
||||
}
|
||||
}
|
||||
|
||||
type McpEntry = NonNullable<Config.Info["mcp"]>[string]
|
||||
type McpEntry = NonNullable<ConfigV1.Info["mcp"]>[string]
|
||||
|
||||
type McpConfigured = ConfigMCP.Info
|
||||
type McpConfigured = ConfigMCPV1.Info
|
||||
function isMcpConfigured(config: McpEntry): config is McpConfigured {
|
||||
return typeof config === "object" && config !== null && "type" in config
|
||||
}
|
||||
@@ -55,11 +57,11 @@ function isMcpRemote(config: McpEntry): config is McpRemote {
|
||||
return isMcpConfigured(config) && config.type === "remote"
|
||||
}
|
||||
|
||||
function configuredServers(config: Config.Info) {
|
||||
function configuredServers(config: ConfigV1.Info) {
|
||||
return Object.entries(config.mcp ?? {}).filter((entry): entry is [string, McpConfigured] => isMcpConfigured(entry[1]))
|
||||
}
|
||||
|
||||
function oauthServers(config: Config.Info) {
|
||||
function oauthServers(config: ConfigV1.Info) {
|
||||
return configuredServers(config).filter(
|
||||
(entry): entry is [string, McpRemote] => isMcpRemote(entry[1]) && entry[1].oauth !== false,
|
||||
)
|
||||
@@ -257,13 +259,17 @@ export const McpAuthCommand = effectCmd({
|
||||
spinner.start("Starting OAuth flow...")
|
||||
|
||||
// Subscribe to browser open failure events to show URL for manual opening
|
||||
const unsubscribe = Bus.subscribe(MCP.BrowserOpenFailed, (evt) => {
|
||||
if (evt.properties.mcpName === serverName) {
|
||||
const events = yield* EventV2Bridge.Service
|
||||
const unsubscribe = yield* events.listen((event) => {
|
||||
if (event.type !== MCP.BrowserOpenFailed.type) return Effect.void
|
||||
const data = event.data as EventV2.Data<typeof MCP.BrowserOpenFailed>
|
||||
if (data.mcpName === serverName) {
|
||||
spinner.stop("Could not open browser automatically")
|
||||
prompts.log.warn("Please open this URL in your browser to authenticate:")
|
||||
prompts.log.info(evt.properties.url)
|
||||
prompts.log.info(data.url)
|
||||
spinner.start("Waiting for authorization...")
|
||||
}
|
||||
return Effect.void
|
||||
})
|
||||
|
||||
yield* MCP.Service.use((mcp) => mcp.authenticate(serverName)).pipe(
|
||||
@@ -301,7 +307,7 @@ export const McpAuthCommand = effectCmd({
|
||||
prompts.log.error(error instanceof Error ? error.message : String(error))
|
||||
}),
|
||||
),
|
||||
Effect.ensuring(Effect.sync(() => unsubscribe())),
|
||||
Effect.ensuring(unsubscribe),
|
||||
)
|
||||
|
||||
prompts.outro("Done")
|
||||
@@ -429,7 +435,7 @@ async function resolveConfigPath(baseDir: string, global = false) {
|
||||
// kilocode_change end
|
||||
}
|
||||
|
||||
async function addMcpToConfig(name: string, mcpConfig: ConfigMCP.Info, configPath: string) {
|
||||
async function addMcpToConfig(name: string, mcpConfig: ConfigMCPV1.Info, configPath: string) {
|
||||
let text = "{}"
|
||||
if (await Filesystem.exists(configPath)) {
|
||||
text = await Filesystem.readText(configPath)
|
||||
@@ -518,7 +524,7 @@ export const McpAddCommand = effectCmd({
|
||||
})
|
||||
if (prompts.isCancel(command)) throw new UI.CancelledError()
|
||||
|
||||
const mcpConfig: ConfigMCP.Info = {
|
||||
const mcpConfig: ConfigMCPV1.Info = {
|
||||
type: "local",
|
||||
command: command.split(" "),
|
||||
}
|
||||
@@ -548,7 +554,7 @@ export const McpAddCommand = effectCmd({
|
||||
})
|
||||
if (prompts.isCancel(useOAuth)) throw new UI.CancelledError()
|
||||
|
||||
let mcpConfig: ConfigMCP.Info
|
||||
let mcpConfig: ConfigMCPV1.Info
|
||||
|
||||
if (useOAuth) {
|
||||
const hasClientId = await prompts.confirm({
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { EOL } from "os"
|
||||
import { Effect } from "effect"
|
||||
import { Provider } from "@/provider/provider"
|
||||
import { ProviderID } from "../../provider/schema"
|
||||
import { ModelsDev } from "@opencode-ai/core/models-dev"
|
||||
import { effectCmd, fail } from "../effect-cmd"
|
||||
import { UI } from "../ui"
|
||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
|
||||
export const ModelsCommand = effectCmd({
|
||||
command: "models [provider]",
|
||||
@@ -25,6 +24,7 @@ export const ModelsCommand = effectCmd({
|
||||
type: "boolean",
|
||||
}),
|
||||
handler: Effect.fn("Cli.models")(function* (args) {
|
||||
const { Provider } = yield* Effect.promise(() => import("@/provider/provider"))
|
||||
if (args.refresh) {
|
||||
yield* ModelsDev.Service.use((s) => s.refresh(true))
|
||||
UI.println(UI.Style.TEXT_SUCCESS_BOLD + "Models cache refreshed" + UI.Style.TEXT_NORMAL)
|
||||
@@ -33,7 +33,7 @@ export const ModelsCommand = effectCmd({
|
||||
const provider = yield* Provider.Service
|
||||
const providers = yield* provider.list()
|
||||
|
||||
const print = (providerID: ProviderID, verbose?: boolean) => {
|
||||
const print = (providerID: ProviderV2.ID, verbose?: boolean) => {
|
||||
const p = providers[providerID]
|
||||
const sorted = Object.entries(p.models).sort(([a], [b]) => a.localeCompare(b))
|
||||
for (const [modelID, model] of sorted) {
|
||||
@@ -47,7 +47,7 @@ export const ModelsCommand = effectCmd({
|
||||
}
|
||||
|
||||
if (args.provider) {
|
||||
const providerID = ProviderID.make(args.provider)
|
||||
const providerID = ProviderV2.ID.make(args.provider)
|
||||
if (!providers[providerID]) return yield* fail(`Provider not found: ${args.provider}`)
|
||||
print(providerID, args.verbose)
|
||||
return
|
||||
@@ -63,6 +63,6 @@ export const ModelsCommand = effectCmd({
|
||||
})
|
||||
// kilocode_change end
|
||||
|
||||
for (const providerID of ids) print(ProviderID.make(providerID), args.verbose)
|
||||
for (const providerID of ids) print(ProviderV2.ID.make(providerID), args.verbose)
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const graphemes = new Intl.Segmenter(undefined, { granularity: "grapheme" })
|
||||
|
||||
function promptOffsetWidth(value: string) {
|
||||
export function promptOffsetWidth(value: string) {
|
||||
let width = 0
|
||||
for (const part of graphemes.segment(value)) {
|
||||
// Textarea offsets count newlines as one position; Bun.stringWidth counts them as zero.
|
||||
|
||||
@@ -479,6 +479,25 @@ export const ProvidersLoginCommand = effectCmd({
|
||||
)
|
||||
}
|
||||
|
||||
if (provider === "snowflake-cortex") {
|
||||
const account = yield* promptValue(
|
||||
yield* Prompt.text({
|
||||
message: "Snowflake Account Identifier",
|
||||
placeholder: "xy12345.us-east-1",
|
||||
validate: (x) => (x && x.length > 0 ? undefined : "Required"),
|
||||
}),
|
||||
)
|
||||
const pat = yield* promptValue(
|
||||
yield* Prompt.password({
|
||||
message: "Programmatic Access Token (PAT)",
|
||||
validate: (x) => (x && x.length > 0 ? undefined : "Required"),
|
||||
}),
|
||||
)
|
||||
yield* Effect.orDie(authSvc.set(provider, { type: "api", key: pat, metadata: { account } }))
|
||||
yield* Prompt.outro("Done")
|
||||
return
|
||||
}
|
||||
|
||||
const key = yield* Prompt.password({
|
||||
message: "Enter your API key",
|
||||
validate: (x) => (x && x.length > 0 ? undefined : "Required"),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { PermissionV1 } from "@opencode-ai/core/v1/permission"
|
||||
// kilocode_change start - use Kilo CLI branding
|
||||
// CLI entry point for `kilo run`.
|
||||
//
|
||||
@@ -19,15 +20,12 @@ import { pathToFileURL } from "url"
|
||||
import { Effect } from "effect"
|
||||
import { UI } from "../ui"
|
||||
import { effectCmd } from "../effect-cmd"
|
||||
import { ServerAuth } from "@/server/auth"
|
||||
import { buildRunMessage } from "@/kilocode/cli/cmd/run-message" // kilocode_change
|
||||
import { EOL } from "os"
|
||||
import { Filesystem } from "@/util/filesystem"
|
||||
import { createKiloClient, type KiloClient, type Session, type ToolPart } from "@kilocode/sdk/v2"
|
||||
import { Agent } from "@/agent/agent"
|
||||
import { Permission } from "@/permission"
|
||||
import { RuntimeFlags } from "@/effect/runtime-flags"
|
||||
import { InstanceRef } from "@/effect/instance-ref"
|
||||
import { FormatError, FormatUnknownError } from "../error"
|
||||
import { INTERACTIVE_INPUT_ERROR, resolveInteractiveStdin } from "./run/runtime.stdin"
|
||||
import { event as normalizeEvent } from "./run/event"
|
||||
@@ -36,7 +34,6 @@ import { KiloRunAuto } from "@/kilocode/cli/run-auto" // kilocode_change
|
||||
import { KiloHeadless } from "@/kilocode/permission/headless" // kilocode_change
|
||||
import { KiloRun, KiloRunDaemon } from "@/kilocode/cli/cmd/run" // kilocode_change
|
||||
|
||||
const runtimeTask = import("./run/runtime")
|
||||
type ModelInput = Parameters<KiloClient["session"]["prompt"]>[0]["model"]
|
||||
|
||||
function pick(value: string | undefined): ModelInput | undefined {
|
||||
@@ -235,8 +232,8 @@ export const RunCommand = effectCmd({
|
||||
})
|
||||
.option("replay", {
|
||||
type: "boolean",
|
||||
default: false,
|
||||
describe: "replay visible session history on interactive resume",
|
||||
default: true,
|
||||
describe: "replay interactive session history on resume and after resize (use --no-replay to disable)",
|
||||
})
|
||||
.option("replay-limit", {
|
||||
type: "number",
|
||||
@@ -266,6 +263,10 @@ export const RunCommand = effectCmd({
|
||||
describe: "enable direct interactive demo slash commands; pass one as the message to run it immediately",
|
||||
}),
|
||||
handler: Effect.fn("Cli.run")(function* (args) {
|
||||
const { Agent } = yield* Effect.promise(() => import("@/agent/agent"))
|
||||
const { RuntimeFlags } = yield* Effect.promise(() => import("@/effect/runtime-flags"))
|
||||
const { InstanceRef } = yield* Effect.promise(() => import("@/effect/instance-ref"))
|
||||
const { ServerAuth } = yield* Effect.promise(() => import("@/server/auth"))
|
||||
const agentSvc = yield* Agent.Service
|
||||
const flags = yield* RuntimeFlags.Service
|
||||
const localInstance = yield* InstanceRef
|
||||
@@ -298,10 +299,6 @@ export const RunCommand = effectCmd({
|
||||
die("--interactive cannot be used with --format json")
|
||||
}
|
||||
|
||||
if (args.replay && !args.interactive) {
|
||||
die("--replay requires --interactive")
|
||||
}
|
||||
|
||||
if (args["replay-limit"] !== undefined && !args.interactive) {
|
||||
die("--replay-limit requires --interactive")
|
||||
}
|
||||
@@ -405,7 +402,7 @@ export const RunCommand = effectCmd({
|
||||
}
|
||||
// kilocode_change end
|
||||
|
||||
const rules: Permission.Ruleset = args.interactive
|
||||
const rules: PermissionV1.Ruleset = args.interactive
|
||||
? []
|
||||
: [
|
||||
{
|
||||
@@ -986,7 +983,7 @@ export const RunCommand = effectCmd({
|
||||
}
|
||||
|
||||
const model = pick(args.model)
|
||||
const { runInteractiveMode } = await runtimeTask
|
||||
const { runInteractiveMode } = await import("./run/runtime")
|
||||
try {
|
||||
await runInteractiveMode({
|
||||
sdk: client,
|
||||
@@ -1003,6 +1000,7 @@ export const RunCommand = effectCmd({
|
||||
initialInput: input.initial,
|
||||
createSession: createFreshSession,
|
||||
thinking,
|
||||
backgroundSubagents: flags.experimentalBackgroundSubagents,
|
||||
demo: args.demo,
|
||||
})
|
||||
} catch (error) {
|
||||
@@ -1014,7 +1012,7 @@ export const RunCommand = effectCmd({
|
||||
if (args.interactive && !args.attach && !args.session && !args.continue) {
|
||||
await loadInput() // kilocode_change - interactive local mode still consumes its initial input
|
||||
const model = pick(args.model)
|
||||
const { runInteractiveLocalMode } = await runtimeTask
|
||||
const { runInteractiveLocalMode } = await import("./run/runtime")
|
||||
const fetchFn = (async (input: RequestInfo | URL, init?: RequestInit) => {
|
||||
const { Server } = await import("@/server/server")
|
||||
const request = new Request(input, init)
|
||||
@@ -1037,6 +1035,7 @@ export const RunCommand = effectCmd({
|
||||
files,
|
||||
initialInput: input.initial,
|
||||
thinking,
|
||||
backgroundSubagents: flags.experimentalBackgroundSubagents,
|
||||
demo: args.demo,
|
||||
})
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// kilocode_change - new file
|
||||
import type {
|
||||
Event as SDKEvent,
|
||||
GlobalEvent,
|
||||
@@ -10,25 +11,25 @@ import type {
|
||||
type MessageUpdated = {
|
||||
id: string
|
||||
type: "message.updated"
|
||||
properties: SyncEventMessageUpdated["data"]
|
||||
properties: SyncEventMessageUpdated["syncEvent"]["data"]
|
||||
}
|
||||
|
||||
type MessageRemoved = {
|
||||
id: string
|
||||
type: "message.removed"
|
||||
properties: SyncEventMessageRemoved["data"]
|
||||
properties: SyncEventMessageRemoved["syncEvent"]["data"]
|
||||
}
|
||||
|
||||
type MessagePartUpdated = {
|
||||
id: string
|
||||
type: "message.part.updated"
|
||||
properties: SyncEventMessagePartUpdated["data"]
|
||||
properties: SyncEventMessagePartUpdated["syncEvent"]["data"]
|
||||
}
|
||||
|
||||
type MessagePartRemoved = {
|
||||
id: string
|
||||
type: "message.part.removed"
|
||||
properties: SyncEventMessagePartRemoved["data"]
|
||||
properties: SyncEventMessagePartRemoved["syncEvent"]["data"]
|
||||
}
|
||||
|
||||
export type Event = SDKEvent | MessageUpdated | MessageRemoved | MessagePartUpdated | MessagePartRemoved
|
||||
@@ -36,15 +37,16 @@ export type Event = SDKEvent | MessageUpdated | MessageRemoved | MessagePartUpda
|
||||
export function event(payload: GlobalEvent["payload"]): Event | undefined {
|
||||
if (payload.type !== "sync") return payload
|
||||
|
||||
switch (payload.name) {
|
||||
const sync = payload.syncEvent
|
||||
switch (sync.type) {
|
||||
case "message.updated.1":
|
||||
return { id: payload.id, type: "message.updated", properties: payload.data }
|
||||
return { id: sync.id, type: "message.updated", properties: sync.data }
|
||||
case "message.removed.1":
|
||||
return { id: payload.id, type: "message.removed", properties: payload.data }
|
||||
return { id: sync.id, type: "message.removed", properties: sync.data }
|
||||
case "message.part.updated.1":
|
||||
return { id: payload.id, type: "message.part.updated", properties: payload.data }
|
||||
return { id: sync.id, type: "message.part.updated", properties: sync.data }
|
||||
case "message.part.removed.1":
|
||||
return { id: payload.id, type: "message.part.removed", properties: payload.data }
|
||||
return { id: sync.id, type: "message.part.removed", properties: sync.data }
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
|
||||
@@ -4,9 +4,8 @@ import { useKeyboard, type JSX } from "@opentui/solid"
|
||||
import fuzzysort from "fuzzysort"
|
||||
import { createEffect, createMemo, createSignal, type Accessor } from "solid-js"
|
||||
import { RunFooterMenu, createFooterMenuState, type RunFooterMenuItem } from "./footer.menu"
|
||||
import { formatBindings } from "./keymap.shared"
|
||||
import type { RunFooterTheme } from "./theme"
|
||||
import type { FooterKeybinds, FooterSubagentTab, RunCommand, RunInput, RunProvider } from "./types"
|
||||
import type { FooterQueuedPrompt, FooterSubagentTab, RunCommand, RunInput, RunProvider } from "./types"
|
||||
|
||||
type PanelEntry = RunFooterMenuItem & {
|
||||
category: string
|
||||
@@ -15,6 +14,7 @@ type PanelEntry = RunFooterMenuItem & {
|
||||
|
||||
type CommandEntry =
|
||||
| (PanelEntry & { action: "model" })
|
||||
| (PanelEntry & { action: "queued" })
|
||||
| (PanelEntry & { action: "subagent" })
|
||||
| (PanelEntry & { action: "variant.cycle" })
|
||||
| (PanelEntry & { action: "variant.list" })
|
||||
@@ -38,6 +38,10 @@ type SubagentEntry = PanelEntry & {
|
||||
current: boolean
|
||||
}
|
||||
|
||||
type QueuedEntry = PanelEntry & {
|
||||
prompt: FooterQueuedPrompt
|
||||
}
|
||||
|
||||
type MenuState = ReturnType<typeof createFooterMenuState>
|
||||
|
||||
const PANEL_PAD = 2
|
||||
@@ -295,11 +299,13 @@ export function RunCommandMenuBody(props: {
|
||||
theme: Accessor<RunFooterTheme>
|
||||
commands: Accessor<RunCommand[] | undefined>
|
||||
subagents: Accessor<FooterSubagentTab[]>
|
||||
queued: Accessor<FooterQueuedPrompt[]>
|
||||
variants: Accessor<string[]>
|
||||
keybinds: FooterKeybinds
|
||||
variantCycle: string
|
||||
onClose: () => void
|
||||
onModel: () => void
|
||||
onSubagent: () => void
|
||||
onQueued: () => void
|
||||
onVariant: () => void
|
||||
onVariantCycle: () => void
|
||||
onCommand: (name: string) => void
|
||||
@@ -316,6 +322,20 @@ export function RunCommandMenuBody(props: {
|
||||
category: "Suggested",
|
||||
display: "Switch model",
|
||||
},
|
||||
...(props.queued().length > 0
|
||||
? [
|
||||
{
|
||||
action: "queued" as const,
|
||||
category: "Suggested",
|
||||
display: "Manage queued prompts",
|
||||
footer: `${props.queued().length} queued`,
|
||||
keywords: props
|
||||
.queued()
|
||||
.map((item) => item.prompt.text)
|
||||
.join(" "),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(props.subagents().length > 0
|
||||
? [
|
||||
{
|
||||
@@ -334,7 +354,7 @@ export function RunCommandMenuBody(props: {
|
||||
action: "variant.cycle",
|
||||
category: "Suggested",
|
||||
display: "Variant cycle",
|
||||
footer: formatBindings(props.keybinds.variantCycle, props.keybinds.leader),
|
||||
footer: props.variantCycle,
|
||||
keywords: "variant cycle",
|
||||
},
|
||||
...(props.variants().length > 0
|
||||
@@ -388,6 +408,11 @@ export function RunCommandMenuBody(props: {
|
||||
return
|
||||
}
|
||||
|
||||
if (item.action === "queued") {
|
||||
props.onQueued()
|
||||
return
|
||||
}
|
||||
|
||||
if (item.action === "variant.cycle") {
|
||||
props.onVariantCycle()
|
||||
return
|
||||
@@ -560,6 +585,102 @@ export function RunSubagentSelectBody(props: {
|
||||
)
|
||||
}
|
||||
|
||||
export function RunQueuedPromptSelectBody(props: {
|
||||
theme: Accessor<RunFooterTheme>
|
||||
prompts: Accessor<FooterQueuedPrompt[]>
|
||||
onClose: () => void
|
||||
onEdit: (prompt: FooterQueuedPrompt) => void | Promise<void>
|
||||
onDelete: (prompt: FooterQueuedPrompt) => void | Promise<void>
|
||||
onRows?: (rows: number) => void
|
||||
}) {
|
||||
let field: InputRenderable | undefined
|
||||
const [query, setQuery] = createSignal("")
|
||||
const entries = createMemo<QueuedEntry[]>(() =>
|
||||
props.prompts().map((prompt) => ({
|
||||
category: "",
|
||||
display: prompt.prompt.text.replaceAll("\n", " "),
|
||||
footer: "queued · ctrl+e edit · ctrl+d remove",
|
||||
keywords: prompt.prompt.text,
|
||||
prompt,
|
||||
})),
|
||||
)
|
||||
const items = createMemo<QueuedEntry[]>(() => match(query(), entries()))
|
||||
const menu = createFooterMenuState({ count: () => items().length, limit: SUBAGENT_LIST_ROWS })
|
||||
const selected = () => items()[menu.selected()]
|
||||
|
||||
createEffect(() => {
|
||||
query()
|
||||
menu.reset()
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
props.onRows?.(menu.rows() + PANEL_FRAME_ROWS)
|
||||
})
|
||||
|
||||
useKeyboard((event) => {
|
||||
if (event.defaultPrevented) {
|
||||
return
|
||||
}
|
||||
|
||||
const item = selected()
|
||||
const ctrl = event.ctrl && !event.meta && !event.shift && !event.super
|
||||
if (item && (event.name === "delete" || (ctrl && event.name === "d"))) {
|
||||
event.preventDefault()
|
||||
props.onDelete(item.prompt)
|
||||
return
|
||||
}
|
||||
|
||||
if (item && ctrl && event.name === "e") {
|
||||
event.preventDefault()
|
||||
props.onEdit(item.prompt)
|
||||
return
|
||||
}
|
||||
|
||||
handleKey({
|
||||
event,
|
||||
menu,
|
||||
field: () => field,
|
||||
setQuery,
|
||||
select: () => {
|
||||
const item = selected()
|
||||
if (item) props.onEdit(item.prompt)
|
||||
},
|
||||
close: props.onClose,
|
||||
})
|
||||
})
|
||||
|
||||
return (
|
||||
<PanelShell
|
||||
id="run-direct-footer-queued-panel"
|
||||
title="Queued prompts"
|
||||
query={query()}
|
||||
count={items().length}
|
||||
total={entries().length}
|
||||
placeholder="Search"
|
||||
theme={props.theme}
|
||||
inputRef={(input) => {
|
||||
field = input
|
||||
}}
|
||||
onQuery={setQuery}
|
||||
>
|
||||
<RunFooterMenu
|
||||
id="run-direct-footer-queued-list"
|
||||
theme={props.theme}
|
||||
items={items}
|
||||
selected={menu.selected}
|
||||
offset={menu.offset}
|
||||
rows={menu.rows}
|
||||
limit={SUBAGENT_LIST_ROWS}
|
||||
empty="No queued prompts"
|
||||
border={false}
|
||||
paddingLeft={PANEL_PAD}
|
||||
paddingRight={PANEL_PAD}
|
||||
grouped={false}
|
||||
/>
|
||||
</PanelShell>
|
||||
)
|
||||
}
|
||||
|
||||
export function RunVariantSelectBody(props: {
|
||||
theme: Accessor<RunFooterTheme>
|
||||
variants: Accessor<string[]>
|
||||
|
||||
@@ -64,7 +64,8 @@ function buttons(
|
||||
)
|
||||
}
|
||||
|
||||
function RejectField(props: {
|
||||
/** @internal Exported to test managed textarea submission without permission navigation. */
|
||||
export function RejectField(props: {
|
||||
theme: RunFooterTheme
|
||||
text: string
|
||||
disabled: boolean
|
||||
@@ -107,6 +108,7 @@ function RejectField(props: {
|
||||
focusedBackgroundColor={props.theme.surface}
|
||||
cursorColor={props.theme.text}
|
||||
focused={!props.disabled}
|
||||
onSubmit={props.onConfirm}
|
||||
onContentChange={() => {
|
||||
if (!area || area.isDestroyed) {
|
||||
return
|
||||
@@ -119,11 +121,6 @@ function RejectField(props: {
|
||||
props.onCancel()
|
||||
return
|
||||
}
|
||||
|
||||
if (event.name === "return" && !event.meta && !event.ctrl && !event.shift) {
|
||||
event.preventDefault()
|
||||
props.onConfirm()
|
||||
}
|
||||
}}
|
||||
ref={(item) => {
|
||||
area = item
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
// Prompt textarea component and its state machine for direct interactive mode.
|
||||
//
|
||||
// createPromptState() wires keybinds, history navigation, leader-key sequences,
|
||||
// and `@` autocomplete for files, subagents, and MCP resources.
|
||||
// createPromptState() wires keymap command layers, history navigation, and
|
||||
// `@` autocomplete for files, subagents, and MCP resources.
|
||||
// It produces a PromptState that RunPromptBody renders as an OpenTUI textarea,
|
||||
// while the footer view renders the current menu state below it.
|
||||
/** @jsxImportSource @opentui/solid */
|
||||
import { pathToFileURL } from "bun"
|
||||
import { StyledText, bg, fg, type KeyBinding, type KeyEvent, type TextareaRenderable } from "@opentui/core"
|
||||
import { useKeyboard, useRenderer } from "@opentui/solid"
|
||||
import { StyledText, bg, fg, type KeyEvent, type TextareaRenderable } from "@opentui/core"
|
||||
import { useRenderer } from "@opentui/solid"
|
||||
import fuzzysort from "fuzzysort"
|
||||
import path from "path"
|
||||
import { createEffect, createMemo, createResource, createSignal, onCleanup, onMount, type Accessor } from "solid-js"
|
||||
@@ -21,15 +21,12 @@ import {
|
||||
mentionTriggerIndex,
|
||||
isNewCommand,
|
||||
movePromptHistory,
|
||||
promptCycle,
|
||||
promptHit,
|
||||
promptInfo,
|
||||
promptKeys,
|
||||
pushPromptHistory,
|
||||
} from "./prompt.shared"
|
||||
import { KILO_BASE_MODE, useBindings } from "@/cli/cmd/tui/keymap"
|
||||
import { FOOTER_MENU_ROWS, createFooterMenuState, type RunFooterMenuItem } from "./footer.menu"
|
||||
import type { RunFooterTheme } from "./theme"
|
||||
import type { FooterKeybinds, FooterState, RunAgent, RunCommand, RunPrompt, RunPromptPart, RunResource } from "./types"
|
||||
import type { FooterState, RunAgent, RunCommand, RunPrompt, RunPromptPart, RunResource, RunTuiConfig } from "./types"
|
||||
|
||||
const AUTOCOMPLETE_ROWS = FOOTER_MENU_ROWS
|
||||
const AUTOCOMPLETE_BOTTOM_ROWS = 1
|
||||
@@ -67,10 +64,9 @@ type PromptInput = {
|
||||
directory: string
|
||||
findFiles: (query: string) => Promise<string[]>
|
||||
agents: Accessor<RunAgent[]>
|
||||
subagents: Accessor<number>
|
||||
resources: Accessor<RunResource[]>
|
||||
commands: Accessor<RunCommand[] | undefined>
|
||||
keybinds: FooterKeybinds
|
||||
tuiConfig: RunTuiConfig
|
||||
state: Accessor<FooterState>
|
||||
view: Accessor<string>
|
||||
prompt: Accessor<boolean>
|
||||
@@ -83,14 +79,12 @@ type PromptInput = {
|
||||
onInputClear: () => void
|
||||
onExitRequest?: () => boolean
|
||||
onExit: () => void
|
||||
onSubagentMenu?: () => void
|
||||
onRows: (rows: number) => void
|
||||
onStatus: (text: string) => void
|
||||
}
|
||||
|
||||
export type PromptState = {
|
||||
placeholder: Accessor<StyledText | string>
|
||||
bindings: Accessor<KeyBinding[]>
|
||||
shell: Accessor<boolean>
|
||||
visible: Accessor<boolean>
|
||||
options: Accessor<PromptOption[]>
|
||||
@@ -103,6 +97,7 @@ export type PromptState = {
|
||||
onKeyDown: (event: KeyEvent) => void
|
||||
onContentChange: () => void
|
||||
replaceDraft: (text: string) => void
|
||||
replacePrompt: (prompt: RunPrompt) => void
|
||||
bind: (area?: TextareaRenderable) => void
|
||||
}
|
||||
|
||||
@@ -201,7 +196,6 @@ export function hintFlags(width: number) {
|
||||
export function RunPromptBody(props: {
|
||||
theme: () => RunFooterTheme
|
||||
placeholder: () => StyledText | string
|
||||
bindings: () => KeyBinding[]
|
||||
onSubmit: () => void
|
||||
onKeyDown: (event: KeyEvent) => void
|
||||
onContentChange: () => void
|
||||
@@ -265,7 +259,6 @@ export function RunPromptBody(props: {
|
||||
backgroundColor={props.theme().surface}
|
||||
focusedBackgroundColor={props.theme().surface}
|
||||
cursorColor={props.theme().text}
|
||||
keyBindings={props.bindings()}
|
||||
onSubmit={props.onSubmit}
|
||||
onKeyDown={props.onKeyDown}
|
||||
onPaste={() => {
|
||||
@@ -282,8 +275,6 @@ export function RunPromptBody(props: {
|
||||
}
|
||||
|
||||
export function createPromptState(input: PromptInput): PromptState {
|
||||
const keys = createMemo(() => promptKeys(input.keybinds))
|
||||
const bindings = createMemo(() => keys().bindings)
|
||||
const [shell, setShell] = createSignal(false)
|
||||
const placeholder = createMemo(() => {
|
||||
if (shell()) {
|
||||
@@ -303,8 +294,6 @@ export function createPromptState(input: PromptInput): PromptState {
|
||||
let draft: RunPrompt = { text: "", parts: [] }
|
||||
let stash: RunPrompt = { text: "", parts: [] }
|
||||
let area: TextareaRenderable | undefined
|
||||
let leader = false
|
||||
let timeout: NodeJS.Timeout | undefined
|
||||
let tick = false
|
||||
let prev = input.view()
|
||||
let type = 0
|
||||
@@ -463,24 +452,6 @@ export function createPromptState(input: PromptInput): PromptState {
|
||||
return visible() ? menu.rows() - 1 + AUTOCOMPLETE_BOTTOM_ROWS : 0
|
||||
})
|
||||
|
||||
const clear = () => {
|
||||
leader = false
|
||||
if (!timeout) {
|
||||
return
|
||||
}
|
||||
|
||||
clearTimeout(timeout)
|
||||
timeout = undefined
|
||||
}
|
||||
|
||||
const arm = () => {
|
||||
clear()
|
||||
leader = true
|
||||
timeout = setTimeout(() => {
|
||||
clear()
|
||||
}, input.keybinds.leaderTimeout)
|
||||
}
|
||||
|
||||
const hide = () => {
|
||||
setMode(false)
|
||||
setQuery("")
|
||||
@@ -744,7 +715,7 @@ export function createPromptState(input: PromptInput): PromptState {
|
||||
|
||||
const move = (dir: -1 | 1, event: KeyEvent) => {
|
||||
if (!area || area.isDestroyed) {
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
if (history.index === null && dir === -1) {
|
||||
@@ -753,7 +724,7 @@ export function createPromptState(input: PromptInput): PromptState {
|
||||
|
||||
const next = movePromptHistory(history, dir, area.plainText, area.cursorOffset)
|
||||
if (!next.apply || next.text === undefined || next.cursor === undefined) {
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
history = next.state
|
||||
@@ -761,28 +732,27 @@ export function createPromptState(input: PromptInput): PromptState {
|
||||
next.state.index === null ? stash : (next.state.items[next.state.index] ?? { text: next.text, parts: [] })
|
||||
restore(value, next.cursor)
|
||||
event.preventDefault()
|
||||
return true
|
||||
}
|
||||
|
||||
const cycle = (event: KeyEvent): boolean => {
|
||||
const next = promptCycle(leader, promptInfo(event), keys().leaders, keys().cycles)
|
||||
if (!next.consume) {
|
||||
return false
|
||||
const historyCommand = (dir: -1 | 1, event: KeyEvent) => {
|
||||
if (move(dir, event)) return
|
||||
if (!area || area.isDestroyed) return false
|
||||
|
||||
const endOffset = Bun.stringWidth(area.plainText)
|
||||
if (dir === -1 && area.visualCursor.visualRow === 0) {
|
||||
area.cursorOffset = 0
|
||||
}
|
||||
|
||||
if (next.clear) {
|
||||
clear()
|
||||
const end =
|
||||
typeof area.height === "number" && Number.isFinite(area.height) && area.height > 0
|
||||
? area.height - 1
|
||||
: Math.max(0, (area.virtualLineCount ?? 1) - 1)
|
||||
if (dir === 1 && area.visualCursor.visualRow === end) {
|
||||
area.cursorOffset = endOffset
|
||||
}
|
||||
|
||||
if (next.arm) {
|
||||
arm()
|
||||
}
|
||||
|
||||
if (next.cycle) {
|
||||
input.onCycle()
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
const requestExit = () => {
|
||||
@@ -822,12 +792,20 @@ export function createPromptState(input: PromptInput): PromptState {
|
||||
}
|
||||
|
||||
if (next.kind === "slash") {
|
||||
const text = `${next.display} ` // kilocode_change
|
||||
const cursor = area.cursorOffset
|
||||
const head = slashHead(area.plainText)
|
||||
const local = !shell() && (next.name === "new" || next.name === "exit")
|
||||
const separator = !shell() && !local && head && /\s/.test(area.plainText[head.end] ?? "") ? "" : " "
|
||||
const text = `${next.display}${separator}` // kilocode_change
|
||||
|
||||
area.cursorOffset = 0
|
||||
const start = area.logicalCursor
|
||||
area.cursorOffset = cursor
|
||||
area.cursorOffset =
|
||||
shell() || !head
|
||||
? cursor
|
||||
: local
|
||||
? Bun.stringWidth(area.plainText)
|
||||
: Bun.stringWidth(area.plainText.slice(0, head.end))
|
||||
const end = area.logicalCursor
|
||||
|
||||
area.deleteRange(start.row, start.col, end.row, end.col)
|
||||
@@ -835,6 +813,11 @@ export function createPromptState(input: PromptInput): PromptState {
|
||||
area.cursorOffset = Bun.stringWidth(text)
|
||||
hide()
|
||||
syncDraft()
|
||||
if (!shell()) {
|
||||
submitPrompt(clonePrompt(draft))
|
||||
return
|
||||
}
|
||||
|
||||
scheduleRows()
|
||||
area.focus()
|
||||
return
|
||||
@@ -914,178 +897,180 @@ export function createPromptState(input: PromptInput): PromptState {
|
||||
refresh()
|
||||
}
|
||||
|
||||
const onKeyDown = (event: KeyEvent) => {
|
||||
const key = promptInfo(event)
|
||||
if (visible()) {
|
||||
const name = event.name.toLowerCase()
|
||||
const ctrl = event.ctrl && !event.meta && !event.shift
|
||||
if (name === "up" || (ctrl && name === "p")) {
|
||||
event.preventDefault()
|
||||
if (options().length > 0) {
|
||||
menu.move(-1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (name === "down" || (ctrl && name === "n")) {
|
||||
event.preventDefault()
|
||||
if (options().length > 0) {
|
||||
menu.move(1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (name === "escape") {
|
||||
event.preventDefault()
|
||||
cancelAutocomplete()
|
||||
return
|
||||
}
|
||||
|
||||
if (name === "return") {
|
||||
if (mode() === "slash" && options().length === 0) {
|
||||
hide()
|
||||
return
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
select()
|
||||
return
|
||||
}
|
||||
|
||||
if (name === "tab") {
|
||||
if (mode() === "slash" && options().length === 0) {
|
||||
hide()
|
||||
return
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
const item = options()[menu.selected()]
|
||||
if (item?.kind === "mention" && item.directory) {
|
||||
expand()
|
||||
return
|
||||
}
|
||||
|
||||
select()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
key.name === "!" &&
|
||||
!shell() &&
|
||||
!event.ctrl &&
|
||||
!event.meta &&
|
||||
!event.super &&
|
||||
area &&
|
||||
!area.isDestroyed &&
|
||||
area.cursorOffset === 0
|
||||
) {
|
||||
event.preventDefault()
|
||||
setShellMode(true)
|
||||
return
|
||||
}
|
||||
|
||||
if (shell() && !visible()) {
|
||||
if (key.name === "escape") {
|
||||
event.preventDefault()
|
||||
setShellMode(false)
|
||||
return
|
||||
}
|
||||
|
||||
if (key.name === "backspace" && area && !area.isDestroyed && area.cursorOffset === 0) {
|
||||
event.preventDefault()
|
||||
setShellMode(false)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
key.name === "down" &&
|
||||
!visible() &&
|
||||
!event.ctrl &&
|
||||
!event.meta &&
|
||||
!event.shift &&
|
||||
!event.super &&
|
||||
area &&
|
||||
!area.isDestroyed &&
|
||||
area.plainText.length === 0 &&
|
||||
input.subagents() > 0
|
||||
) {
|
||||
event.preventDefault()
|
||||
input.onSubagentMenu?.()
|
||||
return
|
||||
}
|
||||
|
||||
if (promptHit(keys().clear, key)) {
|
||||
const handled = requestExit()
|
||||
if (handled) {
|
||||
event.preventDefault()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (promptHit(keys().interrupts, key)) {
|
||||
if (input.onInterrupt()) {
|
||||
event.preventDefault()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (cycle(event)) {
|
||||
return
|
||||
}
|
||||
|
||||
const up = promptHit(keys().previous, key)
|
||||
const down = promptHit(keys().next, key)
|
||||
if (!up && !down) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!area || area.isDestroyed) {
|
||||
return
|
||||
}
|
||||
|
||||
const dir = up ? -1 : 1
|
||||
const endOffset = Bun.stringWidth(area.plainText)
|
||||
if ((dir === -1 && area.cursorOffset === 0) || (dir === 1 && area.cursorOffset === endOffset)) {
|
||||
move(dir, event)
|
||||
return
|
||||
}
|
||||
|
||||
if (dir === -1 && area.visualCursor.visualRow === 0) {
|
||||
area.cursorOffset = 0
|
||||
}
|
||||
|
||||
const end =
|
||||
typeof area.height === "number" && Number.isFinite(area.height) && area.height > 0
|
||||
? area.height - 1
|
||||
: Math.max(0, (area.virtualLineCount ?? 1) - 1)
|
||||
if (dir === 1 && area.visualCursor.visualRow === end) {
|
||||
area.cursorOffset = endOffset
|
||||
}
|
||||
const baseBindingsEnabled = () => {
|
||||
const current = input.view()
|
||||
if (current === "command") return false
|
||||
if (current === "model") return false
|
||||
if (current === "variant") return false
|
||||
if (current === "queued-menu") return false
|
||||
if (current === "subagent-menu") return false
|
||||
return true
|
||||
}
|
||||
|
||||
useKeyboard((event) => {
|
||||
if (input.prompt()) {
|
||||
return
|
||||
}
|
||||
useBindings(() => ({
|
||||
mode: KILO_BASE_MODE,
|
||||
enabled: baseBindingsEnabled(),
|
||||
commands: [
|
||||
{
|
||||
name: "prompt.clear",
|
||||
title: "Clear prompt or exit",
|
||||
category: "Prompt",
|
||||
run() {
|
||||
if (requestExit()) return
|
||||
return false
|
||||
},
|
||||
},
|
||||
],
|
||||
bindings: input.tuiConfig.keybinds.get("prompt.clear"),
|
||||
}))
|
||||
|
||||
if (
|
||||
input.view() === "command" ||
|
||||
input.view() === "model" ||
|
||||
input.view() === "variant" ||
|
||||
input.view() === "subagent-menu"
|
||||
) {
|
||||
return
|
||||
}
|
||||
useBindings(() => ({
|
||||
mode: KILO_BASE_MODE,
|
||||
enabled: input.prompt(),
|
||||
commands: [
|
||||
{
|
||||
name: "session.interrupt",
|
||||
title: "Interrupt session",
|
||||
category: "Session",
|
||||
run() {
|
||||
if (input.onInterrupt()) return
|
||||
return false
|
||||
},
|
||||
},
|
||||
],
|
||||
bindings: input.tuiConfig.keybinds.get("session.interrupt"),
|
||||
}))
|
||||
|
||||
if (promptHit(keys().clear, promptInfo(event))) {
|
||||
const handled = requestExit()
|
||||
if (handled) {
|
||||
event.preventDefault()
|
||||
}
|
||||
}
|
||||
})
|
||||
useBindings(() => ({
|
||||
mode: KILO_BASE_MODE,
|
||||
enabled: input.prompt() && !visible(),
|
||||
commands: [
|
||||
{
|
||||
name: "prompt.history.previous",
|
||||
title: "Previous prompt history",
|
||||
category: "Prompt",
|
||||
run(ctx: { event: KeyEvent }) {
|
||||
return historyCommand(-1, ctx.event)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "prompt.history.next",
|
||||
title: "Next prompt history",
|
||||
category: "Prompt",
|
||||
run(ctx: { event: KeyEvent }) {
|
||||
return historyCommand(1, ctx.event)
|
||||
},
|
||||
},
|
||||
],
|
||||
bindings: [
|
||||
...input.tuiConfig.keybinds.get("prompt.history.previous"),
|
||||
...input.tuiConfig.keybinds.get("prompt.history.next"),
|
||||
],
|
||||
}))
|
||||
|
||||
useBindings(() => ({
|
||||
mode: KILO_BASE_MODE,
|
||||
enabled: input.prompt() && !visible(),
|
||||
bindings: [
|
||||
{
|
||||
key: "!",
|
||||
desc: "Shell mode",
|
||||
group: "Prompt",
|
||||
cmd() {
|
||||
if (shell()) return false
|
||||
if (!area || area.isDestroyed) return false
|
||||
if (area.cursorOffset !== 0) return false
|
||||
setShellMode(true)
|
||||
},
|
||||
},
|
||||
],
|
||||
}))
|
||||
|
||||
useBindings(() => ({
|
||||
mode: KILO_BASE_MODE,
|
||||
enabled: input.prompt() && shell() && !visible(),
|
||||
bindings: [
|
||||
{
|
||||
key: "escape",
|
||||
desc: "Exit shell mode",
|
||||
group: "Prompt",
|
||||
cmd: () => setShellMode(false),
|
||||
},
|
||||
{
|
||||
key: "backspace",
|
||||
desc: "Exit shell mode",
|
||||
group: "Prompt",
|
||||
cmd() {
|
||||
if (!area || area.isDestroyed) return false
|
||||
if (area.cursorOffset !== 0) return false
|
||||
setShellMode(false)
|
||||
},
|
||||
},
|
||||
],
|
||||
}))
|
||||
|
||||
useBindings(() => ({
|
||||
mode: KILO_BASE_MODE,
|
||||
enabled: input.prompt() && visible(),
|
||||
commands: [
|
||||
{
|
||||
name: "prompt.autocomplete.prev",
|
||||
title: "Previous autocomplete item",
|
||||
category: "Autocomplete",
|
||||
run: () => menu.move(-1),
|
||||
},
|
||||
{
|
||||
name: "prompt.autocomplete.next",
|
||||
title: "Next autocomplete item",
|
||||
category: "Autocomplete",
|
||||
run: () => menu.move(1),
|
||||
},
|
||||
{
|
||||
name: "prompt.autocomplete.hide",
|
||||
title: "Hide autocomplete",
|
||||
category: "Autocomplete",
|
||||
run: cancelAutocomplete,
|
||||
},
|
||||
{
|
||||
name: "prompt.autocomplete.select",
|
||||
title: "Select autocomplete item",
|
||||
category: "Autocomplete",
|
||||
run() {
|
||||
if (mode() === "slash" && options().length === 0) {
|
||||
hide()
|
||||
return
|
||||
}
|
||||
select()
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "prompt.autocomplete.complete",
|
||||
title: "Complete autocomplete item",
|
||||
category: "Autocomplete",
|
||||
run() {
|
||||
if (mode() === "slash" && options().length === 0) {
|
||||
hide()
|
||||
return
|
||||
}
|
||||
const item = options()[menu.selected()]
|
||||
if (item?.kind === "mention" && item.directory) {
|
||||
expand()
|
||||
return
|
||||
}
|
||||
select()
|
||||
},
|
||||
},
|
||||
],
|
||||
bindings: input.tuiConfig.keybinds.gather("run.prompt.autocomplete", [
|
||||
"prompt.autocomplete.prev",
|
||||
"prompt.autocomplete.next",
|
||||
"prompt.autocomplete.hide",
|
||||
"prompt.autocomplete.select",
|
||||
"prompt.autocomplete.complete",
|
||||
]),
|
||||
}))
|
||||
|
||||
const onKeyDown = (_event: KeyEvent) => {}
|
||||
|
||||
const submitPrompt = (next: RunPrompt) => {
|
||||
if (!area || area.isDestroyed) {
|
||||
@@ -1146,7 +1131,6 @@ export function createPromptState(input: PromptInput): PromptState {
|
||||
}
|
||||
|
||||
onCleanup(() => {
|
||||
clear()
|
||||
if (area && !area.isDestroyed) {
|
||||
area.off("line-info-change", scheduleRows)
|
||||
}
|
||||
@@ -1190,7 +1174,6 @@ export function createPromptState(input: PromptInput): PromptState {
|
||||
syncDraft()
|
||||
}
|
||||
|
||||
clear()
|
||||
hide()
|
||||
prev = kind
|
||||
if (kind !== "prompt") {
|
||||
@@ -1204,7 +1187,6 @@ export function createPromptState(input: PromptInput): PromptState {
|
||||
|
||||
return {
|
||||
placeholder,
|
||||
bindings,
|
||||
shell,
|
||||
visible,
|
||||
options,
|
||||
@@ -1221,6 +1203,7 @@ export function createPromptState(input: PromptInput): PromptState {
|
||||
scheduleRows()
|
||||
},
|
||||
replaceDraft,
|
||||
replacePrompt: restore,
|
||||
bind,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,10 +177,6 @@ export function RunQuestionBody(props: {
|
||||
return
|
||||
}
|
||||
|
||||
if (event.name === "return" && !event.shift && !event.ctrl && !event.meta) {
|
||||
saveCustom()
|
||||
event.preventDefault()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -496,6 +492,7 @@ export function RunQuestionBody(props: {
|
||||
focusedBackgroundColor={props.theme.surface}
|
||||
cursorColor={props.theme.text}
|
||||
focused={!disabled()}
|
||||
onSubmit={saveCustom}
|
||||
onContentChange={() => {
|
||||
if (!area || area.isDestroyed || disabled()) {
|
||||
return
|
||||
|
||||
@@ -24,25 +24,26 @@
|
||||
// Ctrl-c clears a live prompt draft first; otherwise interrupt and exit use a
|
||||
// two-press pattern where the first press shows a hint and the second press
|
||||
// within 5 seconds actually fires the action.
|
||||
import { CliRenderEvents, type CliRenderer, type TreeSitterClient } from "@opentui/core"
|
||||
import { CliRenderEvents, type CliRenderer, type KeyEvent, type Renderable, type TreeSitterClient } from "@opentui/core"
|
||||
import type { Keymap } from "@opentui/keymap"
|
||||
import { render } from "@opentui/solid"
|
||||
import { createComponent, createSignal, type Accessor, type Setter } from "solid-js"
|
||||
import { createStore, reconcile } from "solid-js/store"
|
||||
import { OpencodeKeymapProvider, formatKeyBindings } from "@/cli/cmd/tui/keymap"
|
||||
import { withRunSpan } from "./otel"
|
||||
import { RUN_COMMAND_PANEL_ROWS, RUN_SUBAGENT_PANEL_ROWS } from "./footer.command"
|
||||
import { RUN_INTERACTIVE_TERMINAL_ROWS } from "@/kilocode/cli/cmd/run/interactive-terminal" // kilocode_change
|
||||
import { SUBAGENT_INSPECTOR_ROWS } from "./footer.subagent"
|
||||
import { PROMPT_MAX_ROWS, TEXTAREA_MIN_ROWS } from "./footer.prompt"
|
||||
import { printableBinding } from "./prompt.shared"
|
||||
import { RunFooterView } from "./footer.view"
|
||||
import { RunScrollbackStream } from "./scrollback.surface"
|
||||
import type { RunTheme } from "./theme"
|
||||
import { RUN_THEME_FALLBACK, resolveRunTheme, type RunTheme } from "./theme"
|
||||
import type {
|
||||
FooterApi,
|
||||
FooterEvent,
|
||||
FooterKeybinds,
|
||||
FooterPatch,
|
||||
FooterPromptRoute,
|
||||
FooterQueuedPrompt,
|
||||
FooterState,
|
||||
FooterSubagentState,
|
||||
FooterView,
|
||||
@@ -56,6 +57,7 @@ import type {
|
||||
RunPrompt,
|
||||
RunProvider,
|
||||
RunResource,
|
||||
RunTuiConfig,
|
||||
StreamCommit,
|
||||
} from "./types"
|
||||
|
||||
@@ -81,7 +83,9 @@ type RunFooterOptions = {
|
||||
first: boolean
|
||||
history?: RunPrompt[]
|
||||
theme: RunTheme
|
||||
keybinds: FooterKeybinds
|
||||
keymap: Keymap<Renderable, KeyEvent>
|
||||
tuiConfig: RunTuiConfig
|
||||
backgroundSubagents: boolean
|
||||
diffStyle: RunDiffStyle
|
||||
onPermissionReply: (input: PermissionReply) => void | Promise<void>
|
||||
onQuestionReply: (input: QuestionReply) => void | Promise<void>
|
||||
@@ -93,6 +97,7 @@ type RunFooterOptions = {
|
||||
onModelSelect?: (model: NonNullable<RunInput["model"]>) => CycleResult | void | Promise<CycleResult | void>
|
||||
onVariantSelect?: (variant: string | undefined) => CycleResult | void | Promise<CycleResult | void>
|
||||
onInterrupt?: () => void
|
||||
onBackground?: () => void
|
||||
onExit?: () => void
|
||||
onSubagentSelect?: (sessionID: string | undefined) => void
|
||||
treeSitterClient?: TreeSitterClient
|
||||
@@ -105,6 +110,7 @@ const SUBAGENT_ROWS = RUN_SUBAGENT_PANEL_ROWS
|
||||
const MODEL_ROWS = RUN_COMMAND_PANEL_ROWS
|
||||
const VARIANT_ROWS = RUN_COMMAND_PANEL_ROWS
|
||||
const AUTOCOMPLETE_COMPACT_ROWS = 2
|
||||
const THEME_REFRESH_DELAYS = [1000, 1000] as const
|
||||
|
||||
function createEmptySubagentState(): FooterSubagentState {
|
||||
return {
|
||||
@@ -166,11 +172,13 @@ export class RunFooter implements FooterApi {
|
||||
private closed = false
|
||||
private destroyed = false
|
||||
private prompts = new Set<(input: RunPrompt) => void>()
|
||||
private queuedRemoves = new Set<(messageID: string) => boolean | Promise<boolean>>()
|
||||
private closes = new Set<() => void>()
|
||||
// Microtask-coalesced commit queue. Flushed on next microtask or on close/destroy.
|
||||
private queue: StreamCommit[] = []
|
||||
private pending = false
|
||||
private flushing: Promise<void> = Promise.resolve()
|
||||
private flushError: unknown
|
||||
// Fixed portion of footer height above the textarea.
|
||||
private base: number
|
||||
private rows = TEXTAREA_MIN_ROWS
|
||||
@@ -188,20 +196,42 @@ export class RunFooter implements FooterApi {
|
||||
private setVariants: Setter<string[]>
|
||||
private currentVariant: Accessor<string | undefined>
|
||||
private setCurrentVariant: Setter<string | undefined>
|
||||
private theme: Accessor<RunTheme>
|
||||
private setTheme: Setter<RunTheme>
|
||||
private state: Accessor<FooterState>
|
||||
private setState: Setter<FooterState>
|
||||
private view: Accessor<FooterView>
|
||||
private setView: Setter<FooterView>
|
||||
private subagent: Accessor<FooterSubagentState>
|
||||
private setSubagent: (next: FooterSubagentState) => void
|
||||
private queuedPrompts: Accessor<FooterQueuedPrompt[]>
|
||||
private setQueuedPrompts: Setter<FooterQueuedPrompt[]>
|
||||
private promptRoute: FooterPromptRoute = { type: "composer" }
|
||||
private subagentMenuRows = SUBAGENT_ROWS
|
||||
private autocomplete = false
|
||||
private interruptTimeout: NodeJS.Timeout | undefined
|
||||
private exitTimeout: NodeJS.Timeout | undefined
|
||||
private interruptHint: string
|
||||
private requestExitHandler: (() => boolean) | undefined
|
||||
private scrollback: RunScrollbackStream
|
||||
private themes: RunTheme[]
|
||||
private paletteRefreshRunning = false
|
||||
private paletteRefreshQueued = false
|
||||
private themeRefreshTimeouts: NodeJS.Timeout[] = []
|
||||
|
||||
private createScrollback(wrote: boolean): RunScrollbackStream {
|
||||
return new RunScrollbackStream(this.renderer, this.theme(), {
|
||||
diffStyle: this.options.diffStyle,
|
||||
wrote,
|
||||
sessionID: this.options.sessionID,
|
||||
treeSitterClient: this.options.treeSitterClient,
|
||||
onThemeRelease: (theme) => {
|
||||
void this.renderer
|
||||
.idle()
|
||||
.catch(() => {})
|
||||
.finally(() => this.destroyTheme(theme))
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
constructor(
|
||||
private renderer: CliRenderer,
|
||||
@@ -244,6 +274,10 @@ export class RunFooter implements FooterApi {
|
||||
const [currentVariant, setCurrentVariant] = createSignal(options.variant)
|
||||
this.currentVariant = currentVariant
|
||||
this.setCurrentVariant = setCurrentVariant
|
||||
const [theme, setTheme] = createSignal(options.theme)
|
||||
this.theme = theme
|
||||
this.setTheme = setTheme
|
||||
this.themes = [options.theme]
|
||||
const [subagent, setSubagent] = createStore<FooterSubagentState>(createEmptySubagentState())
|
||||
this.subagent = () => subagent
|
||||
this.setSubagent = (next) => {
|
||||
@@ -252,58 +286,69 @@ export class RunFooter implements FooterApi {
|
||||
setSubagent("permissions", reconcile(next.permissions, { key: "id" }))
|
||||
setSubagent("questions", reconcile(next.questions, { key: "id" }))
|
||||
}
|
||||
const [queuedPrompts, setQueuedPrompts] = createSignal<FooterQueuedPrompt[]>([])
|
||||
this.queuedPrompts = queuedPrompts
|
||||
this.setQueuedPrompts = setQueuedPrompts
|
||||
this.base = Math.max(1, renderer.footerHeight - TEXTAREA_MIN_ROWS)
|
||||
this.interruptHint = printableBinding(options.keybinds.interrupt, options.keybinds.leader) || "esc"
|
||||
this.scrollback = new RunScrollbackStream(renderer, options.theme, {
|
||||
diffStyle: options.diffStyle,
|
||||
wrote: options.wrote,
|
||||
sessionID: options.sessionID,
|
||||
treeSitterClient: options.treeSitterClient,
|
||||
})
|
||||
this.scrollback = this.createScrollback(options.wrote ?? false)
|
||||
|
||||
this.renderer.on(CliRenderEvents.DESTROY, this.handleDestroy)
|
||||
this.renderer.on(CliRenderEvents.PALETTE, this.handlePalette)
|
||||
this.renderer.on(CliRenderEvents.THEME_MODE, this.handleThemeRefresh)
|
||||
this.renderer.prependInputHandler(this.handleThemeNotification)
|
||||
process.on("SIGUSR2", this.handleThemeSignal)
|
||||
|
||||
const footer = this
|
||||
void render(
|
||||
() =>
|
||||
createComponent(RunFooterView, {
|
||||
directory: options.directory,
|
||||
state: this.state,
|
||||
view: this.view,
|
||||
subagent: this.subagent,
|
||||
findFiles: options.findFiles,
|
||||
agents: this.agents,
|
||||
resources: this.resources,
|
||||
commands: this.commands,
|
||||
providers: this.providers,
|
||||
currentModel: this.currentModel,
|
||||
variants: this.variants,
|
||||
currentVariant: this.currentVariant,
|
||||
theme: options.theme,
|
||||
diffStyle: options.diffStyle,
|
||||
keybinds: options.keybinds,
|
||||
history: options.history,
|
||||
agent: options.agentLabel,
|
||||
onSubmit: this.handlePrompt,
|
||||
onPermissionReply: this.handlePermissionReply,
|
||||
onQuestionReply: this.handleQuestionReply,
|
||||
onQuestionReject: this.handleQuestionReject,
|
||||
// kilocode_change start
|
||||
onTerminalWrite: options.onTerminalWrite,
|
||||
onTerminalResize: options.onTerminalResize,
|
||||
onTerminalClose: options.onTerminalClose,
|
||||
// kilocode_change end
|
||||
onCycle: this.handleCycle,
|
||||
onInterrupt: this.handleInterrupt,
|
||||
onInputClear: this.handleInputClear,
|
||||
onExitRequest: this.handleExit,
|
||||
onRequestExit: this.setRequestExitHandler,
|
||||
onExit: () => this.close(),
|
||||
onModelSelect: this.handleModelSelect,
|
||||
onVariantSelect: this.handleVariantSelect,
|
||||
onRows: this.syncRows,
|
||||
onLayout: this.syncLayout,
|
||||
onStatus: this.setStatus,
|
||||
onSubagentSelect: options.onSubagentSelect,
|
||||
createComponent(OpencodeKeymapProvider, {
|
||||
keymap: options.keymap,
|
||||
get children() {
|
||||
return createComponent(RunFooterView, {
|
||||
directory: options.directory,
|
||||
state: footer.state,
|
||||
view: footer.view,
|
||||
subagent: footer.subagent,
|
||||
queuedPrompts: footer.queuedPrompts,
|
||||
findFiles: options.findFiles,
|
||||
agents: footer.agents,
|
||||
resources: footer.resources,
|
||||
commands: footer.commands,
|
||||
providers: footer.providers,
|
||||
currentModel: footer.currentModel,
|
||||
variants: footer.variants,
|
||||
currentVariant: footer.currentVariant,
|
||||
theme: footer.theme,
|
||||
diffStyle: options.diffStyle,
|
||||
tuiConfig: options.tuiConfig,
|
||||
backgroundSubagents: options.backgroundSubagents,
|
||||
history: options.history,
|
||||
agent: options.agentLabel,
|
||||
onSubmit: footer.handlePrompt,
|
||||
onPermissionReply: footer.handlePermissionReply,
|
||||
onQuestionReply: footer.handleQuestionReply,
|
||||
onQuestionReject: footer.handleQuestionReject,
|
||||
// kilocode_change start
|
||||
onTerminalWrite: options.onTerminalWrite,
|
||||
onTerminalResize: options.onTerminalResize,
|
||||
onTerminalClose: options.onTerminalClose,
|
||||
// kilocode_change end
|
||||
onCycle: footer.handleCycle,
|
||||
onInterrupt: footer.handleInterrupt,
|
||||
onBackground: options.onBackground,
|
||||
onInputClear: footer.handleInputClear,
|
||||
onExitRequest: footer.handleExit,
|
||||
onRequestExit: footer.setRequestExitHandler,
|
||||
onExit: () => footer.close(),
|
||||
onModelSelect: footer.handleModelSelect,
|
||||
onVariantSelect: footer.handleVariantSelect,
|
||||
onRows: footer.syncRows,
|
||||
onLayout: footer.syncLayout,
|
||||
onStatus: footer.setStatus,
|
||||
onSubagentSelect: options.onSubagentSelect,
|
||||
onQueuedRemove: footer.handleQueuedRemove,
|
||||
})
|
||||
},
|
||||
}),
|
||||
this.renderer,
|
||||
).catch(() => {
|
||||
@@ -328,6 +373,13 @@ export class RunFooter implements FooterApi {
|
||||
}
|
||||
}
|
||||
|
||||
public onQueuedRemove(fn: (messageID: string) => boolean | Promise<boolean>): () => void {
|
||||
this.queuedRemoves.add(fn)
|
||||
return () => {
|
||||
this.queuedRemoves.delete(fn)
|
||||
}
|
||||
}
|
||||
|
||||
public onClose(fn: () => void): () => void {
|
||||
if (this.isClosed) {
|
||||
fn()
|
||||
@@ -373,6 +425,15 @@ export class RunFooter implements FooterApi {
|
||||
return
|
||||
}
|
||||
|
||||
if (next.type === "queued.prompts") {
|
||||
if (this.isGone) {
|
||||
return
|
||||
}
|
||||
|
||||
this.setQueuedPrompts(next.prompts)
|
||||
return
|
||||
}
|
||||
|
||||
const patch = eventPatch(next)
|
||||
if (patch) {
|
||||
this.patch(patch)
|
||||
@@ -443,7 +504,9 @@ export class RunFooter implements FooterApi {
|
||||
},
|
||||
),
|
||||
)
|
||||
.catch(() => {})
|
||||
.catch((error) => {
|
||||
this.flushError = error
|
||||
})
|
||||
}
|
||||
|
||||
private present(view: FooterView): void {
|
||||
@@ -501,6 +564,12 @@ export class RunFooter implements FooterApi {
|
||||
}
|
||||
|
||||
return this.flushing.then(async () => {
|
||||
if (this.flushError !== undefined) {
|
||||
const error = this.flushError
|
||||
this.flushError = undefined
|
||||
throw error
|
||||
}
|
||||
|
||||
if (this.isGone) {
|
||||
return
|
||||
}
|
||||
@@ -513,6 +582,30 @@ export class RunFooter implements FooterApi {
|
||||
})
|
||||
}
|
||||
|
||||
public resetForReplay(wrote: boolean): void {
|
||||
if (this.isGone) {
|
||||
return
|
||||
}
|
||||
|
||||
this.scrollback.destroy()
|
||||
this.scrollback = this.createScrollback(wrote)
|
||||
}
|
||||
|
||||
public currentTheme(): RunTheme {
|
||||
return this.theme()
|
||||
}
|
||||
|
||||
private destroyTheme(theme: RunTheme): void {
|
||||
const index = this.themes.indexOf(theme)
|
||||
if (index === -1) {
|
||||
return
|
||||
}
|
||||
|
||||
this.themes.splice(index, 1)
|
||||
theme.block.syntax?.destroy()
|
||||
theme.block.subtleSyntax?.destroy()
|
||||
}
|
||||
|
||||
public close(): void {
|
||||
if (this.closed) {
|
||||
return
|
||||
@@ -549,6 +642,11 @@ export class RunFooter implements FooterApi {
|
||||
this.requestExitHandler = fn
|
||||
}
|
||||
|
||||
private handleQueuedRemove = async (messageID: string): Promise<boolean> => {
|
||||
const fn = [...this.queuedRemoves][0]
|
||||
return fn ? await fn(messageID) : false
|
||||
}
|
||||
|
||||
private handleInputClear = (): void => {
|
||||
this.clearInterruptTimer()
|
||||
this.clearExitTimer()
|
||||
@@ -580,11 +678,13 @@ export class RunFooter implements FooterApi {
|
||||
? 1 + MODEL_ROWS
|
||||
: this.promptRoute.type === "variant"
|
||||
? 1 + VARIANT_ROWS
|
||||
: this.promptRoute.type === "subagent-menu"
|
||||
: this.promptRoute.type === "queued-menu"
|
||||
? 1 + this.subagentMenuRows
|
||||
: this.promptRoute.type === "subagent"
|
||||
? this.base + SUBAGENT_INSPECTOR_ROWS
|
||||
: Math.max(base + TEXTAREA_MIN_ROWS, Math.min(base + PROMPT_MAX_ROWS, base + this.rows))
|
||||
: this.promptRoute.type === "subagent-menu"
|
||||
? 1 + this.subagentMenuRows
|
||||
: this.promptRoute.type === "subagent"
|
||||
? this.base + SUBAGENT_INSPECTOR_ROWS
|
||||
: Math.max(base + TEXTAREA_MIN_ROWS, Math.min(base + PROMPT_MAX_ROWS, base + this.rows))
|
||||
|
||||
if (height !== this.renderer.footerHeight) {
|
||||
this.renderer.footerHeight = height
|
||||
@@ -692,7 +792,11 @@ export class RunFooter implements FooterApi {
|
||||
return
|
||||
}
|
||||
|
||||
const previous = this.currentModel()
|
||||
this.setCurrentModel(model)
|
||||
if (!previous || previous.providerID !== model.providerID || previous.modelID !== model.modelID) {
|
||||
this.setCurrentVariant(undefined)
|
||||
}
|
||||
void Promise.resolve()
|
||||
.then(() => this.options.onModelSelect?.(model))
|
||||
.then((result) => {
|
||||
@@ -794,6 +898,13 @@ export class RunFooter implements FooterApi {
|
||||
}, 5000)
|
||||
}
|
||||
|
||||
private interruptHint(): string {
|
||||
const bindings = this.options.keymap
|
||||
.getCommandBindings({ visibility: "registered", commands: ["session.interrupt"] })
|
||||
.get("session.interrupt")
|
||||
return formatKeyBindings(bindings, this.options.tuiConfig) || "esc"
|
||||
}
|
||||
|
||||
private clearExitTimer(): void {
|
||||
if (!this.exitTimeout) {
|
||||
return
|
||||
@@ -828,7 +939,7 @@ export class RunFooter implements FooterApi {
|
||||
|
||||
if (next < 2) {
|
||||
this.armInterruptTimer()
|
||||
this.patch({ status: `${this.interruptHint} again to interrupt` })
|
||||
this.patch({ status: `${this.interruptHint()} again to interrupt` })
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -860,6 +971,82 @@ export class RunFooter implements FooterApi {
|
||||
return true
|
||||
}
|
||||
|
||||
private handlePalette = (): void => {
|
||||
void resolveRunTheme(this.renderer).then((theme) => {
|
||||
if (this.isGone) {
|
||||
theme.block.syntax?.destroy()
|
||||
theme.block.subtleSyntax?.destroy()
|
||||
return
|
||||
}
|
||||
|
||||
// Keep the last known good theme when a runtime OSC probe times out.
|
||||
if (theme === RUN_THEME_FALLBACK) {
|
||||
return
|
||||
}
|
||||
|
||||
this.themes.push(theme)
|
||||
this.setTheme(theme)
|
||||
this.renderer.setBackgroundColor(theme.background)
|
||||
this.flushing = this.flushing
|
||||
.then(() => this.scrollback.setTheme(theme))
|
||||
.catch((error) => {
|
||||
this.flushError = error
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
private handleThemeNotification = (sequence: string): boolean => {
|
||||
if (sequence !== "\x1b[?997;1n" && sequence !== "\x1b[?997;2n") {
|
||||
return false
|
||||
}
|
||||
|
||||
// OpenTUI clears its palette cache only when dark/light mode changes.
|
||||
// Refresh for same-mode terminal theme swaps too.
|
||||
queueMicrotask(this.handleThemeRefresh)
|
||||
return false
|
||||
}
|
||||
|
||||
private handleThemeRefresh = (): void => {
|
||||
if (this.isGone) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.paletteRefreshRunning) {
|
||||
this.paletteRefreshQueued = true
|
||||
return
|
||||
}
|
||||
|
||||
this.paletteRefreshRunning = true
|
||||
const retry = this.renderer.paletteDetectionStatus === "detecting"
|
||||
this.renderer.clearPaletteCache()
|
||||
void this.renderer
|
||||
.getPalette({ size: 256 })
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
this.paletteRefreshRunning = false
|
||||
if (!retry && !this.paletteRefreshQueued) {
|
||||
return
|
||||
}
|
||||
|
||||
this.paletteRefreshQueued = false
|
||||
this.handleThemeRefresh()
|
||||
})
|
||||
}
|
||||
|
||||
public refreshTheme(): void {
|
||||
this.handleThemeRefresh()
|
||||
}
|
||||
|
||||
private handleThemeSignal = (): void => {
|
||||
// Omarchy signals immediately after requesting a terminal config reload.
|
||||
for (const timeout of this.themeRefreshTimeouts) clearTimeout(timeout)
|
||||
this.themeRefreshTimeouts = THEME_REFRESH_DELAYS.map((delay) =>
|
||||
setTimeout(() => {
|
||||
this.handleThemeRefresh()
|
||||
}, delay),
|
||||
)
|
||||
}
|
||||
|
||||
private handleDestroy = (): void => {
|
||||
if (this.destroyed) {
|
||||
return
|
||||
@@ -871,9 +1058,17 @@ export class RunFooter implements FooterApi {
|
||||
this.clearInterruptTimer()
|
||||
this.clearExitTimer()
|
||||
this.renderer.off(CliRenderEvents.DESTROY, this.handleDestroy)
|
||||
this.renderer.off(CliRenderEvents.PALETTE, this.handlePalette)
|
||||
this.renderer.off(CliRenderEvents.THEME_MODE, this.handleThemeRefresh)
|
||||
this.renderer.removeInputHandler(this.handleThemeNotification)
|
||||
process.off("SIGUSR2", this.handleThemeSignal)
|
||||
for (const timeout of this.themeRefreshTimeouts) clearTimeout(timeout)
|
||||
this.themeRefreshTimeouts.length = 0
|
||||
this.prompts.clear()
|
||||
this.queuedRemoves.clear()
|
||||
this.closes.clear()
|
||||
this.scrollback.destroy()
|
||||
for (const theme of [...this.themes]) this.destroyTheme(theme)
|
||||
}
|
||||
|
||||
// Drains the commit queue to scrollback. The surface manager owns grouping,
|
||||
@@ -903,6 +1098,8 @@ export class RunFooter implements FooterApi {
|
||||
},
|
||||
),
|
||||
)
|
||||
.catch(() => {})
|
||||
.catch((error) => {
|
||||
this.flushError = error
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
// All state comes from the parent RunFooter through SolidJS signals.
|
||||
// The view itself is stateless except for derived memos.
|
||||
/** @jsxImportSource @opentui/solid */
|
||||
import { useKeyboard, useTerminalDimensions } from "@opentui/solid"
|
||||
import { useTerminalDimensions } from "@opentui/solid"
|
||||
import { Match, Show, Switch, createEffect, createMemo, createSignal, onCleanup } from "solid-js"
|
||||
import "opentui-spinner/solid"
|
||||
import { createColors, createFrames } from "../tui/ui/spinner"
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
RUN_SUBAGENT_PANEL_ROWS,
|
||||
RunCommandMenuBody,
|
||||
RunModelSelectBody,
|
||||
RunQueuedPromptSelectBody,
|
||||
RunSubagentSelectBody,
|
||||
RunVariantSelectBody,
|
||||
} from "./footer.command"
|
||||
@@ -27,10 +28,16 @@ import { RunFooterSubagentBody } from "./footer.subagent"
|
||||
import { RunPromptBody, createPromptState, hintFlags } from "./footer.prompt"
|
||||
import { RunPermissionBody } from "./footer.permission"
|
||||
import { RunQuestionBody } from "./footer.question"
|
||||
import { printableBinding, promptBindings, promptHit, promptInfo } from "./prompt.shared"
|
||||
import {
|
||||
KILO_BASE_MODE,
|
||||
formatKeyBindings,
|
||||
useBindings,
|
||||
useKeymapSelector,
|
||||
type OpenTuiKeymap,
|
||||
} from "@/cli/cmd/tui/keymap"
|
||||
import type {
|
||||
FooterKeybinds,
|
||||
FooterPromptRoute,
|
||||
FooterQueuedPrompt,
|
||||
FooterState,
|
||||
FooterSubagentState,
|
||||
FooterView,
|
||||
@@ -44,8 +51,10 @@ import type {
|
||||
RunPrompt,
|
||||
RunProvider,
|
||||
RunResource,
|
||||
RunTuiConfig,
|
||||
} from "./types"
|
||||
import { RUN_THEME_FALLBACK, type RunTheme } from "./theme"
|
||||
import type { RunTheme } from "./theme"
|
||||
import { modelInfo } from "./variant.shared"
|
||||
|
||||
const EMPTY_BORDER = {
|
||||
topLeft: "",
|
||||
@@ -74,9 +83,11 @@ type RunFooterViewProps = {
|
||||
state: () => FooterState
|
||||
view?: () => FooterView
|
||||
subagent?: () => FooterSubagentState
|
||||
theme?: RunTheme
|
||||
queuedPrompts?: () => FooterQueuedPrompt[]
|
||||
theme: () => RunTheme
|
||||
diffStyle?: RunDiffStyle
|
||||
keybinds: FooterKeybinds
|
||||
tuiConfig: RunTuiConfig
|
||||
backgroundSubagents: boolean
|
||||
history?: RunPrompt[]
|
||||
agent: string
|
||||
onSubmit: (input: RunPrompt) => boolean
|
||||
@@ -88,6 +99,7 @@ type RunFooterViewProps = {
|
||||
onTerminalClose: (terminalID: string) => Promise<void> // kilocode_change
|
||||
onCycle: () => void
|
||||
onInterrupt: () => boolean
|
||||
onBackground?: () => void
|
||||
onInputClear: () => void
|
||||
onExitRequest?: () => boolean
|
||||
onRequestExit?: (fn: (() => boolean) | undefined) => void
|
||||
@@ -98,6 +110,7 @@ type RunFooterViewProps = {
|
||||
onLayout: (input: { route: FooterPromptRoute; autocomplete: boolean; subagentRows: number }) => void
|
||||
onStatus: (text: string) => void
|
||||
onSubagentSelect?: (sessionID: string | undefined) => void
|
||||
onQueuedRemove: (messageID: string) => Promise<boolean>
|
||||
}
|
||||
|
||||
export { TEXTAREA_MIN_ROWS, TEXTAREA_MAX_ROWS } from "./footer.prompt"
|
||||
@@ -117,13 +130,15 @@ export function RunFooterView(props: RunFooterViewProps) {
|
||||
})
|
||||
const [route, setRoute] = createSignal<FooterPromptRoute>({ type: "composer" })
|
||||
const [subagentMenuRows, setSubagentMenuRows] = createSignal(RUN_SUBAGENT_PANEL_ROWS)
|
||||
const queuedPrompts = createMemo(() => props.queuedPrompts?.() ?? [])
|
||||
const prompt = createMemo(() => active().type === "prompt" && route().type === "composer")
|
||||
const selectingSubagent = createMemo(() => active().type === "prompt" && route().type === "subagent-menu")
|
||||
const selectingQueued = createMemo(() => active().type === "prompt" && route().type === "queued-menu")
|
||||
const inspecting = createMemo(() => active().type === "prompt" && route().type === "subagent")
|
||||
const commanding = createMemo(() => active().type === "prompt" && route().type === "command")
|
||||
const modeling = createMemo(() => active().type === "prompt" && route().type === "model")
|
||||
const varianting = createMemo(() => active().type === "prompt" && route().type === "variant")
|
||||
const panel = createMemo(() => selectingSubagent() || commanding() || modeling() || varianting())
|
||||
const panel = createMemo(() => selectingQueued() || selectingSubagent() || commanding() || modeling() || varianting())
|
||||
const selected = createMemo(() => {
|
||||
const current = route()
|
||||
return current.type === "subagent" ? current.sessionID : undefined
|
||||
@@ -149,22 +164,84 @@ export function RunFooterView(props: RunFooterViewProps) {
|
||||
label: count === 1 ? "agent" : "agents",
|
||||
}
|
||||
})
|
||||
const foregroundSubagents = createMemo(
|
||||
() => props.backgroundSubagents && tabs().some((item) => item.status === "running" && !item.background),
|
||||
)
|
||||
const queuedIndicator = createMemo(() => {
|
||||
const count = queuedPrompts().length
|
||||
if (count === 0) return
|
||||
return { count }
|
||||
})
|
||||
const model = createMemo(() => {
|
||||
const current = props.currentModel()
|
||||
return current ? modelInfo(props.providers(), current) : { model: props.state().model, provider: undefined }
|
||||
})
|
||||
const detail = createMemo(() => {
|
||||
const current = route()
|
||||
return current.type === "subagent" ? subagent().details[current.sessionID] : undefined
|
||||
})
|
||||
const command = createMemo(() => printableBinding(props.keybinds.commandList, props.keybinds.leader))
|
||||
const interrupt = createMemo(() => printableBinding(props.keybinds.interrupt, props.keybinds.leader))
|
||||
const commandKeys = createMemo(() => promptBindings(props.keybinds.commandList, props.keybinds.leader))
|
||||
const command = useKeymapSelector(
|
||||
(keymap: OpenTuiKeymap) =>
|
||||
formatKeyBindings(
|
||||
keymap
|
||||
.getCommandBindings({ visibility: "registered", commands: ["command.palette.show"] })
|
||||
.get("command.palette.show"),
|
||||
props.tuiConfig,
|
||||
) ?? "",
|
||||
)
|
||||
const interrupt = useKeymapSelector(
|
||||
(keymap: OpenTuiKeymap) =>
|
||||
formatKeyBindings(
|
||||
keymap
|
||||
.getCommandBindings({ visibility: "registered", commands: ["session.interrupt"] })
|
||||
.get("session.interrupt"),
|
||||
props.tuiConfig,
|
||||
) ?? "",
|
||||
)
|
||||
const variantCycle = useKeymapSelector(
|
||||
(keymap: OpenTuiKeymap) =>
|
||||
formatKeyBindings(
|
||||
keymap.getCommandBindings({ visibility: "registered", commands: ["variant.cycle"] }).get("variant.cycle"),
|
||||
props.tuiConfig,
|
||||
) ?? "",
|
||||
)
|
||||
const queuedShortcut = useKeymapSelector(
|
||||
(keymap: OpenTuiKeymap) =>
|
||||
formatKeyBindings(
|
||||
keymap
|
||||
.getCommandBindings({ visibility: "registered", commands: ["session.queued_prompts"] })
|
||||
.get("session.queued_prompts"),
|
||||
props.tuiConfig,
|
||||
) ?? "",
|
||||
)
|
||||
const subagentShortcut = useKeymapSelector(
|
||||
(keymap: OpenTuiKeymap) =>
|
||||
formatKeyBindings(
|
||||
keymap
|
||||
.getCommandBindings({ visibility: "registered", commands: ["session.child.first"] })
|
||||
.get("session.child.first"),
|
||||
props.tuiConfig,
|
||||
) ?? "",
|
||||
)
|
||||
const backgroundShortcut = useKeymapSelector(
|
||||
(keymap: OpenTuiKeymap) =>
|
||||
formatKeyBindings(
|
||||
keymap
|
||||
.getCommandBindings({ visibility: "registered", commands: ["session.background"] })
|
||||
.get("session.background"),
|
||||
props.tuiConfig,
|
||||
) ?? "",
|
||||
)
|
||||
const hints = createMemo(() => hintFlags(term().width))
|
||||
const busy = createMemo(() => props.state().phase === "running")
|
||||
const armed = createMemo(() => props.state().interrupt > 0)
|
||||
const exiting = createMemo(() => props.state().exit > 0)
|
||||
const queue = createMemo(() => props.state().queue)
|
||||
const additionalQueue = createMemo(() => Math.max(0, queue() - queuedPrompts().length))
|
||||
const duration = createMemo(() => props.state().duration)
|
||||
const usage = createMemo(() => props.state().usage)
|
||||
const interruptKey = createMemo(() => interrupt() || "/exit")
|
||||
const runTheme = createMemo(() => props.theme ?? RUN_THEME_FALLBACK)
|
||||
const runTheme = createMemo(() => props.theme())
|
||||
const theme = createMemo(() => runTheme().footer)
|
||||
const block = createMemo(() => runTheme().block)
|
||||
const spin = createMemo(() => {
|
||||
@@ -230,6 +307,12 @@ export function RunFooterView(props: RunFooterViewProps) {
|
||||
props.onSubagentSelect?.(undefined)
|
||||
}
|
||||
|
||||
const openQueuedMenu = () => {
|
||||
if (queuedPrompts().length === 0) return
|
||||
setRoute({ type: "queued-menu" })
|
||||
props.onSubagentSelect?.(undefined)
|
||||
}
|
||||
|
||||
const closePanel = () => {
|
||||
setRoute({ type: "composer" })
|
||||
}
|
||||
@@ -264,10 +347,9 @@ export function RunFooterView(props: RunFooterViewProps) {
|
||||
directory: props.directory,
|
||||
findFiles: props.findFiles,
|
||||
agents: props.agents,
|
||||
subagents: () => tabs().length,
|
||||
resources: props.resources,
|
||||
commands: props.commands,
|
||||
keybinds: props.keybinds,
|
||||
tuiConfig: props.tuiConfig,
|
||||
state: props.state,
|
||||
view: promptView,
|
||||
prompt,
|
||||
@@ -280,7 +362,6 @@ export function RunFooterView(props: RunFooterViewProps) {
|
||||
onInputClear: props.onInputClear,
|
||||
onExitRequest: props.onExitRequest,
|
||||
onExit: props.onExit,
|
||||
onSubagentMenu: openSubagentMenu,
|
||||
onRows: props.onRows,
|
||||
onStatus: props.onStatus,
|
||||
})
|
||||
@@ -295,30 +376,71 @@ export function RunFooterView(props: RunFooterViewProps) {
|
||||
props.onRequestExit?.(undefined)
|
||||
})
|
||||
|
||||
useKeyboard((event) => {
|
||||
if (event.defaultPrevented) {
|
||||
return
|
||||
}
|
||||
useBindings(() => ({
|
||||
mode: KILO_BASE_MODE,
|
||||
enabled: active().type === "prompt" && route().type === "composer" && !composer.visible(),
|
||||
commands: [
|
||||
{
|
||||
name: "command.palette.show",
|
||||
title: "Open command palette",
|
||||
category: "Prompt",
|
||||
run: openCommand,
|
||||
},
|
||||
{
|
||||
name: "variant.cycle",
|
||||
title: "Cycle model variant",
|
||||
category: "Model",
|
||||
run: props.onCycle,
|
||||
},
|
||||
],
|
||||
bindings: [
|
||||
...props.tuiConfig.keybinds.get("command.palette.show"),
|
||||
...props.tuiConfig.keybinds.get("variant.cycle"),
|
||||
],
|
||||
}))
|
||||
|
||||
if (active().type !== "prompt") {
|
||||
return
|
||||
}
|
||||
useBindings(() => ({
|
||||
mode: KILO_BASE_MODE,
|
||||
enabled: active().type === "prompt" && route().type === "composer" && foregroundSubagents(),
|
||||
priority: 1,
|
||||
commands: [
|
||||
{
|
||||
name: "session.background",
|
||||
title: "Background subagents",
|
||||
category: "Session",
|
||||
run: () => props.onBackground?.(),
|
||||
},
|
||||
],
|
||||
bindings: props.tuiConfig.keybinds.get("session.background"),
|
||||
}))
|
||||
|
||||
if (route().type !== "composer") {
|
||||
return
|
||||
}
|
||||
useBindings(() => ({
|
||||
mode: KILO_BASE_MODE,
|
||||
enabled: active().type === "prompt" && route().type === "composer" && tabs().length > 0,
|
||||
commands: [
|
||||
{
|
||||
name: "session.child.first",
|
||||
title: "View subagents",
|
||||
category: "Session",
|
||||
run: openSubagentMenu,
|
||||
},
|
||||
],
|
||||
bindings: props.tuiConfig.keybinds.get("session.child.first"),
|
||||
}))
|
||||
|
||||
if (composer.visible()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!promptHit(commandKeys(), promptInfo(event))) {
|
||||
return
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
openCommand()
|
||||
})
|
||||
useBindings(() => ({
|
||||
mode: KILO_BASE_MODE,
|
||||
enabled: active().type === "prompt" && route().type === "composer" && queuedPrompts().length > 0,
|
||||
commands: [
|
||||
{
|
||||
name: "session.queued_prompts",
|
||||
title: "Manage queued prompts",
|
||||
category: "Session",
|
||||
run: openQueuedMenu,
|
||||
},
|
||||
],
|
||||
bindings: props.tuiConfig.keybinds.get("session.queued_prompts"),
|
||||
}))
|
||||
|
||||
createEffect(() => {
|
||||
const current = route()
|
||||
@@ -345,6 +467,11 @@ export function RunFooterView(props: RunFooterViewProps) {
|
||||
closePanel()
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
if (route().type !== "queued-menu" || queuedPrompts().length > 0) return
|
||||
closePanel()
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
if (active().type === "prompt") {
|
||||
return
|
||||
@@ -355,6 +482,7 @@ export function RunFooterView(props: RunFooterViewProps) {
|
||||
current.type !== "command" &&
|
||||
current.type !== "model" &&
|
||||
current.type !== "variant" &&
|
||||
current.type !== "queued-menu" &&
|
||||
current.type !== "subagent-menu"
|
||||
) {
|
||||
return
|
||||
@@ -417,7 +545,6 @@ export function RunFooterView(props: RunFooterViewProps) {
|
||||
<RunPromptBody
|
||||
theme={theme}
|
||||
placeholder={composer.placeholder}
|
||||
bindings={composer.bindings}
|
||||
onSubmit={composer.onSubmit}
|
||||
onKeyDown={composer.onKeyDown}
|
||||
onContentChange={composer.onContentChange}
|
||||
@@ -434,16 +561,32 @@ export function RunFooterView(props: RunFooterViewProps) {
|
||||
onRows={setSubagentMenuRows}
|
||||
/>
|
||||
</Match>
|
||||
<Match when={selectingQueued()}>
|
||||
<RunQueuedPromptSelectBody
|
||||
theme={theme}
|
||||
prompts={queuedPrompts}
|
||||
onClose={closePanel}
|
||||
onDelete={(item) => void props.onQueuedRemove(item.messageID)}
|
||||
onEdit={async (item) => {
|
||||
if (!(await props.onQueuedRemove(item.messageID))) return
|
||||
closePanel()
|
||||
queueMicrotask(() => composer.replacePrompt(item.prompt))
|
||||
}}
|
||||
onRows={setSubagentMenuRows}
|
||||
/>
|
||||
</Match>
|
||||
<Match when={commanding()}>
|
||||
<RunCommandMenuBody
|
||||
theme={theme}
|
||||
commands={props.commands}
|
||||
subagents={tabs}
|
||||
queued={queuedPrompts}
|
||||
variants={props.variants}
|
||||
keybinds={props.keybinds}
|
||||
variantCycle={variantCycle()}
|
||||
onClose={closePanel}
|
||||
onModel={openModel}
|
||||
onSubagent={openSubagentMenu}
|
||||
onQueued={openQueuedMenu}
|
||||
onVariant={openVariant}
|
||||
onVariantCycle={() => {
|
||||
props.onCycle()
|
||||
@@ -529,16 +672,33 @@ export function RunFooterView(props: RunFooterViewProps) {
|
||||
{shell() ? "Shell" : props.agent}
|
||||
</text>
|
||||
<Show when={!shell()}>
|
||||
<text
|
||||
id="run-direct-footer-model"
|
||||
fg={theme().text}
|
||||
wrapMode="none"
|
||||
truncate
|
||||
flexGrow={1}
|
||||
flexShrink={1}
|
||||
>
|
||||
{props.state().model}
|
||||
</text>
|
||||
<box id="run-direct-footer-model" flexDirection="row" gap={1} flexGrow={1} flexShrink={1}>
|
||||
<text fg={theme().muted} wrapMode="none" flexShrink={0}>
|
||||
·
|
||||
</text>
|
||||
<text fg={theme().text} wrapMode="none" truncate flexShrink={1}>
|
||||
{model().model}
|
||||
</text>
|
||||
<Show when={model().provider}>
|
||||
{(provider) => (
|
||||
<text fg={theme().muted} wrapMode="none" truncate flexShrink={1}>
|
||||
{provider()}
|
||||
</text>
|
||||
)}
|
||||
</Show>
|
||||
<Show when={props.currentVariant()}>
|
||||
{(variant) => (
|
||||
<>
|
||||
<text fg={theme().muted} wrapMode="none" flexShrink={0}>
|
||||
·
|
||||
</text>
|
||||
<text wrapMode="none" truncate flexShrink={1}>
|
||||
<span style={{ fg: theme().warning, bold: true }}>{variant()}</span>
|
||||
</text>
|
||||
</>
|
||||
)}
|
||||
</Show>
|
||||
</box>
|
||||
</Show>
|
||||
</box>
|
||||
</Show>
|
||||
@@ -611,7 +771,9 @@ export function RunFooterView(props: RunFooterViewProps) {
|
||||
gap={1}
|
||||
flexShrink={0}
|
||||
>
|
||||
<Show when={busy() || exiting() || duration().length > 0 || subagentIndicator()}>
|
||||
<Show
|
||||
when={busy() || exiting() || duration().length > 0 || queuedIndicator() || subagentIndicator()}
|
||||
>
|
||||
<box id="run-direct-footer-hint-left" flexDirection="row" gap={1} flexShrink={0} marginLeft={1}>
|
||||
<Show when={exiting()}>
|
||||
<text id="run-direct-footer-hint-exit" fg={theme().highlight} wrapMode="none" truncate>
|
||||
@@ -656,13 +818,25 @@ export function RunFooterView(props: RunFooterViewProps) {
|
||||
<Show when={subagentIndicator()}>
|
||||
{(info) => (
|
||||
<text id="run-direct-footer-subagents-label" fg={theme().text} wrapMode="none" truncate>
|
||||
<Show when={busy() || exiting() || duration().length > 0}>
|
||||
<span style={{ fg: theme().muted }}>· </span>
|
||||
</Show>
|
||||
{info().count} <span style={{ fg: theme().muted }}>{info().label}</span>
|
||||
<span style={{ fg: theme().muted }}> · </span>
|
||||
<span style={{ fg: theme().highlight }}>↓</span>
|
||||
<span style={{ fg: theme().muted }}> to view</span>
|
||||
<span style={{ fg: theme().highlight }}>• </span>
|
||||
{info().count} <span style={{ fg: theme().muted }}>{info().label} </span>
|
||||
<span style={{ fg: theme().highlight }}>{subagentShortcut() || "leader+down"}</span>
|
||||
</text>
|
||||
)}
|
||||
</Show>
|
||||
<Show when={foregroundSubagents() && backgroundShortcut()}>
|
||||
<text id="run-direct-footer-background-label" fg={theme().text} wrapMode="none" truncate>
|
||||
<span style={{ fg: theme().highlight }}>• </span>
|
||||
<span style={{ fg: theme().highlight }}>{backgroundShortcut()}</span>{" "}
|
||||
<span style={{ fg: theme().muted }}>background</span>
|
||||
</text>
|
||||
</Show>
|
||||
<Show when={queuedIndicator()}>
|
||||
{(info) => (
|
||||
<text id="run-direct-footer-queued-label" fg={theme().text} wrapMode="none" truncate>
|
||||
<span style={{ fg: theme().warning }}>• </span>
|
||||
{info().count} <span style={{ fg: theme().muted }}>queued </span>
|
||||
<span style={{ fg: theme().highlight }}>{queuedShortcut() || "leader+q"}</span>
|
||||
</text>
|
||||
)}
|
||||
</Show>
|
||||
@@ -682,9 +856,9 @@ export function RunFooterView(props: RunFooterViewProps) {
|
||||
when={shell()}
|
||||
fallback={
|
||||
<>
|
||||
<Show when={queue() > 0}>
|
||||
<Show when={additionalQueue() > 0}>
|
||||
<text id="run-direct-footer-queue" fg={theme().muted} wrapMode="none" truncate>
|
||||
{queue()} queued
|
||||
{additionalQueue()} queued
|
||||
</text>
|
||||
</Show>
|
||||
<Show when={usage().length > 0}>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user