pikibun 0.20.0 → 0.22.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.
Files changed (29) hide show
  1. package/README.md +15 -6
  2. package/dist/chunks/{chunk-BVKSJ7PV.js → chunk-KOZ2NZL7.js} +4590 -3062
  3. package/dist/chunks/{cli-W7LFSBCE.js → cli-OGQRYOKN.js} +2523 -1644
  4. package/dist/cli.js +114 -5
  5. package/dist/migrations/postgres/0038_workspace_attachments.sql +11 -0
  6. package/dist/migrations/postgres/0039_extensions.sql +54 -0
  7. package/dist/migrations/postgres/0040_child_session_settings.sql +12 -0
  8. package/dist/migrations/postgres/0040_host_builtin_extensions.sql +5 -0
  9. package/dist/migrations/postgres/0040_notifications.sql +47 -0
  10. package/dist/migrations/postgres/0041_piki_extensions.sql +32 -0
  11. package/dist/migrations/postgres/0042_default_extensions.sql +23 -0
  12. package/dist/migrations/postgres/0043_host_required_extensions.sql +6 -0
  13. package/dist/migrations/sqlite/0045_workspace_attachments.sql +9 -0
  14. package/dist/migrations/sqlite/0046_extensions.sql +52 -0
  15. package/dist/migrations/sqlite/0047_child_session_settings.sql +10 -0
  16. package/dist/migrations/sqlite/0047_host_builtin_extensions.sql +3 -0
  17. package/dist/migrations/sqlite/0047_notifications.sql +45 -0
  18. package/dist/migrations/sqlite/0048_piki_extensions.sql +30 -0
  19. package/dist/migrations/sqlite/0049_default_extensions.sql +22 -0
  20. package/dist/migrations/sqlite/0050_host_required_extensions.sql +4 -0
  21. package/dist/web/assets/index-BdJ_-50H.js +62 -0
  22. package/dist/web/assets/{index-CnOXvi_D.js → index-ClW_3YVo.js} +1 -1
  23. package/dist/web/assets/index-DR-JFaOB.css +1 -0
  24. package/dist/web/index.html +2 -2
  25. package/dist/web/sw.js +61 -0
  26. package/npm-shrinkwrap.json +3 -2
  27. package/package.json +2 -1
  28. package/dist/web/assets/index-BTJ7REap.css +0 -1
  29. package/dist/web/assets/index-D8LT-Xr6.js +0 -58
package/dist/cli.js CHANGED
@@ -1,9 +1,13 @@
1
1
  import {
2
2
  ConnectorSetupError,
3
+ ExtensionSourceError,
4
+ ExtensionStore,
3
5
  RepositoryCache,
4
6
  connectMain,
5
- defaultStateDir
6
- } from "./chunks/chunk-BVKSJ7PV.js";
7
+ defaultStateDir,
8
+ loadInstalled,
9
+ parseExtensionSource
10
+ } from "./chunks/chunk-KOZ2NZL7.js";
7
11
 
8
12
  // ../../apps/runner/src/cache-cli.ts
9
13
  import { join, relative, resolve } from "node:path";
@@ -61,10 +65,113 @@ async function cacheMain(args2) {
61
65
  `);
62
66
  }
63
67
 
68
+ // ../../apps/runner/src/extensions-cli.ts
69
+ import { resolve as resolve2 } from "node:path";
70
+ import { parseArgs as parseArgs2 } from "node:util";
71
+ var HELP2 = `Usage: pikibun extensions <command> [options]
72
+
73
+ Pi extensions for the sessions on this host. A workspace chooses which ones its sessions use (Settings > Workspaces >
74
+ the workspace > Extensions in Pikibun); a session runs with those this host has at the same version. The host's owner
75
+ can also install them from Pikibun (Settings > Hosts > this host > Extensions). They run in the connector, as this
76
+ machine's user, outside the sandbox: install only code you trust.
77
+
78
+ Commands:
79
+ add SOURCE Install an extension, pinned:
80
+ npm:<package>@<exact version> e.g. npm:@ollama/pi-web-search@0.0.5
81
+ git:<https URL>@<full commit> e.g. git:github.com/acme/search@<40-character commit>
82
+ PATH a dev extension: a .ts/.js file or a package directory,
83
+ loaded in place, in your own sessions on this host only
84
+ list Load Pikibun's own and the installed extensions and show what each offers, ignores, or why it
85
+ doesn't load
86
+ remove NAME Uninstall one: its source as listed, a package name, or a dev extension's path
87
+
88
+ Options:
89
+ --state-dir PATH Connector state (default ~/.local/share/pikibun/connector)
90
+ --allow-scripts For add: let npm run the package's install scripts (off by default)
91
+ --help Show this help
92
+
93
+ Pikibun's built-in extensions (piki:gh, piki:glab, piki:azure-cli) come with the connector: they run the GitHub, GitLab
94
+ or Azure CLI installed and signed in on this host, where a workspace uses them. Nothing to install.
95
+
96
+ A running connector picks up changes made here when it next reconnects; restart it to use them at once.
97
+ `;
98
+ async function extensionsMain(args2) {
99
+ const { values, positionals } = parseArgs2({ args: args2, strict: true, allowPositionals: true, options: {
100
+ help: { type: "boolean" },
101
+ "state-dir": { type: "string" },
102
+ "allow-scripts": { type: "boolean" }
103
+ } });
104
+ const [command2, ...rest] = positionals;
105
+ if (values.help || command2 === void 0) {
106
+ process.stdout.write(HELP2);
107
+ return;
108
+ }
109
+ const store = ExtensionStore.forStateDir(resolve2(values["state-dir"] ?? defaultStateDir()));
110
+ try {
111
+ if (command2 === "add" && rest.length === 1) {
112
+ const installed = await store.add(parseExtensionSource(rest[0]), { allowScripts: values["allow-scripts"] });
113
+ process.stdout.write(`Installed ${installed.source}.
114
+ `);
115
+ print((await loadInstalled(store)).filter((item) => item.summary.source === installed.source));
116
+ return;
117
+ }
118
+ if (values["allow-scripts"]) throw new ConnectorSetupError("--allow-scripts applies to add only.");
119
+ if (command2 === "list" && !rest.length) {
120
+ const loaded = await loadInstalled(store);
121
+ if (!loaded.some((item) => !item.factories)) process.stdout.write(`No extensions installed in ${store.directory}.
122
+ `);
123
+ print(loaded);
124
+ return;
125
+ }
126
+ if (command2 === "remove" && rest.length === 1) {
127
+ const removed = await store.remove(rest[0]);
128
+ process.stdout.write(`Removed ${removed.source}.
129
+ `);
130
+ return;
131
+ }
132
+ } catch (error) {
133
+ if (error instanceof ExtensionSourceError) throw new ConnectorSetupError(error.message);
134
+ throw error;
135
+ }
136
+ throw new ConnectorSetupError(`Unknown extensions command: ${positionals.join(" ")}. Run "pikibun extensions --help".`);
137
+ }
138
+ function print(loaded) {
139
+ for (const { summary } of loaded) {
140
+ process.stdout.write(`${summary.source}${summary.dev ? " (dev: your own sessions on this host)" : summary.source.startsWith("piki:") ? " (built into the connector)" : ""}
141
+ `);
142
+ if (summary.description) process.stdout.write(` ${summary.description}
143
+ `);
144
+ if (summary.error) {
145
+ process.stdout.write(` Doesn't load: ${summary.error}
146
+ `);
147
+ continue;
148
+ }
149
+ for (const module of summary.modules) {
150
+ process.stdout.write(` ${module.path}
151
+ `);
152
+ if (module.error) {
153
+ process.stdout.write(` Doesn't load: ${module.error}
154
+ `);
155
+ continue;
156
+ }
157
+ if (module.tools.length) process.stdout.write(` Tools: ${module.tools.map((tool) => tool.name).join(", ")}
158
+ `);
159
+ if (module.events.length) process.stdout.write(` Events: ${module.events.join(", ")}
160
+ `);
161
+ if (module.flags.length) process.stdout.write(` Settings: ${module.flags.map((flag) => flag.name).join(", ")}
162
+ `);
163
+ if (module.noops.length) process.stdout.write(` Does nothing here: ${module.noops.join(", ")}
164
+ `);
165
+ if (module.ignored.length) process.stdout.write(` Not supported: ${module.ignored.join(", ")}
166
+ `);
167
+ }
168
+ }
169
+ }
170
+
64
171
  // package.json
65
172
  var package_default = {
66
173
  name: "pikibun",
67
- version: "0.20.0",
174
+ version: "0.22.0",
68
175
  description: "Connect your Linux or WSL2 Pi host to a Pikibun workspace, or run Pikibun on your own computer.",
69
176
  license: "MIT",
70
177
  homepage: "https://pikibun.com",
@@ -82,6 +189,7 @@ var package_default = {
82
189
  dependencies: {
83
190
  "@earendil-works/chord": "1.4.0",
84
191
  "@earendil-works/pi-ai": "1.4.0",
192
+ "@earendil-works/pi-codemode": "1.4.0",
85
193
  "@earendil-works/pi-coding-agent": "1.4.0",
86
194
  "@earendil-works/pi-durable": "1.4.0",
87
195
  "@hono/node-server": "1.19.11",
@@ -105,6 +213,7 @@ Commands:
105
213
  serve Run Pikibun on this computer, for yourself with it as the host,
106
214
  or shared with others (--share)
107
215
  cache List or prune this host's repository cache
216
+ extensions Add, list or remove this host's extensions
108
217
 
109
218
  Options:
110
219
  --help Show this help
@@ -113,8 +222,8 @@ Options:
113
222
  Run "pikibun connect --help" or "pikibun serve --help" for their options.
114
223
  `;
115
224
  var [command, ...args] = process.argv.slice(2);
116
- var serveMain = async (args2) => (await import("./chunks/cli-W7LFSBCE.js")).serveMain(args2);
117
- var run = command === "connect" ? connectMain : command === "cache" ? cacheMain : command === "serve" ? serveMain : void 0;
225
+ var serveMain = async (args2) => (await import("./chunks/cli-OGQRYOKN.js")).serveMain(args2);
226
+ var run = command === "connect" ? connectMain : command === "cache" ? cacheMain : command === "extensions" ? extensionsMain : command === "serve" ? serveMain : void 0;
118
227
  if (run) {
119
228
  run(args).catch((error) => {
120
229
  process.stderr.write(error instanceof ConnectorSetupError ? `${error.message}
@@ -0,0 +1,11 @@
1
+ -- Postgres translation of sqlite/0045_workspace_attachments.sql.
2
+
3
+ -- Files attached to messages (apps/server/src/file-uploads.ts).
4
+ -- A workspace's attachment settings, as its owners and admins chose them; no row: the defaults (10 MiB a file).
5
+ -- max_file_bytes: the largest file a message here may carry, uploaded into the session's worktree.
6
+ CREATE TABLE workspace_attachment_settings (
7
+ workspace_id TEXT PRIMARY KEY REFERENCES workspaces(id) ON DELETE CASCADE,
8
+ max_file_bytes BIGINT NOT NULL CHECK (max_file_bytes BETWEEN 1048576 AND 52428800),
9
+ updated_by TEXT NOT NULL REFERENCES users(id),
10
+ updated_at TEXT NOT NULL
11
+ );
@@ -0,0 +1,54 @@
1
+ -- Postgres translation of sqlite/0046_extensions.sql.
2
+
3
+ -- Extensions (docs/design/host-extensions.md, apps/server/src/extensions.ts).
4
+ -- The Pi extensions a workspace's sessions use, one version of each package, in order; its owners and admins choose them.
5
+ -- package_key is npm:<name> or git:<url>; source pins the version (npm:<name>@<version>, git:<url>@<commit>).
6
+ CREATE TABLE workspace_extensions (
7
+ workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
8
+ package_key TEXT NOT NULL,
9
+ source TEXT NOT NULL,
10
+ position BIGINT NOT NULL,
11
+ enabled BIGINT NOT NULL DEFAULT 1 CHECK (enabled IN (0, 1)),
12
+ tools_mode TEXT NOT NULL DEFAULT 'approval' CHECK (tools_mode IN ('approval', 'automatic')),
13
+ settings_json TEXT NOT NULL DEFAULT '{}',
14
+ updated_by TEXT NOT NULL REFERENCES users(id),
15
+ updated_at TEXT NOT NULL,
16
+ PRIMARY KEY (workspace_id, package_key)
17
+ );
18
+ -- What each host last reported it has installed (HostExtensionsReport), on each sync of its connector.
19
+ CREATE TABLE connector_extensions (
20
+ connector_id TEXT PRIMARY KEY REFERENCES connectors(id) ON DELETE CASCADE,
21
+ report_json TEXT NOT NULL,
22
+ reported_at TEXT NOT NULL
23
+ );
24
+ -- The host owner's own extension settings; no row: the defaults. automatic_tools 1 lets workspaces run extension tools
25
+ -- without approval on this host, where they set an extension's tools to automatic.
26
+ CREATE TABLE connector_extension_settings (
27
+ connector_id TEXT PRIMARY KEY REFERENCES connectors(id) ON DELETE CASCADE,
28
+ automatic_tools BIGINT NOT NULL DEFAULT 0 CHECK (automatic_tools IN (0, 1)),
29
+ updated_by TEXT NOT NULL REFERENCES users(id),
30
+ updated_at TEXT NOT NULL
31
+ );
32
+ -- Installs and removals the host's owner asked for in Pikibun; its connector carries them out when it next syncs and
33
+ -- reports how they went.
34
+ CREATE TABLE connector_extension_requests (
35
+ id TEXT PRIMARY KEY,
36
+ connector_id TEXT NOT NULL REFERENCES connectors(id) ON DELETE CASCADE,
37
+ action TEXT NOT NULL CHECK (action IN ('install', 'remove')),
38
+ source TEXT NOT NULL,
39
+ requested_by TEXT NOT NULL REFERENCES users(id),
40
+ requested_at TEXT NOT NULL,
41
+ status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'done', 'failed')),
42
+ error TEXT,
43
+ finished_at TEXT
44
+ );
45
+ CREATE INDEX connector_extension_requests_pending ON connector_extension_requests (connector_id, status, requested_at);
46
+ -- Another member's extension tool call a host said ran automatically, which the control plane couldn't confirm the first
47
+ -- time it saw it (the workspace didn't run that extension's tools automatically on that host): shown as a plain record,
48
+ -- never reviewed, even once the setting allows it.
49
+ CREATE TABLE approval_unconfirmed_automatic (
50
+ session_id TEXT NOT NULL REFERENCES sessions(id),
51
+ approval_id TEXT NOT NULL,
52
+ created_at TEXT NOT NULL,
53
+ PRIMARY KEY (session_id, approval_id)
54
+ );
@@ -0,0 +1,12 @@
1
+ -- Postgres translation of sqlite/0047_child_session_settings.sql.
2
+
3
+ -- Sessions started by an agent (apps/server/src/cross-session.ts, docs/design/cross-session.md).
4
+ -- A workspace's settings for them, as its owners and admins chose them; no row: the defaults (on).
5
+ -- child_models 1: an agent that starts a session for a sub-task may choose the model and reasoning level it runs on.
6
+ -- 0: every such session runs on its parent's current model and reasoning level.
7
+ CREATE TABLE workspace_child_session_settings (
8
+ workspace_id TEXT PRIMARY KEY REFERENCES workspaces(id) ON DELETE CASCADE,
9
+ child_models BIGINT NOT NULL DEFAULT 1 CHECK (child_models IN (0, 1)),
10
+ updated_by TEXT NOT NULL REFERENCES users(id),
11
+ updated_at TEXT NOT NULL
12
+ );
@@ -0,0 +1,5 @@
1
+ -- Postgres translation of sqlite/0047_host_builtin_extensions.sql.
2
+
3
+ -- Pi's built-in extensions every session on a host runs with, as its owner and admins set it in Pikibun
4
+ -- (HostConfig.builtinExtensions in packages/shared/src/host-config.ts): their names, as a JSON array. '[]' runs none.
5
+ ALTER TABLE connector_configs ADD COLUMN builtin_extensions_json TEXT NOT NULL DEFAULT '[]';
@@ -0,0 +1,47 @@
1
+ -- Postgres translation of sqlite/0047_notifications.sql.
2
+
3
+ -- Notifications (docs/notifications.md, apps/server/src/notifications.ts).
4
+ -- Which notifications each person gets; no row: the defaults (everything on, with session titles).
5
+ CREATE TABLE notification_preferences (
6
+ user_id TEXT PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
7
+ approvals BIGINT NOT NULL DEFAULT 1 CHECK (approvals IN (0, 1)),
8
+ finished BIGINT NOT NULL DEFAULT 1 CHECK (finished IN (0, 1)),
9
+ failed BIGINT NOT NULL DEFAULT 1 CHECK (failed IN (0, 1)),
10
+ details BIGINT NOT NULL DEFAULT 1 CHECK (details IN (0, 1)),
11
+ updated_at TEXT NOT NULL
12
+ );
13
+ -- The devices that get a person's notifications: a browser's Web Push subscription (endpoint, p256dh and auth keys) or
14
+ -- the app's Expo push token (in endpoint). Each belongs to the sign-in that registered it, and goes with it: signing
15
+ -- out, or signing an app out from the web, stops its notifications.
16
+ CREATE TABLE notification_devices (
17
+ id TEXT PRIMARY KEY,
18
+ user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
19
+ auth_token_hash TEXT NOT NULL REFERENCES auth_sessions(token_hash) ON DELETE CASCADE,
20
+ kind TEXT NOT NULL CHECK (kind IN ('web', 'expo')),
21
+ endpoint TEXT NOT NULL UNIQUE,
22
+ p256dh TEXT,
23
+ auth TEXT,
24
+ name TEXT,
25
+ created_at TEXT NOT NULL,
26
+ CHECK ((kind = 'web') = (p256dh IS NOT NULL AND auth IS NOT NULL))
27
+ );
28
+ CREATE INDEX notification_devices_user ON notification_devices(user_id);
29
+ CREATE INDEX notification_devices_auth ON notification_devices(auth_token_hash);
30
+ -- Each person's recent notifications, which open web pages and desktop windows poll (GET /api/notifications/feed).
31
+ -- Ids sort in the order they were made; rows older than a day are removed as new ones come.
32
+ CREATE TABLE notifications (
33
+ id TEXT PRIMARY KEY,
34
+ user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
35
+ session_id TEXT REFERENCES sessions(id) ON DELETE CASCADE,
36
+ kind TEXT NOT NULL CHECK (kind IN ('approval', 'finished', 'failed', 'test')),
37
+ title TEXT NOT NULL,
38
+ body TEXT NOT NULL,
39
+ created_at TEXT NOT NULL
40
+ );
41
+ CREATE INDEX notifications_user ON notifications(user_id, id);
42
+ -- Keys the server makes for itself once; the Web Push (VAPID) key pair, its private half sealed with VAULT_KEY.
43
+ CREATE TABLE server_keys (
44
+ name TEXT PRIMARY KEY,
45
+ value TEXT NOT NULL,
46
+ created_at TEXT NOT NULL
47
+ );
@@ -0,0 +1,32 @@
1
+ -- Postgres translation of sqlite/0048_piki_extensions.sql.
2
+
3
+ -- Pikibun extensions (docs/design/piki-extensions.md, apps/server/src/extensions.ts).
4
+ -- piki:<name> sources come with the connector; their package_key is the source itself. A workspace that binds its
5
+ -- first repository on GitHub (github.com or Enterprise Server), GitLab or Azure DevOps gets the extension of that forge's
6
+ -- command-line tool (gh, glab, az): its tools wait for approval, and owners and admins turn it off or remove it like any
7
+ -- other. Workspaces that already have such a repository get it here, as their owner, unless they use 32 extensions.
8
+ INSERT INTO workspace_extensions (workspace_id, package_key, source, position, enabled, tools_mode, settings_json, updated_by, updated_at)
9
+ SELECT w.id, 'piki:gh', 'piki:gh', COALESCE((SELECT MAX(e.position) FROM workspace_extensions e WHERE e.workspace_id = w.id), 0) + 1, 1, 'approval', '{}', w.owner_id,
10
+ to_char(clock_timestamp() AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')
11
+ FROM workspaces w
12
+ WHERE EXISTS (SELECT 1 FROM workspace_repositories r WHERE r.workspace_id = w.id AND r.provider IN ('github', 'github-enterprise'))
13
+ AND NOT EXISTS (SELECT 1 FROM workspace_extensions e WHERE e.workspace_id = w.id AND e.package_key = 'piki:gh')
14
+ AND (SELECT COUNT(*) FROM workspace_extensions e WHERE e.workspace_id = w.id) < 32;
15
+ INSERT INTO workspace_extensions (workspace_id, package_key, source, position, enabled, tools_mode, settings_json, updated_by, updated_at)
16
+ SELECT w.id, 'piki:glab', 'piki:glab', COALESCE((SELECT MAX(e.position) FROM workspace_extensions e WHERE e.workspace_id = w.id), 0) + 1, 1, 'approval', '{}', w.owner_id,
17
+ to_char(clock_timestamp() AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')
18
+ FROM workspaces w
19
+ WHERE EXISTS (SELECT 1 FROM workspace_repositories r WHERE r.workspace_id = w.id AND r.provider IN ('gitlab'))
20
+ AND NOT EXISTS (SELECT 1 FROM workspace_extensions e WHERE e.workspace_id = w.id AND e.package_key = 'piki:glab')
21
+ AND (SELECT COUNT(*) FROM workspace_extensions e WHERE e.workspace_id = w.id) < 32;
22
+ INSERT INTO workspace_extensions (workspace_id, package_key, source, position, enabled, tools_mode, settings_json, updated_by, updated_at)
23
+ SELECT w.id, 'piki:azure-cli', 'piki:azure-cli', COALESCE((SELECT MAX(e.position) FROM workspace_extensions e WHERE e.workspace_id = w.id), 0) + 1, 1, 'approval', '{}', w.owner_id,
24
+ to_char(clock_timestamp() AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')
25
+ FROM workspaces w
26
+ WHERE EXISTS (SELECT 1 FROM workspace_repositories r WHERE r.workspace_id = w.id AND r.provider IN ('azure-devops'))
27
+ AND NOT EXISTS (SELECT 1 FROM workspace_extensions e WHERE e.workspace_id = w.id AND e.package_key = 'piki:azure-cli')
28
+ AND (SELECT COUNT(*) FROM workspace_extensions e WHERE e.workspace_id = w.id) < 32;
29
+ -- Pi's built-in extensions a host's configuration turns on are named pi:<name> (packages/shared/src/host-config.ts),
30
+ -- beside Pikibun's own piki:<name>; configurations saved before named them bare. Reads accept those too.
31
+ UPDATE connector_configs SET builtin_extensions_json = REPLACE(builtin_extensions_json, '"codemode"', '"pi:codemode"')
32
+ WHERE builtin_extensions_json LIKE '%"codemode"%';
@@ -0,0 +1,23 @@
1
+ -- Postgres translation of sqlite/0049_default_extensions.sql.
2
+ -- Popular Pi extensions for every existing workspace (docs/extensions.md), turned off and with tools on approval, after
3
+ -- the ones it has: its owners and admins turn on the ones they want. New workspaces get the same ones when they are
4
+ -- created (defaultWorkspaceExtensions in packages/shared/src/host-extensions.ts, which lists the same sources). A
5
+ -- workspace that already uses a package, any version of it, keeps its own; one at the limit of 32 gets no more.
6
+ INSERT INTO workspace_extensions (workspace_id, package_key, source, position, enabled, tools_mode, settings_json, updated_by, updated_at)
7
+ SELECT w.id, 'npm:pi-web-access', 'npm:pi-web-access@0.46.0', (SELECT COALESCE(MAX(e.position), 0) + 1 FROM workspace_extensions e WHERE e.workspace_id = w.id),
8
+ 0, 'approval', '{}', w.owner_id, to_char(now() AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')
9
+ FROM workspaces w
10
+ WHERE (SELECT COUNT(*) FROM workspace_extensions e WHERE e.workspace_id = w.id) < 32
11
+ ON CONFLICT (workspace_id, package_key) DO NOTHING;
12
+ INSERT INTO workspace_extensions (workspace_id, package_key, source, position, enabled, tools_mode, settings_json, updated_by, updated_at)
13
+ SELECT w.id, 'npm:pi-web-search', 'npm:pi-web-search@1.7.0', (SELECT COALESCE(MAX(e.position), 0) + 1 FROM workspace_extensions e WHERE e.workspace_id = w.id),
14
+ 0, 'approval', '{}', w.owner_id, to_char(now() AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')
15
+ FROM workspaces w
16
+ WHERE (SELECT COUNT(*) FROM workspace_extensions e WHERE e.workspace_id = w.id) < 32
17
+ ON CONFLICT (workspace_id, package_key) DO NOTHING;
18
+ INSERT INTO workspace_extensions (workspace_id, package_key, source, position, enabled, tools_mode, settings_json, updated_by, updated_at)
19
+ SELECT w.id, 'npm:cc-safety-net', 'npm:cc-safety-net@2.7.2', (SELECT COALESCE(MAX(e.position), 0) + 1 FROM workspace_extensions e WHERE e.workspace_id = w.id),
20
+ 0, 'approval', '{}', w.owner_id, to_char(now() AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')
21
+ FROM workspaces w
22
+ WHERE (SELECT COUNT(*) FROM workspace_extensions e WHERE e.workspace_id = w.id) < 32
23
+ ON CONFLICT (workspace_id, package_key) DO NOTHING;
@@ -0,0 +1,6 @@
1
+ -- Postgres translation of sqlite/0050_host_required_extensions.sql.
2
+
3
+ -- The extensions a host's owner requires of every session on it, whatever its workspace chose (HostConfig.requiredExtensions
4
+ -- in packages/shared/src/host-config.ts): each one's source and settings, as JSON, in the order sessions run them.
5
+ -- '[]' requires none.
6
+ ALTER TABLE connector_configs ADD COLUMN required_extensions_json TEXT NOT NULL DEFAULT '[]';
@@ -0,0 +1,9 @@
1
+ -- Files attached to messages (apps/server/src/file-uploads.ts).
2
+ -- A workspace's attachment settings, as its owners and admins chose them; no row: the defaults (10 MiB a file).
3
+ -- max_file_bytes: the largest file a message here may carry, uploaded into the session's worktree.
4
+ CREATE TABLE workspace_attachment_settings (
5
+ workspace_id TEXT PRIMARY KEY REFERENCES workspaces(id) ON DELETE CASCADE,
6
+ max_file_bytes INTEGER NOT NULL CHECK (max_file_bytes BETWEEN 1048576 AND 52428800),
7
+ updated_by TEXT NOT NULL REFERENCES users(id),
8
+ updated_at TEXT NOT NULL
9
+ );
@@ -0,0 +1,52 @@
1
+ -- Extensions (docs/design/host-extensions.md, apps/server/src/extensions.ts).
2
+ -- The Pi extensions a workspace's sessions use, one version of each package, in order; its owners and admins choose them.
3
+ -- package_key is npm:<name> or git:<url>; source pins the version (npm:<name>@<version>, git:<url>@<commit>).
4
+ CREATE TABLE workspace_extensions (
5
+ workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
6
+ package_key TEXT NOT NULL,
7
+ source TEXT NOT NULL,
8
+ position INTEGER NOT NULL,
9
+ enabled INTEGER NOT NULL DEFAULT 1 CHECK (enabled IN (0, 1)),
10
+ tools_mode TEXT NOT NULL DEFAULT 'approval' CHECK (tools_mode IN ('approval', 'automatic')),
11
+ settings_json TEXT NOT NULL DEFAULT '{}',
12
+ updated_by TEXT NOT NULL REFERENCES users(id),
13
+ updated_at TEXT NOT NULL,
14
+ PRIMARY KEY (workspace_id, package_key)
15
+ );
16
+ -- What each host last reported it has installed (HostExtensionsReport), on each sync of its connector.
17
+ CREATE TABLE connector_extensions (
18
+ connector_id TEXT PRIMARY KEY REFERENCES connectors(id) ON DELETE CASCADE,
19
+ report_json TEXT NOT NULL,
20
+ reported_at TEXT NOT NULL
21
+ );
22
+ -- The host owner's own extension settings; no row: the defaults. automatic_tools 1 lets workspaces run extension tools
23
+ -- without approval on this host, where they set an extension's tools to automatic.
24
+ CREATE TABLE connector_extension_settings (
25
+ connector_id TEXT PRIMARY KEY REFERENCES connectors(id) ON DELETE CASCADE,
26
+ automatic_tools INTEGER NOT NULL DEFAULT 0 CHECK (automatic_tools IN (0, 1)),
27
+ updated_by TEXT NOT NULL REFERENCES users(id),
28
+ updated_at TEXT NOT NULL
29
+ );
30
+ -- Installs and removals the host's owner asked for in Pikibun; its connector carries them out when it next syncs and
31
+ -- reports how they went.
32
+ CREATE TABLE connector_extension_requests (
33
+ id TEXT PRIMARY KEY,
34
+ connector_id TEXT NOT NULL REFERENCES connectors(id) ON DELETE CASCADE,
35
+ action TEXT NOT NULL CHECK (action IN ('install', 'remove')),
36
+ source TEXT NOT NULL,
37
+ requested_by TEXT NOT NULL REFERENCES users(id),
38
+ requested_at TEXT NOT NULL,
39
+ status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'done', 'failed')),
40
+ error TEXT,
41
+ finished_at TEXT
42
+ );
43
+ CREATE INDEX connector_extension_requests_pending ON connector_extension_requests (connector_id, status, requested_at);
44
+ -- Another member's extension tool call a host said ran automatically, which the control plane couldn't confirm the first
45
+ -- time it saw it (the workspace didn't run that extension's tools automatically on that host): shown as a plain record,
46
+ -- never reviewed, even once the setting allows it.
47
+ CREATE TABLE approval_unconfirmed_automatic (
48
+ session_id TEXT NOT NULL REFERENCES sessions(id),
49
+ approval_id TEXT NOT NULL,
50
+ created_at TEXT NOT NULL,
51
+ PRIMARY KEY (session_id, approval_id)
52
+ );
@@ -0,0 +1,10 @@
1
+ -- Sessions started by an agent (apps/server/src/cross-session.ts, docs/design/cross-session.md).
2
+ -- A workspace's settings for them, as its owners and admins chose them; no row: the defaults (on).
3
+ -- child_models 1: an agent that starts a session for a sub-task may choose the model and reasoning level it runs on.
4
+ -- 0: every such session runs on its parent's current model and reasoning level.
5
+ CREATE TABLE workspace_child_session_settings (
6
+ workspace_id TEXT PRIMARY KEY REFERENCES workspaces(id) ON DELETE CASCADE,
7
+ child_models INTEGER NOT NULL DEFAULT 1 CHECK (child_models IN (0, 1)),
8
+ updated_by TEXT NOT NULL REFERENCES users(id),
9
+ updated_at TEXT NOT NULL
10
+ );
@@ -0,0 +1,3 @@
1
+ -- Pi's built-in extensions every session on a host runs with, as its owner and admins set it in Pikibun
2
+ -- (HostConfig.builtinExtensions in packages/shared/src/host-config.ts): their names, as a JSON array. '[]' runs none.
3
+ ALTER TABLE connector_configs ADD COLUMN builtin_extensions_json TEXT NOT NULL DEFAULT '[]';
@@ -0,0 +1,45 @@
1
+ -- Notifications (docs/notifications.md, apps/server/src/notifications.ts).
2
+ -- Which notifications each person gets; no row: the defaults (everything on, with session titles).
3
+ CREATE TABLE notification_preferences (
4
+ user_id TEXT PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
5
+ approvals INTEGER NOT NULL DEFAULT 1 CHECK (approvals IN (0, 1)),
6
+ finished INTEGER NOT NULL DEFAULT 1 CHECK (finished IN (0, 1)),
7
+ failed INTEGER NOT NULL DEFAULT 1 CHECK (failed IN (0, 1)),
8
+ details INTEGER NOT NULL DEFAULT 1 CHECK (details IN (0, 1)),
9
+ updated_at TEXT NOT NULL
10
+ );
11
+ -- The devices that get a person's notifications: a browser's Web Push subscription (endpoint, p256dh and auth keys) or
12
+ -- the app's Expo push token (in endpoint). Each belongs to the sign-in that registered it, and goes with it: signing
13
+ -- out, or signing an app out from the web, stops its notifications.
14
+ CREATE TABLE notification_devices (
15
+ id TEXT PRIMARY KEY,
16
+ user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
17
+ auth_token_hash TEXT NOT NULL REFERENCES auth_sessions(token_hash) ON DELETE CASCADE,
18
+ kind TEXT NOT NULL CHECK (kind IN ('web', 'expo')),
19
+ endpoint TEXT NOT NULL UNIQUE,
20
+ p256dh TEXT,
21
+ auth TEXT,
22
+ name TEXT,
23
+ created_at TEXT NOT NULL,
24
+ CHECK ((kind = 'web') = (p256dh IS NOT NULL AND auth IS NOT NULL))
25
+ );
26
+ CREATE INDEX notification_devices_user ON notification_devices(user_id);
27
+ CREATE INDEX notification_devices_auth ON notification_devices(auth_token_hash);
28
+ -- Each person's recent notifications, which open web pages and desktop windows poll (GET /api/notifications/feed).
29
+ -- Ids sort in the order they were made; rows older than a day are removed as new ones come.
30
+ CREATE TABLE notifications (
31
+ id TEXT PRIMARY KEY,
32
+ user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
33
+ session_id TEXT REFERENCES sessions(id) ON DELETE CASCADE,
34
+ kind TEXT NOT NULL CHECK (kind IN ('approval', 'finished', 'failed', 'test')),
35
+ title TEXT NOT NULL,
36
+ body TEXT NOT NULL,
37
+ created_at TEXT NOT NULL
38
+ );
39
+ CREATE INDEX notifications_user ON notifications(user_id, id);
40
+ -- Keys the server makes for itself once; the Web Push (VAPID) key pair, its private half sealed with VAULT_KEY.
41
+ CREATE TABLE server_keys (
42
+ name TEXT PRIMARY KEY,
43
+ value TEXT NOT NULL,
44
+ created_at TEXT NOT NULL
45
+ );
@@ -0,0 +1,30 @@
1
+ -- Pikibun extensions (docs/design/piki-extensions.md, apps/server/src/extensions.ts).
2
+ -- piki:<name> sources come with the connector; their package_key is the source itself. A workspace that binds its
3
+ -- first repository on GitHub (github.com or Enterprise Server), GitLab or Azure DevOps gets the extension of that forge's
4
+ -- command-line tool (gh, glab, az): its tools wait for approval, and owners and admins turn it off or remove it like any
5
+ -- other. Workspaces that already have such a repository get it here, as their owner, unless they use 32 extensions.
6
+ INSERT INTO workspace_extensions (workspace_id, package_key, source, position, enabled, tools_mode, settings_json, updated_by, updated_at)
7
+ SELECT w.id, 'piki:gh', 'piki:gh', COALESCE((SELECT MAX(e.position) FROM workspace_extensions e WHERE e.workspace_id = w.id), 0) + 1, 1, 'approval', '{}', w.owner_id,
8
+ strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
9
+ FROM workspaces w
10
+ WHERE EXISTS (SELECT 1 FROM workspace_repositories r WHERE r.workspace_id = w.id AND r.provider IN ('github', 'github-enterprise'))
11
+ AND NOT EXISTS (SELECT 1 FROM workspace_extensions e WHERE e.workspace_id = w.id AND e.package_key = 'piki:gh')
12
+ AND (SELECT COUNT(*) FROM workspace_extensions e WHERE e.workspace_id = w.id) < 32;
13
+ INSERT INTO workspace_extensions (workspace_id, package_key, source, position, enabled, tools_mode, settings_json, updated_by, updated_at)
14
+ SELECT w.id, 'piki:glab', 'piki:glab', COALESCE((SELECT MAX(e.position) FROM workspace_extensions e WHERE e.workspace_id = w.id), 0) + 1, 1, 'approval', '{}', w.owner_id,
15
+ strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
16
+ FROM workspaces w
17
+ WHERE EXISTS (SELECT 1 FROM workspace_repositories r WHERE r.workspace_id = w.id AND r.provider IN ('gitlab'))
18
+ AND NOT EXISTS (SELECT 1 FROM workspace_extensions e WHERE e.workspace_id = w.id AND e.package_key = 'piki:glab')
19
+ AND (SELECT COUNT(*) FROM workspace_extensions e WHERE e.workspace_id = w.id) < 32;
20
+ INSERT INTO workspace_extensions (workspace_id, package_key, source, position, enabled, tools_mode, settings_json, updated_by, updated_at)
21
+ SELECT w.id, 'piki:azure-cli', 'piki:azure-cli', COALESCE((SELECT MAX(e.position) FROM workspace_extensions e WHERE e.workspace_id = w.id), 0) + 1, 1, 'approval', '{}', w.owner_id,
22
+ strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
23
+ FROM workspaces w
24
+ WHERE EXISTS (SELECT 1 FROM workspace_repositories r WHERE r.workspace_id = w.id AND r.provider IN ('azure-devops'))
25
+ AND NOT EXISTS (SELECT 1 FROM workspace_extensions e WHERE e.workspace_id = w.id AND e.package_key = 'piki:azure-cli')
26
+ AND (SELECT COUNT(*) FROM workspace_extensions e WHERE e.workspace_id = w.id) < 32;
27
+ -- Pi's built-in extensions a host's configuration turns on are named pi:<name> (packages/shared/src/host-config.ts),
28
+ -- beside Pikibun's own piki:<name>; configurations saved before named them bare. Reads accept those too.
29
+ UPDATE connector_configs SET builtin_extensions_json = REPLACE(builtin_extensions_json, '"codemode"', '"pi:codemode"')
30
+ WHERE builtin_extensions_json LIKE '%"codemode"%';
@@ -0,0 +1,22 @@
1
+ -- Popular Pi extensions for every existing workspace (docs/extensions.md), turned off and with tools on approval, after
2
+ -- the ones it has: its owners and admins turn on the ones they want. New workspaces get the same ones when they are
3
+ -- created (defaultWorkspaceExtensions in packages/shared/src/host-extensions.ts, which lists the same sources). A
4
+ -- workspace that already uses a package, any version of it, keeps its own; one at the limit of 32 gets no more.
5
+ INSERT INTO workspace_extensions (workspace_id, package_key, source, position, enabled, tools_mode, settings_json, updated_by, updated_at)
6
+ SELECT w.id, 'npm:pi-web-access', 'npm:pi-web-access@0.46.0', (SELECT COALESCE(MAX(e.position), 0) + 1 FROM workspace_extensions e WHERE e.workspace_id = w.id),
7
+ 0, 'approval', '{}', w.owner_id, strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
8
+ FROM workspaces w
9
+ WHERE (SELECT COUNT(*) FROM workspace_extensions e WHERE e.workspace_id = w.id) < 32
10
+ ON CONFLICT (workspace_id, package_key) DO NOTHING;
11
+ INSERT INTO workspace_extensions (workspace_id, package_key, source, position, enabled, tools_mode, settings_json, updated_by, updated_at)
12
+ SELECT w.id, 'npm:pi-web-search', 'npm:pi-web-search@1.7.0', (SELECT COALESCE(MAX(e.position), 0) + 1 FROM workspace_extensions e WHERE e.workspace_id = w.id),
13
+ 0, 'approval', '{}', w.owner_id, strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
14
+ FROM workspaces w
15
+ WHERE (SELECT COUNT(*) FROM workspace_extensions e WHERE e.workspace_id = w.id) < 32
16
+ ON CONFLICT (workspace_id, package_key) DO NOTHING;
17
+ INSERT INTO workspace_extensions (workspace_id, package_key, source, position, enabled, tools_mode, settings_json, updated_by, updated_at)
18
+ SELECT w.id, 'npm:cc-safety-net', 'npm:cc-safety-net@2.7.2', (SELECT COALESCE(MAX(e.position), 0) + 1 FROM workspace_extensions e WHERE e.workspace_id = w.id),
19
+ 0, 'approval', '{}', w.owner_id, strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
20
+ FROM workspaces w
21
+ WHERE (SELECT COUNT(*) FROM workspace_extensions e WHERE e.workspace_id = w.id) < 32
22
+ ON CONFLICT (workspace_id, package_key) DO NOTHING;
@@ -0,0 +1,4 @@
1
+ -- The extensions a host's owner requires of every session on it, whatever its workspace chose (HostConfig.requiredExtensions
2
+ -- in packages/shared/src/host-config.ts): each one's source and settings, as JSON, in the order sessions run them.
3
+ -- '[]' requires none.
4
+ ALTER TABLE connector_configs ADD COLUMN required_extensions_json TEXT NOT NULL DEFAULT '[]';