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 个工具按需增量加载,保持系统提示词前缀稳定
- **笔记本级 RWD 权限审计**:R 读 / W 写 / D 破坏性(rollback、move 移出、删除类),可自由组合;`NONE` = 禁止一切操作
- 拒绝消息包含:目标笔记本(id+name)、需要的权限、缺失的权限、被拒绝的操作名
- 新笔记本自动以 `R` 权限同步进配置文件(发现入口 `list_notebooks`审计豁免)
- Pi 启动及调用 `list_notebooks` 时自动同步笔记本;新笔记本以 `R` 权限写入配置文件(审计豁免)
- 连接信息:环境变量优先,配置文件兜底
## 安装
@@ -33,7 +33,7 @@ pi -e git:git.ipao.vip:rogee/pi-siyuan
```bash
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`)。
@@ -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
```
缺失时回落到 `~/.pi/agent/extensions/pi-siyuan/config.json`
修改 `~/.zshenv` 后需新开 shell 并重启 Pi(已有进程不会自动获得新环境变量)。缺失时回落到 `~/.pi/agent/extensions/pi-siyuan/config.json`
```json
{
@@ -64,7 +64,7 @@ export SIYUAN_TOKEN="your-api-token" # SiYuan 设置 → 关于
- `permissions``R`/`W`/`D` 自由组合,或 `["NONE"]`(禁止一切操作)
- `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",
"version": "0.1.0",
"version": "0.1.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "pi-siyuan",
"version": "0.1.0",
"version": "0.1.3",
"license": "MIT",
"dependencies": {
"typebox": "1.3.7"
+5 -4
View File
@@ -5,8 +5,8 @@
"dependencies": {
"typebox": "1.3.7"
},
"version": "0.1.0",
"description": "SiYuan Note extension for pi coding agent \u2014 HTTP API wrapper with notebook-level RWD permission auditing",
"version": "0.1.3",
"description": "SiYuan Note extension for pi coding agent HTTP API wrapper with notebook-level RWD permission auditing",
"keywords": [
"pi-package",
"pi-extension",
@@ -20,14 +20,15 @@
},
"pi": {
"extensions": [
"./src"
"./index.ts"
]
},
"devDependencies": {
"@earendil-works/pi-coding-agent": "0.84.4"
},
"main": "src/index.ts",
"main": "index.ts",
"files": [
"index.ts",
"src",
"README.md",
"LICENSE",
+15
View File
@@ -4,12 +4,14 @@
*/
import assert from "node:assert";
import { existsSync, rmSync } from "node:fs";
import {
permsOf,
auditNotebook,
auditAny,
auditAll,
denyMessage,
loadConfig,
syncNotebooks,
} from "./audit.ts";
@@ -87,6 +89,19 @@ assert.ok(
assert.ok(msg.includes("get_document_content"), "含操作名");
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 条)— 用临时路径,不污染真实配置 ----
const cfg: any = {
apiUrl: "u",
+9 -5
View File
@@ -5,7 +5,7 @@
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
import { homedir } from "node:os";
import { join } from "node:path";
import { dirname, join } from "node:path";
import type { SiYuanClient } from "./siyuan-client.ts";
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 } {
@@ -121,9 +127,7 @@ export function syncNotebooks(
}
export function saveConfig(cfg: PiSiyuanConfig, cfgPath: string = CONFIG_PATH) {
mkdirSync(join(homedir(), ".pi/agent/extensions/pi-siyuan"), {
recursive: true,
});
mkdirSync(dirname(cfgPath), { recursive: true });
writeFileSync(cfgPath, JSON.stringify(cfg, null, 2) + "\n");
}
+7 -2
View File
@@ -422,11 +422,16 @@ export default function siyuanExtension(pi: ExtensionAPI) {
},
});
// 初始只激活 loader(渐进式披露)
pi.on("session_start", () => {
// 初始只激活 loader(渐进式披露),并同步笔记本权限配置
pi.on("session_start", async () => {
const initial = pi
.getActiveTools()
.filter((name) => !SIYUAN_TOOL_NAMES.has(name));
pi.setActiveTools([...new Set([...initial, "siyuan_discover"])]);
try {
syncNotebooks(cfg, await client.lsNotebooks());
} catch {
// SiYuan 离线不应阻止 Pi 启动;工具调用时会返回连接错误
}
});
}