3 Commits
Author SHA1 Message Date
rogee e33c50754c fix: initialize and sync notebook config on startup
Publish to npm / publish (push) Successful in 29s
2026-09-02 22:41:21 +08:00
rogee 776f31fb47 fix: use package-root extension entrypoint
Publish to npm / publish (push) Successful in 30s
2026-09-02 22:29:10 +08:00
rogee 1b47f33a7e fix: use explicit pi extension entrypoint
Publish to npm / publish (push) Successful in 28s
2026-09-02 22:16:56 +08:00
7 changed files with 44 additions and 18 deletions
+4 -4
View File
@@ -8,7 +8,7 @@
- **渐进式披露**:初始仅激活一个 `siyuan_discover` loader 工具(含连通性检查),其余 15 个工具按需增量加载,保持系统提示词前缀稳定 - **渐进式披露**:初始仅激活一个 `siyuan_discover` loader 工具(含连通性检查),其余 15 个工具按需增量加载,保持系统提示词前缀稳定
- **笔记本级 RWD 权限审计**:R 读 / W 写 / D 破坏性(rollback、move 移出、删除类),可自由组合;`NONE` = 禁止一切操作 - **笔记本级 RWD 权限审计**:R 读 / W 写 / D 破坏性(rollback、move 移出、删除类),可自由组合;`NONE` = 禁止一切操作
- 拒绝消息包含:目标笔记本(id+name)、需要的权限、缺失的权限、被拒绝的操作名 - 拒绝消息包含:目标笔记本(id+name)、需要的权限、缺失的权限、被拒绝的操作名
- 新笔记本自动以 `R` 权限同步进配置文件(发现入口 `list_notebooks`审计豁免) - Pi 启动及调用 `list_notebooks` 时自动同步笔记本;新笔记本以 `R` 权限写入配置文件(审计豁免)
- 连接信息:环境变量优先,配置文件兜底 - 连接信息:环境变量优先,配置文件兜底
## 安装 ## 安装
@@ -33,7 +33,7 @@ pi -e git:git.ipao.vip:rogee/pi-siyuan
```bash ```bash
git clone git@git.ipao.vip:rogee/pi-siyuan.git ~/.pi/agent/extensions/pi-siyuan git clone git@git.ipao.vip:rogee/pi-siyuan.git ~/.pi/agent/extensions/pi-siyuan
# src/ 内含 index.tspi 会自动加载 extensions/*/index.ts # 包根目录含 index.tspi 会自动加载 extensions/*/index.ts
``` ```
发布流程:推送 tag(如 `v0.1.0`)触发 Gitea workflow 自动发布到 npm`.gitea/workflows/publish-npm.yml`,使用仓库 secret `NPM_PACKAGE_TOKEN`)。 发布流程:推送 tag(如 `v0.1.0`)触发 Gitea workflow 自动发布到 npm`.gitea/workflows/publish-npm.yml`,使用仓库 secret `NPM_PACKAGE_TOKEN`)。
@@ -48,7 +48,7 @@ export SIYUAN_API_URL="https://your-siyuan-host" # 默认 http://127.0.0.1:680
export SIYUAN_TOKEN="your-api-token" # SiYuan 设置 → 关于 → API token export SIYUAN_TOKEN="your-api-token" # SiYuan 设置 → 关于 → API token
``` ```
缺失时回落到 `~/.pi/agent/extensions/pi-siyuan/config.json` 修改 `~/.zshenv` 后需新开 shell 并重启 Pi(已有进程不会自动获得新环境变量)。缺失时回落到 `~/.pi/agent/extensions/pi-siyuan/config.json`
```json ```json
{ {
@@ -64,7 +64,7 @@ export SIYUAN_TOKEN="your-api-token" # SiYuan 设置 → 关于
- `permissions``R`/`W`/`D` 自由组合,或 `["NONE"]`(禁止一切操作) - `permissions``R`/`W`/`D` 自由组合,或 `["NONE"]`(禁止一切操作)
- `name` 仅做 human-readable,一切逻辑按 `id` 匹配 - `name` 仅做 human-readable,一切逻辑按 `id` 匹配
- 首次调用 `list_notebooks` 会把未记录的笔记本以 `["R"]` 补写进该文件 - Pi 启动时会创建该配置文件并同步笔记本;调用 `list_notebooks` 时也会再次同步。未记录的笔记本以 `["R"]` 补写,已有笔记本名称变化时更新 `name`
### 权限规则速查 ### 权限规则速查
+1
View File
@@ -0,0 +1 @@
export { default } from "./src/index.ts";
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "pi-siyuan", "name": "pi-siyuan",
"version": "0.1.0", "version": "0.1.3",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "pi-siyuan", "name": "pi-siyuan",
"version": "0.1.0", "version": "0.1.3",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"typebox": "1.3.7" "typebox": "1.3.7"
+6 -5
View File
@@ -5,8 +5,8 @@
"dependencies": { "dependencies": {
"typebox": "1.3.7" "typebox": "1.3.7"
}, },
"version": "0.1.0", "version": "0.1.3",
"description": "SiYuan Note extension for pi coding agent \u2014 HTTP API wrapper with notebook-level RWD permission auditing", "description": "SiYuan Note extension for pi coding agent HTTP API wrapper with notebook-level RWD permission auditing",
"keywords": [ "keywords": [
"pi-package", "pi-package",
"pi-extension", "pi-extension",
@@ -20,14 +20,15 @@
}, },
"pi": { "pi": {
"extensions": [ "extensions": [
"./src" "./index.ts"
] ]
}, },
"devDependencies": { "devDependencies": {
"@earendil-works/pi-coding-agent": "0.84.4" "@earendil-works/pi-coding-agent": "0.84.4"
}, },
"main": "src/index.ts", "main": "index.ts",
"files": [ "files": [
"index.ts",
"src", "src",
"README.md", "README.md",
"LICENSE", "LICENSE",
@@ -39,4 +40,4 @@
"check": "node --experimental-strip-types --no-warnings src/audit.test.ts" "check": "node --experimental-strip-types --no-warnings src/audit.test.ts"
}, },
"prepublishOnly": "node --experimental-strip-types --no-warnings src/audit.test.ts" "prepublishOnly": "node --experimental-strip-types --no-warnings src/audit.test.ts"
} }
+15
View File
@@ -4,12 +4,14 @@
*/ */
import assert from "node:assert"; import assert from "node:assert";
import { existsSync, rmSync } from "node:fs";
import { import {
permsOf, permsOf,
auditNotebook, auditNotebook,
auditAny, auditAny,
auditAll, auditAll,
denyMessage, denyMessage,
loadConfig,
syncNotebooks, syncNotebooks,
} from "./audit.ts"; } from "./audit.ts";
@@ -87,6 +89,19 @@ assert.ok(
assert.ok(msg.includes("get_document_content"), "含操作名"); assert.ok(msg.includes("get_document_content"), "含操作名");
assert.ok(/需要 R/.test(msg) && /缺失 R/.test(msg), "含需要/缺失权限"); assert.ok(/需要 R/.test(msg) && /缺失 R/.test(msg), "含需要/缺失权限");
// ---- 配置首次加载自动初始化 ----
const initDir = "/tmp/pi-siyuan-audit-test";
const initPath = `${initDir}/nested/config.json`;
rmSync(initDir, { recursive: true, force: true });
const initialized = loadConfig(initPath);
assert.equal(existsSync(initPath), true, "配置文件应在首次加载时创建");
assert.deepEqual(initialized, {
apiUrl: "http://127.0.0.1:6806",
token: "",
notebooks: [],
});
rmSync(initDir, { recursive: true, force: true });
// ---- syncNotebooks(第 10 条)— 用临时路径,不污染真实配置 ---- // ---- syncNotebooks(第 10 条)— 用临时路径,不污染真实配置 ----
const cfg: any = { const cfg: any = {
apiUrl: "u", apiUrl: "u",
+9 -5
View File
@@ -5,7 +5,7 @@
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs"; import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
import { homedir } from "node:os"; import { homedir } from "node:os";
import { join } from "node:path"; import { dirname, join } from "node:path";
import type { SiYuanClient } from "./siyuan-client.ts"; import type { SiYuanClient } from "./siyuan-client.ts";
export type Perm = "R" | "W" | "D"; export type Perm = "R" | "W" | "D";
@@ -86,7 +86,13 @@ export function loadConfig(cfgPath: string = CONFIG_PATH): PiSiyuanConfig {
// 损坏则重建 // 损坏则重建
} }
} }
return { apiUrl: "", token: "", notebooks: [] }; const cfg = {
apiUrl: "http://127.0.0.1:6806",
token: "",
notebooks: [],
};
saveConfig(cfg, cfgPath);
return cfg;
} }
export function resolveConnection(): { apiUrl: string; token: string } { export function resolveConnection(): { apiUrl: string; token: string } {
@@ -121,9 +127,7 @@ export function syncNotebooks(
} }
export function saveConfig(cfg: PiSiyuanConfig, cfgPath: string = CONFIG_PATH) { export function saveConfig(cfg: PiSiyuanConfig, cfgPath: string = CONFIG_PATH) {
mkdirSync(join(homedir(), ".pi/agent/extensions/pi-siyuan"), { mkdirSync(dirname(cfgPath), { recursive: true });
recursive: true,
});
writeFileSync(cfgPath, JSON.stringify(cfg, null, 2) + "\n"); writeFileSync(cfgPath, JSON.stringify(cfg, null, 2) + "\n");
} }
+7 -2
View File
@@ -422,11 +422,16 @@ export default function siyuanExtension(pi: ExtensionAPI) {
}, },
}); });
// 初始只激活 loader(渐进式披露) // 初始只激活 loader(渐进式披露),并同步笔记本权限配置
pi.on("session_start", () => { pi.on("session_start", async () => {
const initial = pi const initial = pi
.getActiveTools() .getActiveTools()
.filter((name) => !SIYUAN_TOOL_NAMES.has(name)); .filter((name) => !SIYUAN_TOOL_NAMES.has(name));
pi.setActiveTools([...new Set([...initial, "siyuan_discover"])]); pi.setActiveTools([...new Set([...initial, "siyuan_discover"])]);
try {
syncNotebooks(cfg, await client.lsNotebooks());
} catch {
// SiYuan 离线不应阻止 Pi 启动;工具调用时会返回连接错误
}
}); });
} }