@douglas-agent/sandbank-db9 0.2.1

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.
@@ -0,0 +1,25 @@
1
+ import type { ServiceConfig, ServiceInfo, SkillDefinition } from '@douglas-agent/sandbank-core';
2
+ import { Db9Client, type Db9ClientConfig } from './client.js';
3
+ export interface Db9AdapterConfig extends Db9ClientConfig {
4
+ /** 是否自动注入 db9 官方 skill。默认 true */
5
+ injectSkill?: boolean;
6
+ /** 自定义 skill 内容(覆盖默认获取) */
7
+ skillContent?: string;
8
+ }
9
+ export declare class Db9ServiceAdapter {
10
+ readonly name = "db9";
11
+ readonly client: Db9Client;
12
+ private readonly config;
13
+ constructor(config: Db9AdapterConfig);
14
+ createService(config: ServiceConfig): Promise<ServiceInfo>;
15
+ getService(id: string): Promise<ServiceInfo>;
16
+ listServices(): Promise<ServiceInfo[]>;
17
+ destroyService(id: string): Promise<void>;
18
+ /** 创建数据库分支 */
19
+ branchService(serviceId: string, name: string): Promise<ServiceInfo>;
20
+ /** 删除分支 */
21
+ deleteBranch(branchId: string): Promise<void>;
22
+ /** 获取 db9 官方 skill(带 24h 内存缓存) */
23
+ getSkill(): Promise<SkillDefinition>;
24
+ }
25
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAC/F,OAAO,EAAE,SAAS,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAA;AAI7D,MAAM,WAAW,gBAAiB,SAAQ,eAAe;IACvD,kCAAkC;IAClC,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,2BAA2B;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AA2BD,qBAAa,iBAAiB;IAC5B,QAAQ,CAAC,IAAI,SAAQ;IACrB,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAA;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;gBAE7B,MAAM,EAAE,gBAAgB;IAK9B,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,WAAW,CAAC;IAK1D,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAK5C,YAAY,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAKtC,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM/C,cAAc;IACR,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAK1E,WAAW;IACL,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAInD,kCAAkC;IAC5B,QAAQ,IAAI,OAAO,CAAC,eAAe,CAAC;CAO3C"}
@@ -0,0 +1,68 @@
1
+ import { Db9Client } from './client.js';
2
+ import { fetchDb9Skill, db9SkillDefinition } from './skill.js';
3
+ function mapDbToServiceInfo(db) {
4
+ return {
5
+ id: db.id,
6
+ type: 'postgres',
7
+ name: db.name,
8
+ state: db.state === 'ready' ? 'ready'
9
+ : db.state === 'creating' ? 'creating'
10
+ : db.state === 'terminated' || db.state === 'deleted' ? 'terminated'
11
+ : 'error',
12
+ credentials: {
13
+ url: db.connection_string,
14
+ env: {
15
+ DATABASE_URL: db.connection_string,
16
+ DB9_DATABASE_ID: db.id,
17
+ DB9_DATABASE_NAME: db.name,
18
+ PGHOST: db.host,
19
+ PGPORT: String(db.port),
20
+ PGUSER: db.username,
21
+ PGPASSWORD: db.password,
22
+ PGDATABASE: db.database,
23
+ },
24
+ },
25
+ };
26
+ }
27
+ export class Db9ServiceAdapter {
28
+ name = 'db9';
29
+ client;
30
+ config;
31
+ constructor(config) {
32
+ this.config = config;
33
+ this.client = new Db9Client(config);
34
+ }
35
+ async createService(config) {
36
+ const db = await this.client.createDatabase(config.name);
37
+ return mapDbToServiceInfo(db);
38
+ }
39
+ async getService(id) {
40
+ const db = await this.client.getDatabase(id);
41
+ return mapDbToServiceInfo(db);
42
+ }
43
+ async listServices() {
44
+ const dbs = await this.client.listDatabases();
45
+ return dbs.map(mapDbToServiceInfo);
46
+ }
47
+ async destroyService(id) {
48
+ await this.client.deleteDatabase(id);
49
+ }
50
+ // --- db9 特有能力 ---
51
+ /** 创建数据库分支 */
52
+ async branchService(serviceId, name) {
53
+ const db = await this.client.createBranch(serviceId, name);
54
+ return mapDbToServiceInfo(db);
55
+ }
56
+ /** 删除分支 */
57
+ async deleteBranch(branchId) {
58
+ await this.client.deleteBranch(branchId);
59
+ }
60
+ /** 获取 db9 官方 skill(带 24h 内存缓存) */
61
+ async getSkill() {
62
+ if (this.config.skillContent) {
63
+ return db9SkillDefinition(this.config.skillContent);
64
+ }
65
+ const content = await fetchDb9Skill();
66
+ return db9SkillDefinition(content);
67
+ }
68
+ }
@@ -0,0 +1,4 @@
1
+ import type { SkillDefinition } from '@douglas-agent/sandbank-core';
2
+ export declare const BRAIN_SKILL = "# brain \u2014 \u5171\u4EAB\u8BB0\u5FC6\u7CFB\u7EDF\n\n\u4F60\u6B63\u5728\u4E00\u4E2A\u591A Agent \u534F\u4F5C\u73AF\u5883\u4E2D\u5DE5\u4F5C\u3002\u4F60\u6709\u4E00\u4E2A\u5171\u4EAB\u6570\u636E\u5E93\u7528\u4E8E\u8BB0\u5FC6\u548C\u4EFB\u52A1\u534F\u8C03\u3002\n\n## \u73AF\u5883\u53D8\u91CF\n- $DATABASE_URL \u2014 PostgreSQL \u8FDE\u63A5\u4E32\n\n## \u5199\u5165\u8BB0\u5FC6\n\u5728\u53D1\u73B0\u91CD\u8981\u4FE1\u606F\u65F6\uFF0C\u5199\u5165 memory \u8868\uFF1A\n```sql\nINSERT INTO memory (agent, scope, kind, content)\nVALUES ('\u4F60\u7684\u89D2\u8272', 'task:\u5F53\u524D\u4EFB\u52A1ID', 'fact|decision|question|blocker', '\u5185\u5BB9');\n```\n\n## \u67E5\u8BE2\u8BB0\u5FC6\n\u5F00\u59CB\u5DE5\u4F5C\u524D\uFF0C\u5148\u67E5\u770B\u5176\u4ED6 Agent \u7684\u53D1\u73B0\uFF1A\n```sql\nSELECT agent, kind, content FROM memory\nWHERE scope = 'task:\u5F53\u524D\u4EFB\u52A1ID' AND superseded_by IS NULL\nORDER BY created_at;\n```\n\n## \u8BED\u4E49\u641C\u7D22\u8BB0\u5FC6\n```sql\nSELECT content, agent, kind FROM memory\nWHERE superseded_by IS NULL\nORDER BY embedding <=> $query_embedding\nLIMIT 10;\n```\n\n## \u8BA4\u9886\u4EFB\u52A1\n```sql\nUPDATE tasks SET status = 'claimed', claimed_by = '\u4F60\u7684\u89D2\u8272', claimed_at = now()\nWHERE id = (\n SELECT id FROM tasks\n WHERE status = 'pending'\n AND NOT EXISTS (\n SELECT 1 FROM unnest(depends_on) dep JOIN tasks t ON t.id = dep WHERE t.status != 'done'\n )\n ORDER BY created_at FOR UPDATE SKIP LOCKED LIMIT 1\n) RETURNING *;\n```\n\n## \u5B8C\u6210\u4EFB\u52A1\n```sql\nUPDATE tasks SET status = 'done', result = '{\"summary\":\"...\"}' WHERE id = \u5F53\u524D\u4EFB\u52A1ID;\n```\n\n## \u6CE8\u518C\u4EA7\u51FA\u7269\n```sql\nINSERT INTO artifacts (task_id, agent, kind, path, content)\nVALUES (\u5F53\u524D\u4EFB\u52A1ID, '\u4F60\u7684\u89D2\u8272', 'code|analysis|config', '/path/to/file', '\u5185\u5BB9\u6458\u8981');\n```\n";
3
+ export declare function brainSkillDefinition(): SkillDefinition;
4
+ //# sourceMappingURL=brain-skill.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brain-skill.d.ts","sourceRoot":"","sources":["../src/brain-skill.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAEnE,eAAO,MAAM,WAAW,43DAqDvB,CAAA;AAED,wBAAgB,oBAAoB,IAAI,eAAe,CAEtD"}
@@ -0,0 +1,57 @@
1
+ export const BRAIN_SKILL = `# brain — 共享记忆系统
2
+
3
+ 你正在一个多 Agent 协作环境中工作。你有一个共享数据库用于记忆和任务协调。
4
+
5
+ ## 环境变量
6
+ - \$DATABASE_URL — PostgreSQL 连接串
7
+
8
+ ## 写入记忆
9
+ 在发现重要信息时,写入 memory 表:
10
+ \`\`\`sql
11
+ INSERT INTO memory (agent, scope, kind, content)
12
+ VALUES ('你的角色', 'task:当前任务ID', 'fact|decision|question|blocker', '内容');
13
+ \`\`\`
14
+
15
+ ## 查询记忆
16
+ 开始工作前,先查看其他 Agent 的发现:
17
+ \`\`\`sql
18
+ SELECT agent, kind, content FROM memory
19
+ WHERE scope = 'task:当前任务ID' AND superseded_by IS NULL
20
+ ORDER BY created_at;
21
+ \`\`\`
22
+
23
+ ## 语义搜索记忆
24
+ \`\`\`sql
25
+ SELECT content, agent, kind FROM memory
26
+ WHERE superseded_by IS NULL
27
+ ORDER BY embedding <=> $query_embedding
28
+ LIMIT 10;
29
+ \`\`\`
30
+
31
+ ## 认领任务
32
+ \`\`\`sql
33
+ UPDATE tasks SET status = 'claimed', claimed_by = '你的角色', claimed_at = now()
34
+ WHERE id = (
35
+ SELECT id FROM tasks
36
+ WHERE status = 'pending'
37
+ AND NOT EXISTS (
38
+ SELECT 1 FROM unnest(depends_on) dep JOIN tasks t ON t.id = dep WHERE t.status != 'done'
39
+ )
40
+ ORDER BY created_at FOR UPDATE SKIP LOCKED LIMIT 1
41
+ ) RETURNING *;
42
+ \`\`\`
43
+
44
+ ## 完成任务
45
+ \`\`\`sql
46
+ UPDATE tasks SET status = 'done', result = '{"summary":"..."}' WHERE id = 当前任务ID;
47
+ \`\`\`
48
+
49
+ ## 注册产出物
50
+ \`\`\`sql
51
+ INSERT INTO artifacts (task_id, agent, kind, path, content)
52
+ VALUES (当前任务ID, '你的角色', 'code|analysis|config', '/path/to/file', '内容摘要');
53
+ \`\`\`
54
+ `;
55
+ export function brainSkillDefinition() {
56
+ return { name: 'brain', content: BRAIN_SKILL };
57
+ }
@@ -0,0 +1,5 @@
1
+ import type { Db9Client } from './client.js';
2
+ export declare const BRAIN_SCHEMA = "\n-- \u5171\u4EAB\u8BB0\u5FC6\nCREATE TABLE IF NOT EXISTS memory (\n id serial PRIMARY KEY,\n agent text NOT NULL,\n scope text NOT NULL DEFAULT 'global',\n kind text NOT NULL CHECK (kind IN ('fact','decision','question','blocker')),\n content text NOT NULL,\n embedding vector(1536),\n created_at timestamptz DEFAULT now(),\n superseded_by int REFERENCES memory(id)\n);\nCREATE INDEX IF NOT EXISTS idx_memory_scope ON memory (scope, kind);\nCREATE INDEX IF NOT EXISTS idx_memory_embedding ON memory USING hnsw (embedding vector_cosine_ops);\n\n-- \u4EFB\u52A1\u534F\u8C03\nCREATE TABLE IF NOT EXISTS tasks (\n id serial PRIMARY KEY,\n title text NOT NULL,\n status text DEFAULT 'pending' CHECK (status IN ('pending','claimed','done','failed')),\n claimed_by text,\n claimed_at timestamptz,\n depends_on int[] DEFAULT '{}',\n result jsonb,\n created_at timestamptz DEFAULT now()\n);\nCREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks (status);\n\n-- \u4EA7\u51FA\u7269\u6CE8\u518C\nCREATE TABLE IF NOT EXISTS artifacts (\n id serial PRIMARY KEY,\n task_id int REFERENCES tasks(id),\n agent text NOT NULL,\n kind text NOT NULL,\n path text,\n content text,\n metadata jsonb,\n created_at timestamptz DEFAULT now()\n);\n";
3
+ /** 初始化 brain schema(含 pgvector 扩展) */
4
+ export declare function initBrainSchema(client: Db9Client, dbId: string): Promise<void>;
5
+ //# sourceMappingURL=brain.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brain.d.ts","sourceRoot":"","sources":["../src/brain.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAE5C,eAAO,MAAM,YAAY,kuCAuCxB,CAAA;AAED,sCAAsC;AACtC,wBAAsB,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGpF"}
package/dist/brain.js ADDED
@@ -0,0 +1,45 @@
1
+ export const BRAIN_SCHEMA = `
2
+ -- 共享记忆
3
+ CREATE TABLE IF NOT EXISTS memory (
4
+ id serial PRIMARY KEY,
5
+ agent text NOT NULL,
6
+ scope text NOT NULL DEFAULT 'global',
7
+ kind text NOT NULL CHECK (kind IN ('fact','decision','question','blocker')),
8
+ content text NOT NULL,
9
+ embedding vector(1536),
10
+ created_at timestamptz DEFAULT now(),
11
+ superseded_by int REFERENCES memory(id)
12
+ );
13
+ CREATE INDEX IF NOT EXISTS idx_memory_scope ON memory (scope, kind);
14
+ CREATE INDEX IF NOT EXISTS idx_memory_embedding ON memory USING hnsw (embedding vector_cosine_ops);
15
+
16
+ -- 任务协调
17
+ CREATE TABLE IF NOT EXISTS tasks (
18
+ id serial PRIMARY KEY,
19
+ title text NOT NULL,
20
+ status text DEFAULT 'pending' CHECK (status IN ('pending','claimed','done','failed')),
21
+ claimed_by text,
22
+ claimed_at timestamptz,
23
+ depends_on int[] DEFAULT '{}',
24
+ result jsonb,
25
+ created_at timestamptz DEFAULT now()
26
+ );
27
+ CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks (status);
28
+
29
+ -- 产出物注册
30
+ CREATE TABLE IF NOT EXISTS artifacts (
31
+ id serial PRIMARY KEY,
32
+ task_id int REFERENCES tasks(id),
33
+ agent text NOT NULL,
34
+ kind text NOT NULL,
35
+ path text,
36
+ content text,
37
+ metadata jsonb,
38
+ created_at timestamptz DEFAULT now()
39
+ );
40
+ `;
41
+ /** 初始化 brain schema(含 pgvector 扩展) */
42
+ export async function initBrainSchema(client, dbId) {
43
+ await client.executeSQL(dbId, 'CREATE EXTENSION IF NOT EXISTS vector');
44
+ await client.executeSQL(dbId, BRAIN_SCHEMA);
45
+ }
@@ -0,0 +1,28 @@
1
+ import type { Db9Database, Db9SqlResult } from './types.js';
2
+ export interface Db9ClientConfig {
3
+ /** db9 API Token */
4
+ token: string;
5
+ /** API Base URL,默认 https://db9.ai/api */
6
+ baseUrl?: string;
7
+ }
8
+ export declare class Db9Client {
9
+ private readonly baseUrl;
10
+ private readonly token;
11
+ constructor(config: Db9ClientConfig);
12
+ /** 创建数据库 */
13
+ createDatabase(name: string): Promise<Db9Database>;
14
+ /** 获取数据库详情 */
15
+ getDatabase(id: string): Promise<Db9Database>;
16
+ /** 列出所有数据库 */
17
+ listDatabases(): Promise<Db9Database[]>;
18
+ /** 删除数据库(幂等) */
19
+ deleteDatabase(id: string): Promise<void>;
20
+ /** 执行 SQL */
21
+ executeSQL(dbId: string, query: string): Promise<Db9SqlResult>;
22
+ /** 创建分支 */
23
+ createBranch(dbId: string, name: string): Promise<Db9Database>;
24
+ /** 删除分支(删除分支数据库) */
25
+ deleteBranch(branchDbId: string): Promise<void>;
26
+ private request;
27
+ }
28
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE3D,MAAM,WAAW,eAAe;IAC9B,oBAAoB;IACpB,KAAK,EAAE,MAAM,CAAA;IACb,yCAAyC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;gBAElB,MAAM,EAAE,eAAe;IAKnC,YAAY;IACN,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAIxD,cAAc;IACR,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAInD,cAAc;IACR,aAAa,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAI7C,gBAAgB;IACV,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/C,aAAa;IACP,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAQpE,WAAW;IACL,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAQpE,oBAAoB;IACd,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAIvC,OAAO;CAiCtB"}
package/dist/client.js ADDED
@@ -0,0 +1,67 @@
1
+ export class Db9Client {
2
+ baseUrl;
3
+ token;
4
+ constructor(config) {
5
+ this.baseUrl = (config.baseUrl ?? 'https://db9.ai/api').replace(/\/$/, '');
6
+ this.token = config.token;
7
+ }
8
+ /** 创建数据库 */
9
+ async createDatabase(name) {
10
+ return this.request('POST', '/customer/databases', { name });
11
+ }
12
+ /** 获取数据库详情 */
13
+ async getDatabase(id) {
14
+ return this.request('GET', `/customer/databases/${encodeURIComponent(id)}`);
15
+ }
16
+ /** 列出所有数据库 */
17
+ async listDatabases() {
18
+ return this.request('GET', '/customer/databases');
19
+ }
20
+ /** 删除数据库(幂等) */
21
+ async deleteDatabase(id) {
22
+ await this.request('DELETE', `/customer/databases/${encodeURIComponent(id)}`);
23
+ }
24
+ /** 执行 SQL */
25
+ async executeSQL(dbId, query) {
26
+ return this.request('POST', `/customer/databases/${encodeURIComponent(dbId)}/sql`, { query });
27
+ }
28
+ /** 创建分支 */
29
+ async createBranch(dbId, name) {
30
+ return this.request('POST', `/customer/databases/${encodeURIComponent(dbId)}/branch`, { name });
31
+ }
32
+ /** 删除分支(删除分支数据库) */
33
+ async deleteBranch(branchDbId) {
34
+ await this.deleteDatabase(branchDbId);
35
+ }
36
+ async request(method, path, body) {
37
+ const url = `${this.baseUrl}${path}`;
38
+ const headers = {
39
+ 'Authorization': `Bearer ${this.token}`,
40
+ 'Accept': 'application/json',
41
+ };
42
+ const init = { method, headers };
43
+ if (body !== undefined) {
44
+ headers['Content-Type'] = 'application/json';
45
+ init.body = JSON.stringify(body);
46
+ }
47
+ const resp = await fetch(url, init);
48
+ if (!resp.ok) {
49
+ let message = `db9 API error: ${resp.status} ${resp.statusText}`;
50
+ try {
51
+ const err = await resp.json();
52
+ if (err.error || err.message) {
53
+ message = `db9 API error: ${err.error ?? err.message}`;
54
+ }
55
+ }
56
+ catch {
57
+ // ignore parse errors
58
+ }
59
+ throw new Error(message);
60
+ }
61
+ // DELETE 等可能无 body
62
+ const text = await resp.text();
63
+ if (!text)
64
+ return undefined;
65
+ return JSON.parse(text);
66
+ }
67
+ }
@@ -0,0 +1,19 @@
1
+ import type { ServiceInfo, SkillDefinition } from '@douglas-agent/sandbank-core';
2
+ import { Db9ServiceAdapter, type Db9AdapterConfig } from './adapter.js';
3
+ /** 一键创建数据库 + 获取 skill */
4
+ export declare function createDb9Service(config: Db9AdapterConfig & {
5
+ name: string;
6
+ }): Promise<{
7
+ service: ServiceInfo;
8
+ skill: SkillDefinition;
9
+ adapter: Db9ServiceAdapter;
10
+ }>;
11
+ /** 一键创建带 brain schema 的多 Agent 数据库 */
12
+ export declare function createDb9Brain(config: Db9AdapterConfig & {
13
+ name: string;
14
+ }): Promise<{
15
+ service: ServiceInfo;
16
+ skills: SkillDefinition[];
17
+ adapter: Db9ServiceAdapter;
18
+ }>;
19
+ //# sourceMappingURL=convenience.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convenience.d.ts","sourceRoot":"","sources":["../src/convenience.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAChF,OAAO,EAAE,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAIvE,yBAAyB;AACzB,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,gBAAgB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAC1C,OAAO,CAAC;IAAE,OAAO,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE,iBAAiB,CAAA;CAAE,CAAC,CAOvF;AAED,sCAAsC;AACtC,wBAAsB,cAAc,CAClC,MAAM,EAAE,gBAAgB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAC1C,OAAO,CAAC;IACT,OAAO,EAAE,WAAW,CAAA;IACpB,MAAM,EAAE,eAAe,EAAE,CAAA;IACzB,OAAO,EAAE,iBAAiB,CAAA;CAC3B,CAAC,CAeD"}
@@ -0,0 +1,29 @@
1
+ import { Db9ServiceAdapter } from './adapter.js';
2
+ import { initBrainSchema } from './brain.js';
3
+ import { brainSkillDefinition } from './brain-skill.js';
4
+ /** 一键创建数据库 + 获取 skill */
5
+ export async function createDb9Service(config) {
6
+ const adapter = new Db9ServiceAdapter(config);
7
+ const [service, skill] = await Promise.all([
8
+ adapter.createService({ type: 'postgres', name: config.name }),
9
+ adapter.getSkill(),
10
+ ]);
11
+ return { service, skill, adapter };
12
+ }
13
+ /** 一键创建带 brain schema 的多 Agent 数据库 */
14
+ export async function createDb9Brain(config) {
15
+ const { service, skill, adapter } = await createDb9Service(config);
16
+ if (service.state !== 'ready') {
17
+ await adapter.destroyService(service.id).catch(() => { });
18
+ throw new Error(`Database '${service.id}' is not ready (state: ${service.state}), cannot initialize brain schema`);
19
+ }
20
+ await initBrainSchema(adapter.client, service.id).catch(async (err) => {
21
+ await adapter.destroyService(service.id).catch(() => { });
22
+ throw err;
23
+ });
24
+ return {
25
+ service,
26
+ skills: [skill, brainSkillDefinition()],
27
+ adapter,
28
+ };
29
+ }
@@ -0,0 +1,9 @@
1
+ import type { SandboxObserver } from '@douglas-agent/sandbank-core';
2
+ import type { Db9Client } from './client.js';
3
+ export declare const EVENTS_SCHEMA = "\nCREATE TABLE IF NOT EXISTS sandbox_events (\n id serial PRIMARY KEY,\n type text NOT NULL,\n sandbox_id text NOT NULL,\n task_id text,\n data jsonb NOT NULL DEFAULT '{}',\n created_at timestamptz DEFAULT now()\n);\nCREATE INDEX IF NOT EXISTS idx_sandbox_events_sandbox ON sandbox_events (sandbox_id);\nCREATE INDEX IF NOT EXISTS idx_sandbox_events_type ON sandbox_events (type);\n";
4
+ /**
5
+ * 创建 DB9 观察者,将沙箱事件写入 sandbox_events 表。
6
+ * 首次调用时自动初始化表结构。
7
+ */
8
+ export declare function createDb9Observer(client: Db9Client, dbId: string): SandboxObserver;
9
+ //# sourceMappingURL=db9-observer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db9-observer.d.ts","sourceRoot":"","sources":["../src/db9-observer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAgB,MAAM,8BAA8B,CAAA;AACjF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAE5C,eAAO,MAAM,aAAa,yYAWzB,CAAA;AAMD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,eAAe,CAkBlF"}
@@ -0,0 +1,36 @@
1
+ export const EVENTS_SCHEMA = `
2
+ CREATE TABLE IF NOT EXISTS sandbox_events (
3
+ id serial PRIMARY KEY,
4
+ type text NOT NULL,
5
+ sandbox_id text NOT NULL,
6
+ task_id text,
7
+ data jsonb NOT NULL DEFAULT '{}',
8
+ created_at timestamptz DEFAULT now()
9
+ );
10
+ CREATE INDEX IF NOT EXISTS idx_sandbox_events_sandbox ON sandbox_events (sandbox_id);
11
+ CREATE INDEX IF NOT EXISTS idx_sandbox_events_type ON sandbox_events (type);
12
+ `;
13
+ function escapeSql(str) {
14
+ return str.replace(/'/g, "''");
15
+ }
16
+ /**
17
+ * 创建 DB9 观察者,将沙箱事件写入 sandbox_events 表。
18
+ * 首次调用时自动初始化表结构。
19
+ */
20
+ export function createDb9Observer(client, dbId) {
21
+ let schemaReady = null;
22
+ function ensureSchema() {
23
+ if (!schemaReady) {
24
+ schemaReady = client.executeSQL(dbId, EVENTS_SCHEMA).then(() => { });
25
+ }
26
+ return schemaReady;
27
+ }
28
+ return {
29
+ async onEvent(event) {
30
+ await ensureSchema();
31
+ const taskIdVal = event.taskId ? `'${escapeSql(event.taskId)}'` : 'NULL';
32
+ const sql = `INSERT INTO sandbox_events (type, sandbox_id, task_id, data, created_at) VALUES ('${escapeSql(event.type)}', '${escapeSql(event.sandboxId)}', ${taskIdVal}, '${escapeSql(JSON.stringify(event.data))}'::jsonb, to_timestamp(${event.timestamp / 1000}))`;
33
+ await client.executeSQL(dbId, sql);
34
+ },
35
+ };
36
+ }
@@ -0,0 +1,11 @@
1
+ export { Db9ServiceAdapter } from './adapter.js';
2
+ export type { Db9AdapterConfig } from './adapter.js';
3
+ export { Db9Client } from './client.js';
4
+ export type { Db9ClientConfig } from './client.js';
5
+ export { fetchDb9Skill, db9SkillDefinition, clearSkillCache } from './skill.js';
6
+ export { BRAIN_SCHEMA, initBrainSchema } from './brain.js';
7
+ export { BRAIN_SKILL, brainSkillDefinition } from './brain-skill.js';
8
+ export { createDb9Service, createDb9Brain } from './convenience.js';
9
+ export { EVENTS_SCHEMA, createDb9Observer } from './db9-observer.js';
10
+ export type { Db9Database, Db9SqlResult, Db9ApiError } from './types.js';
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAChD,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAGpD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAGlD,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAG/E,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAC1D,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAA;AAGpE,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAGnE,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAGpE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,13 @@
1
+ // Adapter
2
+ export { Db9ServiceAdapter } from './adapter.js';
3
+ // Client
4
+ export { Db9Client } from './client.js';
5
+ // Skill
6
+ export { fetchDb9Skill, db9SkillDefinition, clearSkillCache } from './skill.js';
7
+ // Brain
8
+ export { BRAIN_SCHEMA, initBrainSchema } from './brain.js';
9
+ export { BRAIN_SKILL, brainSkillDefinition } from './brain-skill.js';
10
+ // Convenience
11
+ export { createDb9Service, createDb9Brain } from './convenience.js';
12
+ // Observer
13
+ export { EVENTS_SCHEMA, createDb9Observer } from './db9-observer.js';
@@ -0,0 +1,8 @@
1
+ import type { SkillDefinition } from '@douglas-agent/sandbank-core';
2
+ /** 获取 db9 官方 skill,带 24h 内存缓存和并发去重 */
3
+ export declare function fetchDb9Skill(): Promise<string>;
4
+ /** 构建注入用的 SkillDefinition */
5
+ export declare function db9SkillDefinition(content: string): SkillDefinition;
6
+ /** 清除 skill 缓存(测试用)。不清空 inflightRequest,让飞行中的请求自然完成。 */
7
+ export declare function clearSkillCache(): void;
8
+ //# sourceMappingURL=skill.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill.d.ts","sourceRoot":"","sources":["../src/skill.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAQnE,sCAAsC;AACtC,wBAAsB,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CAkBrD;AAED,6BAA6B;AAC7B,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,CAEnE;AAED,wDAAwD;AACxD,wBAAgB,eAAe,IAAI,IAAI,CAEtC"}
package/dist/skill.js ADDED
@@ -0,0 +1,32 @@
1
+ const SKILL_URL = 'https://db9.ai/skill.md';
2
+ const CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours
3
+ let cachedSkill = null;
4
+ let inflightRequest = null;
5
+ /** 获取 db9 官方 skill,带 24h 内存缓存和并发去重 */
6
+ export async function fetchDb9Skill() {
7
+ if (cachedSkill && Date.now() - cachedSkill.fetchedAt < CACHE_TTL) {
8
+ return cachedSkill.content;
9
+ }
10
+ if (!inflightRequest) {
11
+ inflightRequest = (async () => {
12
+ const resp = await fetch(SKILL_URL);
13
+ if (!resp.ok) {
14
+ throw new Error(`Failed to fetch db9 skill: ${resp.status} ${resp.statusText}`);
15
+ }
16
+ const content = await resp.text();
17
+ cachedSkill = { content, fetchedAt: Date.now() };
18
+ return content;
19
+ })().finally(() => {
20
+ inflightRequest = null;
21
+ });
22
+ }
23
+ return inflightRequest;
24
+ }
25
+ /** 构建注入用的 SkillDefinition */
26
+ export function db9SkillDefinition(content) {
27
+ return { name: 'db9-postgres', content };
28
+ }
29
+ /** 清除 skill 缓存(测试用)。不清空 inflightRequest,让飞行中的请求自然完成。 */
30
+ export function clearSkillCache() {
31
+ cachedSkill = null;
32
+ }
@@ -0,0 +1,25 @@
1
+ /** db9 API 响应:数据库 */
2
+ export interface Db9Database {
3
+ id: string;
4
+ name: string;
5
+ state: string;
6
+ host: string;
7
+ port: number;
8
+ username: string;
9
+ password: string;
10
+ database: string;
11
+ connection_string: string;
12
+ created_at: string;
13
+ }
14
+ /** db9 API 响应:SQL 执行结果 */
15
+ export interface Db9SqlResult {
16
+ columns: string[];
17
+ rows: unknown[][];
18
+ row_count: number;
19
+ }
20
+ /** db9 API 响应:错误 */
21
+ export interface Db9ApiError {
22
+ error: string;
23
+ message?: string;
24
+ }
25
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,qBAAqB;AACrB,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,iBAAiB,EAAE,MAAM,CAAA;IACzB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,0BAA0B;AAC1B,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,IAAI,EAAE,OAAO,EAAE,EAAE,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,oBAAoB;AACpB,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB"}
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@douglas-agent/sandbank-db9",
3
+ "version": "0.2.1",
4
+ "description": "db9.ai service adapter for Sandbank — serverless PostgreSQL for AI agents",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "homepage": "https://sandbank.dev",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/Xeonice/sandbank-douglas-agent.git",
11
+ "directory": "packages/db9"
12
+ },
13
+ "keywords": [
14
+ "sandbox",
15
+ "ai-agent",
16
+ "db9",
17
+ "postgres",
18
+ "database",
19
+ "service"
20
+ ],
21
+ "exports": {
22
+ ".": {
23
+ "types": "./dist/index.d.ts",
24
+ "import": "./dist/index.js"
25
+ }
26
+ },
27
+ "files": [
28
+ "dist"
29
+ ],
30
+ "scripts": {
31
+ "build": "tsc",
32
+ "typecheck": "tsc --noEmit",
33
+ "clean": "rm -rf dist",
34
+ "test": "vitest run",
35
+ "test:e2e": "vitest run --config vitest.e2e.config.ts"
36
+ },
37
+ "dependencies": {
38
+ "@douglas-agent/sandbank-core": "^0.3.6"
39
+ },
40
+ "devDependencies": {
41
+ "typescript": "^5.7.3",
42
+ "vitest": "^4.0.18"
43
+ }
44
+ }