@loicngr/kobo 1.2.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +14 -0
- package/dist/mcp-server/kobo-tasks-server.js +2 -0
- package/dist/server/db/index.js +1 -0
- package/dist/server/db/migrations.js +72 -10
- package/dist/server/db/schema.js +5 -0
- package/dist/server/index.js +63 -9
- package/dist/server/routes/workspaces.js +208 -52
- package/dist/server/services/agent-manager.js +101 -9
- package/dist/server/services/notion-service.js +6 -3
- package/dist/server/services/pr-watcher-service.js +82 -0
- package/dist/server/services/settings-service.js +41 -22
- package/dist/server/services/websocket-service.js +41 -4
- package/dist/server/services/workspace-service.js +25 -2
- package/dist/server/utils/git-ops.js +200 -4
- package/dist/server/utils/paths.js +13 -0
- package/dist/server/utils/process-tracker.js +0 -4
- package/package.json +4 -3
- package/src/client/dist/spa/assets/ActivityFeed-Dxuw_8et.js +60 -0
- package/src/client/dist/spa/assets/ActivityFeed-OvgJQL4-.css +1 -0
- package/src/client/dist/spa/assets/CreatePage-CTFi3DpD.js +2 -0
- package/src/client/dist/spa/assets/CreatePage-DOr3puTt.css +1 -0
- package/src/client/dist/spa/assets/DiffViewer-7dck6mJc.css +1 -0
- package/src/client/dist/spa/assets/DiffViewer-DV9gt8DT.js +2 -0
- package/src/client/dist/spa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuYjalmUiAw-k1h7X_-h.woff +0 -0
- package/src/client/dist/spa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuZtalmUiAw-B7du-70m.woff +0 -0
- package/src/client/dist/spa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuaabVmUiAw-CoAZ_DKt.woff +0 -0
- package/src/client/dist/spa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWub2bVmUiAw-D0406B4n.woff +0 -0
- package/src/client/dist/spa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWubEbFmUiAw-CnAg2DeQ.woff +0 -0
- package/src/client/dist/spa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWubEbVmUiAw-BG9VWE5v.woff +0 -0
- package/src/client/dist/spa/assets/MainLayout-BxqZy-kp.js +2 -0
- package/src/client/dist/spa/assets/MainLayout-gjAr74zR.css +1 -0
- package/src/client/dist/spa/assets/QBadge-Y5QfSDtm.js +1 -0
- package/src/client/dist/spa/assets/QBtn-D_bkYnrl.js +1 -0
- package/src/client/dist/spa/assets/QExpansionItem-sghN-B7_.js +1 -0
- package/src/client/dist/spa/assets/QPage-DL4rY7LD.js +1 -0
- package/src/client/dist/spa/assets/QSeparator-MRAsTeNf.js +1 -0
- package/src/client/dist/spa/assets/QSpinnerDots-Bu0GloxK.js +1 -0
- package/src/client/dist/spa/assets/QTooltip-Cuj49WIu.js +1 -0
- package/src/client/dist/spa/assets/SettingsPage-50Nqrcsk.js +1 -0
- package/src/client/dist/spa/assets/SettingsPage-DV5avRbc.css +1 -0
- package/src/client/dist/spa/assets/WorkspacePage-CFC48jKO.css +1 -0
- package/src/client/dist/spa/assets/WorkspacePage-L46GJjcy.js +2 -0
- package/src/client/dist/spa/assets/_plugin-vue_export-helper-Bo392ayB.js +1 -0
- package/src/client/dist/spa/assets/abap-Co3wj02O.js +1 -0
- package/src/client/dist/spa/assets/apex-CUKwGs62.js +1 -0
- package/src/client/dist/spa/assets/azcli-DMImymmY.js +1 -0
- package/src/client/dist/spa/assets/bat--P_y70-E.js +1 -0
- package/src/client/dist/spa/assets/bicep-C3w6oSfK.js +2 -0
- package/src/client/dist/spa/assets/cameligo-D9NSR4Rj.js +1 -0
- package/src/client/dist/spa/assets/clojure-BMcQme0t.js +1 -0
- package/src/client/dist/spa/assets/codicon-CgENjH2v.ttf +0 -0
- package/src/client/dist/spa/assets/coffee-BbMZaWx7.js +1 -0
- package/src/client/dist/spa/assets/cpp-CbrtEGgw.js +1 -0
- package/src/client/dist/spa/assets/csharp-Bc0fjUxA.js +1 -0
- package/src/client/dist/spa/assets/csp-DmbXuMT0.js +1 -0
- package/src/client/dist/spa/assets/css-gdwCt5by.js +3 -0
- package/src/client/dist/spa/assets/css.worker-D1piIYC4.js +102 -0
- package/src/client/dist/spa/assets/cssMode-DO8hqIpD.js +4 -0
- package/src/client/dist/spa/assets/cypher-ocmmfoQr.js +1 -0
- package/src/client/dist/spa/assets/dart-DbZ5eklb.js +1 -0
- package/src/client/dist/spa/assets/dockerfile-BLaMayDc.js +1 -0
- package/src/client/dist/spa/assets/ecl-LxXpHirr.js +1 -0
- package/src/client/dist/spa/assets/editor-COGk2gAX.css +1 -0
- package/src/client/dist/spa/assets/editor-CS3NEPi9.css +1 -0
- package/src/client/dist/spa/assets/editor.api-BZP41lht.js +818 -0
- package/src/client/dist/spa/assets/editor.main-BOjf9Jyl.js +53 -0
- package/src/client/dist/spa/assets/editor.worker-CJ9iTmkr.js +26 -0
- package/src/client/dist/spa/assets/elixir-C_geKt5o.js +1 -0
- package/src/client/dist/spa/assets/flUhRq6tzZclQEJ-Vdg-IuiaDsNa-OUIwM9U8.woff +0 -0
- package/src/client/dist/spa/assets/flow9-DE2fI2ca.js +1 -0
- package/src/client/dist/spa/assets/formatters-CXx5Gzsp.js +1 -0
- package/src/client/dist/spa/assets/freemarker2-QAd0phKD.js +3 -0
- package/src/client/dist/spa/assets/fsharp-CJD6fImD.js +1 -0
- package/src/client/dist/spa/assets/go-jUCqQ7bD.js +1 -0
- package/src/client/dist/spa/assets/graphql-rw7g9h7D.js +1 -0
- package/src/client/dist/spa/assets/handlebars-D40ZA-yu.js +1 -0
- package/src/client/dist/spa/assets/hcl-BKX27Mn7.js +1 -0
- package/src/client/dist/spa/assets/html-Bzo97Bk0.js +1 -0
- package/src/client/dist/spa/assets/html.worker-C4q4XMPn.js +509 -0
- package/src/client/dist/spa/assets/htmlMode-7HShfg96.js +4 -0
- package/src/client/dist/spa/assets/i18n-BiMAFoN_.js +1 -0
- package/src/client/dist/spa/assets/index-CaOiQq0z.js +5 -0
- package/src/client/dist/spa/assets/{index-BThMCiY7.css → index-eX_lKHSg.css} +1 -1
- package/src/client/dist/spa/assets/ini-CrXjga2H.js +1 -0
- package/src/client/dist/spa/assets/java-D4jksGBb.js +1 -0
- package/src/client/dist/spa/assets/javascript-DpFlF6yx.js +1 -0
- package/src/client/dist/spa/assets/json.worker-C9p7xCYk.js +65 -0
- package/src/client/dist/spa/assets/jsonMode-DxEb1VXU.js +10 -0
- package/src/client/dist/spa/assets/julia-CbWxfkeS.js +1 -0
- package/src/client/dist/spa/assets/kotlin-B26Yx80V.js +1 -0
- package/src/client/dist/spa/assets/less-DFzn-zC9.js +2 -0
- package/src/client/dist/spa/assets/lexon-C-w-W8Yv.js +1 -0
- package/src/client/dist/spa/assets/liquid-IpMvWkVS.js +1 -0
- package/src/client/dist/spa/assets/lua-CHuE_HoG.js +1 -0
- package/src/client/dist/spa/assets/m3-DEFZN2qS.js +1 -0
- package/src/client/dist/spa/assets/markdown-Cbt4TlFt.js +1 -0
- package/src/client/dist/spa/assets/mdx-BM5S9XtA.js +1 -0
- package/src/client/dist/spa/assets/mips-C6m4XECw.js +1 -0
- package/src/client/dist/spa/assets/monaco.contribution-Cpcgk43V.js +2 -0
- package/src/client/dist/spa/assets/msdax-un0CFb_S.js +1 -0
- package/src/client/dist/spa/assets/mysql-CuAPeiOV.js +1 -0
- package/src/client/dist/spa/assets/nodes-Bo-5xQjA.js +1 -0
- package/src/client/dist/spa/assets/objective-c-DLVMdxAC.js +1 -0
- package/src/client/dist/spa/assets/pascal-BGCThuPY.js +1 -0
- package/src/client/dist/spa/assets/pascaligo-DfxSVpdo.js +1 -0
- package/src/client/dist/spa/assets/perl-BOE6y94t.js +1 -0
- package/src/client/dist/spa/assets/pgsql-Dn7JkY4F.js +1 -0
- package/src/client/dist/spa/assets/php-r1gD0KyT.js +1 -0
- package/src/client/dist/spa/assets/pla-CgXknhb0.js +1 -0
- package/src/client/dist/spa/assets/position-engine-CDz__T_5.js +1 -0
- package/src/client/dist/spa/assets/postiats-CsIEtnRB.js +1 -0
- package/src/client/dist/spa/assets/powerquery-yNJCmC_6.js +1 -0
- package/src/client/dist/spa/assets/powershell-CQcz1SqH.js +1 -0
- package/src/client/dist/spa/assets/protobuf-BmC34uvO.js +2 -0
- package/src/client/dist/spa/assets/pug-C20znvWM.js +1 -0
- package/src/client/dist/spa/assets/python-CBiKH2mZ.js +1 -0
- package/src/client/dist/spa/assets/qsharp-B7bnARMS.js +1 -0
- package/src/client/dist/spa/assets/r-ClvcLdqC.js +1 -0
- package/src/client/dist/spa/assets/razor-BV3hIY51.js +1 -0
- package/src/client/dist/spa/assets/redis-DCyda7_S.js +1 -0
- package/src/client/dist/spa/assets/redshift-BtWDr4pb.js +1 -0
- package/src/client/dist/spa/assets/restructuredtext-CLcnlkhl.js +1 -0
- package/src/client/dist/spa/assets/ruby-DY0SOSSZ.js +1 -0
- package/src/client/dist/spa/assets/runtime-core.esm-bundler-BLPLlWMG.js +1 -0
- package/src/client/dist/spa/assets/rust-JQd-fJZI.js +1 -0
- package/src/client/dist/spa/assets/sb-BV2j8yFF.js +1 -0
- package/src/client/dist/spa/assets/scala-DwbnREDs.js +1 -0
- package/src/client/dist/spa/assets/scheme-CrtA-vei.js +1 -0
- package/src/client/dist/spa/assets/scss-VxQz3zmI.js +3 -0
- package/src/client/dist/spa/assets/shell-CP9faqFI.js +1 -0
- package/src/client/dist/spa/assets/solidity-9IIb0b89.js +1 -0
- package/src/client/dist/spa/assets/sophia-D2LQU2AD.js +1 -0
- package/src/client/dist/spa/assets/sparql-DONCa5dy.js +1 -0
- package/src/client/dist/spa/assets/sql-DaAAHGEt.js +1 -0
- package/src/client/dist/spa/assets/st-CRY2V-j3.js +1 -0
- package/src/client/dist/spa/assets/swift-BlKbfloF.js +1 -0
- package/src/client/dist/spa/assets/systemverilog-B_h9Q_T_.js +1 -0
- package/src/client/dist/spa/assets/tcl-C4wN3A6M.js +1 -0
- package/src/client/dist/spa/assets/ts.worker-Cj3zTgVE.js +51353 -0
- package/src/client/dist/spa/assets/tsMode-DUqyritq.js +11 -0
- package/src/client/dist/spa/assets/twig-DDdaBLC9.js +1 -0
- package/src/client/dist/spa/assets/typescript-BvZDZzaz.js +1 -0
- package/src/client/dist/spa/assets/typespec-Dc1ipt8A.js +1 -0
- package/src/client/dist/spa/assets/use-checkbox-Dwcwf6Nj.js +1 -0
- package/src/client/dist/spa/assets/use-quasar-DMvrrord.js +1 -0
- package/src/client/dist/spa/assets/vb-C4BXIvrh.js +1 -0
- package/src/client/dist/spa/assets/vue-i18n-CoZsbeQK.js +3 -0
- package/src/client/dist/spa/assets/wgsl-XVg3Pi-r.js +298 -0
- package/src/client/dist/spa/assets/xml-BgsHEniP.js +1 -0
- package/src/client/dist/spa/assets/yaml-C-Mr6Xov.js +1 -0
- package/src/client/dist/spa/index.html +5 -3
- package/src/mcp-server/kobo-tasks-server.ts +2 -0
- package/src/client/dist/spa/assets/ActivityFeed-CPfYmybV.js +0 -60
- package/src/client/dist/spa/assets/ActivityFeed-DBljh9rq.css +0 -1
- package/src/client/dist/spa/assets/CreatePage-BlgXsrJO.css +0 -1
- package/src/client/dist/spa/assets/CreatePage-C_c3Gr0F.js +0 -2
- package/src/client/dist/spa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuYjalmUiAw-BepdiOnY.woff +0 -0
- package/src/client/dist/spa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuZtalmUiAw-4ZhHFPot.woff +0 -0
- package/src/client/dist/spa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuaabVmUiAw-CNa4tw4G.woff +0 -0
- package/src/client/dist/spa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWub2bVmUiAw-CHKg1YId.woff +0 -0
- package/src/client/dist/spa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWubEbFmUiAw-yBxCyPWP.woff +0 -0
- package/src/client/dist/spa/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWubEbVmUiAw-3fZ6d7DD.woff +0 -0
- package/src/client/dist/spa/assets/MainLayout-BMxEROm4.css +0 -1
- package/src/client/dist/spa/assets/MainLayout-QtbVbbnd.js +0 -1
- package/src/client/dist/spa/assets/QBadge-CNojh9Rl.js +0 -1
- package/src/client/dist/spa/assets/QDialog-DgR7t6Vf.js +0 -1
- package/src/client/dist/spa/assets/QExpansionItem-VVjlYOIT.js +0 -1
- package/src/client/dist/spa/assets/QPage-DX4g-Dpe.js +0 -1
- package/src/client/dist/spa/assets/QSpinnerDots-DeCf9Lr-.js +0 -1
- package/src/client/dist/spa/assets/QTooltip-DKYJ8kVW.js +0 -1
- package/src/client/dist/spa/assets/SettingsPage-DjWKsLC-.js +0 -1
- package/src/client/dist/spa/assets/SettingsPage-Yv31Z9aG.css +0 -1
- package/src/client/dist/spa/assets/WorkspacePage-DkM58caD.css +0 -1
- package/src/client/dist/spa/assets/WorkspacePage-EAh91w9s.js +0 -2
- package/src/client/dist/spa/assets/_plugin-vue_export-helper-C6NdfBK4.js +0 -1
- package/src/client/dist/spa/assets/flUhRq6tzZclQEJ-Vdg-IuiaDsNa-Dr0goTwe.woff +0 -0
- package/src/client/dist/spa/assets/index-C4WDJfjD.js +0 -5
- package/src/client/dist/spa/assets/nodes-irfhA8FK.js +0 -1
- package/src/client/dist/spa/assets/use-checkbox-BS9cbwg_.js +0 -1
- package/src/client/dist/spa/assets/use-quasar-CH0pSHUf.js +0 -1
package/AGENTS.md
CHANGED
|
@@ -171,6 +171,19 @@ See the "Notion integration" section of the README for the end-user setup guide.
|
|
|
171
171
|
|
|
172
172
|
**Dependencies** — root `package.json` covers backend + tests. `src/client/package.json` is a separate npm tree. Install both.
|
|
173
173
|
|
|
174
|
+
## Internationalization (i18n)
|
|
175
|
+
|
|
176
|
+
The frontend uses `vue-i18n` v10 with 5 supported locales: English (`en`), French (`fr`), German (`de`), Spanish (`es`), Italian (`it`). Translation files live in `src/client/src/i18n/`.
|
|
177
|
+
|
|
178
|
+
**Mandatory rules for all frontend code:**
|
|
179
|
+
|
|
180
|
+
- **NEVER hardcode user-visible text** in Vue templates or scripts. Always use `$t('key')` in templates and `t('key')` in `<script setup>` (via `const { t } = useI18n()`).
|
|
181
|
+
- When adding or modifying a text, **update ALL 5 locale files** (`en.ts`, `fr.ts`, `de.ts`, `es.ts`, `it.ts`) with the corresponding translation.
|
|
182
|
+
- Keys follow the pattern `'component.label'` (e.g. `'git.push'`, `'settings.title'`, `'common.save'`). Use the existing key structure as reference.
|
|
183
|
+
- Keep technical terms in English across all locales when that's the industry convention (Git, PR, Push, Diff, Commit, Branch, Tokens, etc.).
|
|
184
|
+
- Placeholders like `{count}`, `{n}`, `{query}` must remain intact in all translations.
|
|
185
|
+
- The language selector is in the Settings page (Global tab). The locale is auto-detected from the browser on first visit and persisted in `localStorage('kobo:locale')`.
|
|
186
|
+
|
|
174
187
|
## Testing discipline
|
|
175
188
|
|
|
176
189
|
- **TDD for backend** — write the failing test, confirm it fails for the right reason, implement minimally, confirm it passes, commit. One commit per logical unit. See existing tests in `src/__tests__/workspace-service.test.ts` for the setup pattern (fresh in-memory DB per test via `resetDb()`).
|
|
@@ -225,3 +238,4 @@ The human user of this repository prefers French for conversational exchanges. C
|
|
|
225
238
|
- Don't introduce ORMs, query builders, or schema validation libraries — the project is small enough for raw prepared statements and hand-written mappers.
|
|
226
239
|
- Don't break the single-source-of-truth of `CLAUDE.md` → `AGENTS.md` symlink. Edit `AGENTS.md`; `CLAUDE.md` follows automatically.
|
|
227
240
|
- Don't skip `try/catch` swallowing on best-effort cleanup (agent stop, dev-server stop, worktree removal). These must never break the primary operation.
|
|
241
|
+
- Don't hardcode user-visible text in the frontend. Every string must go through `$t()` / `t()` with keys in all 5 locale files. See [Internationalization (i18n)](#internationalization-i18n).
|
|
@@ -20,6 +20,7 @@ let db;
|
|
|
20
20
|
try {
|
|
21
21
|
db = new Database(dbPath, { readonly: false });
|
|
22
22
|
db.pragma('journal_mode = WAL');
|
|
23
|
+
db.pragma('busy_timeout = 5000');
|
|
23
24
|
db.pragma('foreign_keys = ON');
|
|
24
25
|
}
|
|
25
26
|
catch (err) {
|
|
@@ -205,6 +206,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
205
206
|
properties: {
|
|
206
207
|
status: {
|
|
207
208
|
type: 'string',
|
|
209
|
+
enum: ['idle', 'completed', 'error'],
|
|
208
210
|
description: 'New status (e.g. idle, completed)',
|
|
209
211
|
},
|
|
210
212
|
},
|
package/dist/server/db/index.js
CHANGED
|
@@ -1,20 +1,82 @@
|
|
|
1
1
|
import { initSchema } from './schema.js';
|
|
2
|
-
export const
|
|
2
|
+
export const migrations = [
|
|
3
|
+
{
|
|
4
|
+
version: 2,
|
|
5
|
+
name: 'add-permission-mode',
|
|
6
|
+
migrate: (db) => {
|
|
7
|
+
db.exec("ALTER TABLE workspaces ADD COLUMN permission_mode TEXT NOT NULL DEFAULT 'auto-accept'");
|
|
8
|
+
},
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
version: 3,
|
|
12
|
+
name: 'add-workspace-id-indexes',
|
|
13
|
+
migrate: (db) => {
|
|
14
|
+
db.exec(`
|
|
15
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_workspace_id ON tasks(workspace_id);
|
|
16
|
+
CREATE INDEX IF NOT EXISTS idx_agent_sessions_workspace_id ON agent_sessions(workspace_id);
|
|
17
|
+
CREATE INDEX IF NOT EXISTS idx_ws_events_workspace_id ON ws_events(workspace_id);
|
|
18
|
+
`);
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
];
|
|
22
|
+
/** Current schema version — always equals the highest migration version. */
|
|
23
|
+
export const SCHEMA_VERSION = migrations.length > 0 ? migrations[migrations.length - 1].version : 1;
|
|
3
24
|
export function runMigrations(db) {
|
|
25
|
+
// Create the history table (replaces the old single-row schema_version table).
|
|
4
26
|
db.exec(`
|
|
5
|
-
CREATE TABLE IF NOT EXISTS
|
|
6
|
-
version
|
|
27
|
+
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
28
|
+
version INTEGER PRIMARY KEY,
|
|
29
|
+
name TEXT NOT NULL,
|
|
30
|
+
applied_at TEXT NOT NULL
|
|
7
31
|
)
|
|
8
32
|
`);
|
|
9
|
-
|
|
10
|
-
const
|
|
11
|
-
if (
|
|
33
|
+
// ── Backward compat: migrate from legacy schema_version table ──────────────
|
|
34
|
+
const hasLegacy = db.prepare("SELECT count(*) as c FROM sqlite_master WHERE type='table' AND name='schema_version'").get().c > 0;
|
|
35
|
+
if (hasLegacy) {
|
|
36
|
+
const legacyRow = db.prepare('SELECT version FROM schema_version LIMIT 1').get();
|
|
37
|
+
const legacyVersion = legacyRow?.version ?? 0;
|
|
38
|
+
// Back-fill history for all migrations that were already applied under the old system.
|
|
39
|
+
if (legacyVersion >= 1) {
|
|
40
|
+
const now = new Date().toISOString();
|
|
41
|
+
// Version 1 = initSchema (always applied if legacyVersion >= 1)
|
|
42
|
+
db.prepare('INSERT OR IGNORE INTO schema_migrations (version, name, applied_at) VALUES (?, ?, ?)').run(1, 'init-schema', now);
|
|
43
|
+
for (const m of migrations) {
|
|
44
|
+
if (m.version <= legacyVersion) {
|
|
45
|
+
db.prepare('INSERT OR IGNORE INTO schema_migrations (version, name, applied_at) VALUES (?, ?, ?)').run(m.version, m.name, now);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
db.exec('DROP TABLE schema_version');
|
|
50
|
+
}
|
|
51
|
+
// ── Determine current state ────────────────────────────────────────────────
|
|
52
|
+
const applied = new Set(db.prepare('SELECT version FROM schema_migrations').all().map((r) => r.version));
|
|
53
|
+
// Fresh install — no migrations applied yet.
|
|
54
|
+
if (!applied.has(1)) {
|
|
12
55
|
initSchema(db);
|
|
13
|
-
|
|
14
|
-
|
|
56
|
+
const now = new Date().toISOString();
|
|
57
|
+
db.prepare('INSERT INTO schema_migrations (version, name, applied_at) VALUES (?, ?, ?)').run(1, 'init-schema', now);
|
|
58
|
+
// Mark all incremental migrations as applied (initSchema creates the latest shape).
|
|
59
|
+
for (const m of migrations) {
|
|
60
|
+
db.prepare('INSERT INTO schema_migrations (version, name, applied_at) VALUES (?, ?, ?)').run(m.version, m.name, now);
|
|
15
61
|
}
|
|
16
|
-
|
|
17
|
-
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
// Apply pending migrations sequentially.
|
|
65
|
+
for (const m of migrations) {
|
|
66
|
+
if (!applied.has(m.version)) {
|
|
67
|
+
m.migrate(db);
|
|
68
|
+
db.prepare('INSERT INTO schema_migrations (version, name, applied_at) VALUES (?, ?, ?)').run(m.version, m.name, new Date().toISOString());
|
|
18
69
|
}
|
|
19
70
|
}
|
|
20
71
|
}
|
|
72
|
+
/** Return the full migration history (for diagnostics / admin UI). */
|
|
73
|
+
export function getMigrationHistory(db) {
|
|
74
|
+
try {
|
|
75
|
+
return db
|
|
76
|
+
.prepare('SELECT version, name, applied_at FROM schema_migrations ORDER BY version')
|
|
77
|
+
.all();
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
}
|
package/dist/server/db/schema.js
CHANGED
|
@@ -10,6 +10,7 @@ export function initSchema(db) {
|
|
|
10
10
|
notion_url TEXT,
|
|
11
11
|
notion_page_id TEXT,
|
|
12
12
|
model TEXT NOT NULL DEFAULT 'claude-opus-4-6',
|
|
13
|
+
permission_mode TEXT NOT NULL DEFAULT 'auto-accept',
|
|
13
14
|
dev_server_status TEXT NOT NULL DEFAULT 'stopped',
|
|
14
15
|
archived_at TEXT,
|
|
15
16
|
created_at TEXT NOT NULL,
|
|
@@ -45,5 +46,9 @@ export function initSchema(db) {
|
|
|
45
46
|
session_id TEXT,
|
|
46
47
|
created_at TEXT NOT NULL
|
|
47
48
|
);
|
|
49
|
+
|
|
50
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_workspace_id ON tasks(workspace_id);
|
|
51
|
+
CREATE INDEX IF NOT EXISTS idx_agent_sessions_workspace_id ON agent_sessions(workspace_id);
|
|
52
|
+
CREATE INDEX IF NOT EXISTS idx_ws_events_workspace_id ON ws_events(workspace_id);
|
|
48
53
|
`);
|
|
49
54
|
}
|
package/dist/server/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import path from 'node:path';
|
|
|
5
5
|
import { serve } from '@hono/node-server';
|
|
6
6
|
import { Hono } from 'hono';
|
|
7
7
|
import { WebSocketServer } from 'ws';
|
|
8
|
-
import { getDb } from './db/index.js';
|
|
8
|
+
import { closeDb, getDb } from './db/index.js';
|
|
9
9
|
import { runMigrations } from './db/migrations.js';
|
|
10
10
|
import devServerRouter from './routes/dev-server.js';
|
|
11
11
|
import gitRouter from './routes/git.js';
|
|
@@ -13,12 +13,13 @@ import imagesRouter from './routes/images.js';
|
|
|
13
13
|
import notionRouter from './routes/notion.js';
|
|
14
14
|
import settingsRouter from './routes/settings.js';
|
|
15
15
|
import workspacesRouter from './routes/workspaces.js';
|
|
16
|
-
import { getAvailableSkills, sendMessage, setBackendPort, startAgent, stopAgent } from './services/agent-manager.js';
|
|
16
|
+
import { getAvailableSkills, sendMessage, setBackendPort, startAgent, startWatchdog, stopAgent, stopWatchdog, } from './services/agent-manager.js';
|
|
17
17
|
import { startDevServer, stopDevServer } from './services/dev-server-service.js';
|
|
18
|
+
import { startPrWatcher, stopPrWatcher } from './services/pr-watcher-service.js';
|
|
18
19
|
import { emit, handleConnection, setMessageHandler } from './services/websocket-service.js';
|
|
19
20
|
import { getLatestSession, getWorkspace, updateWorkspaceStatus } from './services/workspace-service.js';
|
|
20
|
-
import { getClientSpaPath, getKoboHome } from './utils/paths.js';
|
|
21
|
-
import { initProcessCleanup } from './utils/process-tracker.js';
|
|
21
|
+
import { getClientSpaPath, getKoboHome, getPackageVersion } from './utils/paths.js';
|
|
22
|
+
import { initProcessCleanup, killAll as killAllTrackedProcesses } from './utils/process-tracker.js';
|
|
22
23
|
// 0. Runtime prerequisite check — warn if claude CLI is missing. Don't block
|
|
23
24
|
// startup: the user may still want to configure settings or browse workspaces
|
|
24
25
|
// before installing Claude Code.
|
|
@@ -32,12 +33,14 @@ console.log(`[kobo] Kōbō home: ${getKoboHome()}`);
|
|
|
32
33
|
// 1. Initialize DB + run migrations
|
|
33
34
|
const db = getDb();
|
|
34
35
|
runMigrations(db);
|
|
35
|
-
// 2. Initialize process cleanup
|
|
36
|
+
// 2. Initialize process cleanup, agent watchdog, and PR watcher
|
|
36
37
|
initProcessCleanup();
|
|
38
|
+
startWatchdog();
|
|
39
|
+
startPrWatcher();
|
|
37
40
|
// 3. Create Hono app
|
|
38
41
|
const app = new Hono();
|
|
39
42
|
// Health check (root / is handled by the SPA catch-all below)
|
|
40
|
-
app.get('/api/health', (c) => c.json({ status: 'ok', version:
|
|
43
|
+
app.get('/api/health', (c) => c.json({ status: 'ok', version: getPackageVersion() }));
|
|
41
44
|
// 4. Mount route sub-routers
|
|
42
45
|
app.route('/api/workspaces', workspacesRouter);
|
|
43
46
|
app.route('/api/workspaces', imagesRouter);
|
|
@@ -47,7 +50,7 @@ app.route('/api/settings', settingsRouter);
|
|
|
47
50
|
app.route('/api/dev-server', devServerRouter);
|
|
48
51
|
// Skills endpoint
|
|
49
52
|
app.get('/api/skills', (c) => c.json(getAvailableSkills()));
|
|
50
|
-
const PORT = process.env.
|
|
53
|
+
const PORT = parseInt(process.env.SERVER_PORT || process.env.PORT || '3000', 10);
|
|
51
54
|
// 9. Serve static files from the built SPA if present (production mode).
|
|
52
55
|
// The path is resolved relative to the package install directory, so this
|
|
53
56
|
// works both in dev (tsx running from src/) and when installed via npm / npx
|
|
@@ -118,7 +121,7 @@ setMessageHandler((type, payload) => {
|
|
|
118
121
|
const workspace = getWorkspace(p.workspaceId);
|
|
119
122
|
if (workspace) {
|
|
120
123
|
const worktreePath = `${workspace.projectPath}/.worktrees/${workspace.workingBranch}`;
|
|
121
|
-
startAgent(p.workspaceId, worktreePath, p.content, workspace.model, true);
|
|
124
|
+
startAgent(p.workspaceId, worktreePath, p.content, workspace.model, true, workspace.permissionMode);
|
|
122
125
|
updateWorkspaceStatus(p.workspaceId, 'executing');
|
|
123
126
|
}
|
|
124
127
|
}
|
|
@@ -136,7 +139,7 @@ setMessageHandler((type, payload) => {
|
|
|
136
139
|
}
|
|
137
140
|
const worktreePath = `${workspace.projectPath}/.worktrees/${workspace.workingBranch}`;
|
|
138
141
|
const prompt = p.prompt ?? 'Continue the previous task where you left off.';
|
|
139
|
-
startAgent(p.workspaceId, worktreePath, prompt, workspace.model);
|
|
142
|
+
startAgent(p.workspaceId, worktreePath, prompt, workspace.model, false, workspace.permissionMode);
|
|
140
143
|
}
|
|
141
144
|
catch (err) {
|
|
142
145
|
console.error('[ws] Failed to start agent:', err);
|
|
@@ -179,3 +182,54 @@ server.on('upgrade', (request, socket, head) => {
|
|
|
179
182
|
socket.destroy();
|
|
180
183
|
}
|
|
181
184
|
});
|
|
185
|
+
// 9. Graceful shutdown handler
|
|
186
|
+
let isShuttingDown = false;
|
|
187
|
+
function gracefulShutdown(signal) {
|
|
188
|
+
if (isShuttingDown)
|
|
189
|
+
return;
|
|
190
|
+
isShuttingDown = true;
|
|
191
|
+
console.log(`\n[kobo] Received ${signal}, shutting down gracefully…`);
|
|
192
|
+
// 1. Stop accepting new connections
|
|
193
|
+
wss.close(() => {
|
|
194
|
+
console.log('[kobo] WebSocket server closed');
|
|
195
|
+
});
|
|
196
|
+
server.close(() => {
|
|
197
|
+
console.log('[kobo] HTTP server closed');
|
|
198
|
+
});
|
|
199
|
+
// 2. Stop background services
|
|
200
|
+
try {
|
|
201
|
+
stopWatchdog();
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
// Best-effort
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
stopPrWatcher();
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
// Best-effort
|
|
211
|
+
}
|
|
212
|
+
// 3. Kill all tracked child processes (agents, dev servers)
|
|
213
|
+
try {
|
|
214
|
+
killAllTrackedProcesses();
|
|
215
|
+
console.log('[kobo] Tracked processes killed');
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
// Best-effort
|
|
219
|
+
}
|
|
220
|
+
// 4. Close database
|
|
221
|
+
try {
|
|
222
|
+
closeDb();
|
|
223
|
+
console.log('[kobo] Database closed');
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
// Best-effort
|
|
227
|
+
}
|
|
228
|
+
// 4. Give a short grace period for in-flight requests, then exit
|
|
229
|
+
setTimeout(() => {
|
|
230
|
+
console.log('[kobo] Shutdown complete');
|
|
231
|
+
process.exit(0);
|
|
232
|
+
}, 2000).unref();
|
|
233
|
+
}
|
|
234
|
+
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
|
|
235
|
+
process.on('SIGINT', () => gracefulShutdown('SIGINT'));
|