butler-mcp 0.1.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/README.md +159 -0
- package/dist/auth.d.ts +15 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +23 -0
- package/dist/auth.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +40 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +13 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +47 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/scope-guard.d.ts +14 -0
- package/dist/scope-guard.d.ts.map +1 -0
- package/dist/scope-guard.js +33 -0
- package/dist/scope-guard.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +40 -0
- package/dist/server.js.map +1 -0
- package/dist/services/audit.d.ts +13 -0
- package/dist/services/audit.d.ts.map +1 -0
- package/dist/services/audit.js +16 -0
- package/dist/services/audit.js.map +1 -0
- package/dist/services/config.d.ts +17 -0
- package/dist/services/config.d.ts.map +1 -0
- package/dist/services/config.js +20 -0
- package/dist/services/config.js.map +1 -0
- package/dist/services/drive.d.ts +14 -0
- package/dist/services/drive.d.ts.map +1 -0
- package/dist/services/drive.js +96 -0
- package/dist/services/drive.js.map +1 -0
- package/dist/services/sheets.d.ts +40 -0
- package/dist/services/sheets.d.ts.map +1 -0
- package/dist/services/sheets.js +164 -0
- package/dist/services/sheets.js.map +1 -0
- package/dist/tools/config.d.ts +18 -0
- package/dist/tools/config.d.ts.map +1 -0
- package/dist/tools/config.js +52 -0
- package/dist/tools/config.js.map +1 -0
- package/dist/tools/drive.d.ts +18 -0
- package/dist/tools/drive.d.ts.map +1 -0
- package/dist/tools/drive.js +65 -0
- package/dist/tools/drive.js.map +1 -0
- package/dist/tools/sheets.d.ts +18 -0
- package/dist/tools/sheets.d.ts.map +1 -0
- package/dist/tools/sheets.js +83 -0
- package/dist/tools/sheets.js.map +1 -0
- package/dist/utils/diff.d.ts +14 -0
- package/dist/utils/diff.d.ts.map +1 -0
- package/dist/utils/diff.js +17 -0
- package/dist/utils/diff.js.map +1 -0
- package/dist/validation/schema.d.ts +10 -0
- package/dist/validation/schema.d.ts.map +1 -0
- package/dist/validation/schema.js +28 -0
- package/dist/validation/schema.js.map +1 -0
- package/package.json +41 -0
package/README.md
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# butler-mcp
|
|
2
|
+
|
|
3
|
+
MCP server for **Butler**: Google Drive / Google Sheets 連携でタスク台帳と執事設定を扱う。
|
|
4
|
+
Cursor 等の MCP クライアントから `npx butler-mcp` で起動する。
|
|
5
|
+
|
|
6
|
+
## セットアップ
|
|
7
|
+
|
|
8
|
+
1. **依存のインストール**
|
|
9
|
+
```bash
|
|
10
|
+
npm install
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
2. **必須**
|
|
14
|
+
- **driveRootId** … Butler 用ルートフォルダの Google Drive フォルダ ID(必須)
|
|
15
|
+
- **spreadsheetId** … 省略可。省略時は Drive 内で名前 `"Tasks"` のスプレッドシートを検索し、なければ自動作成
|
|
16
|
+
- **credentialsPath** … サービスアカウント JSON のパス(任意。未指定時は環境変数 `GOOGLE_APPLICATION_CREDENTIALS` を参照)
|
|
17
|
+
|
|
18
|
+
3. **設定ファイル**(任意)
|
|
19
|
+
```bash
|
|
20
|
+
npx butler-mcp --config ./butler.config.json
|
|
21
|
+
```
|
|
22
|
+
JSON 例: `{ "driveRootId": "...", "credentialsPath": "/path/to/key.json" }`(spreadsheetId は任意)
|
|
23
|
+
|
|
24
|
+
## Google Drive / Sheets の認証(サービスアカウント)
|
|
25
|
+
|
|
26
|
+
butler-mcp は **サービスアカウント** で Google API にアクセスする。手順は次のとおり。
|
|
27
|
+
|
|
28
|
+
### 1. Google Cloud でプロジェクトを作る
|
|
29
|
+
|
|
30
|
+
1. [Google Cloud Console](https://console.cloud.google.com/) にログイン
|
|
31
|
+
2. 画面上部のプロジェクト選択 → **新しいプロジェクト** で作成(例: `butler-mcp`)
|
|
32
|
+
|
|
33
|
+
### 2. API を有効にする
|
|
34
|
+
|
|
35
|
+
1. **API とサービス** → **ライブラリ**
|
|
36
|
+
2. **Google Drive API** を検索 → **有効にする**
|
|
37
|
+
3. 同様に **Google Sheets API** を検索 → **有効にする**
|
|
38
|
+
|
|
39
|
+
### 3. サービスアカウントとキーを作る
|
|
40
|
+
|
|
41
|
+
1. **API とサービス** → **認証情報** → **認証情報を作成** → **サービスアカウント**
|
|
42
|
+
2. 名前(例: `butler-mcp`)を入れて **作成**
|
|
43
|
+
3. ロールは省略で OK → **完了**
|
|
44
|
+
4. 一覧で作ったサービスアカウントをクリック → **キー** タブ
|
|
45
|
+
5. **鍵を追加** → **新しい鍵を作成** → **JSON** を選んで **作成**
|
|
46
|
+
6. ダウンロードされた JSON ファイルを安全な場所に保存(例: `~/butler-service-account.json`)
|
|
47
|
+
|
|
48
|
+
この JSON が **秘密鍵**。Git にコミットしたり公開しないこと。
|
|
49
|
+
|
|
50
|
+
### 4. Drive のフォルダをサービスアカウントに共有する
|
|
51
|
+
|
|
52
|
+
サービスアカウントは「別の Google アカウント」なので、**あなたの Drive のフォルダを共有しないとアクセスできない**。
|
|
53
|
+
|
|
54
|
+
1. Google Drive で Butler 用のルートフォルダ(例: `Butler`)を作成
|
|
55
|
+
2. フォルダを右クリック → **共有**
|
|
56
|
+
3. **ユーザーやグループを追加** に、サービスアカウントの **メールアドレス** を入力
|
|
57
|
+
(JSON 内の `client_email` の値。例: `butler-mcp@your-project.iam.gserviceaccount.com`)
|
|
58
|
+
4. 権限を **編集者** にして **送信**
|
|
59
|
+
5. フォルダの URL から **フォルダ ID** を取得
|
|
60
|
+
例: `https://drive.google.com/drive/folders/1ABC...xyz` → `1ABC...xyz` が `driveRootId`
|
|
61
|
+
|
|
62
|
+
### 5. butler-mcp に渡す
|
|
63
|
+
|
|
64
|
+
- **環境変数**: `GOOGLE_APPLICATION_CREDENTIALS=/path/to/サービスアカウント.json`
|
|
65
|
+
- **または args**: `--credentialsPath` に上記 JSON の絶対パスを指定(MCP 設定で渡す)
|
|
66
|
+
|
|
67
|
+
これで Drive / Sheets への認証が通る。
|
|
68
|
+
|
|
69
|
+
### このリポジトリで `googleCloudKey.json` を使う場合
|
|
70
|
+
|
|
71
|
+
キーをリポジトリ直下の `googleCloudKey.json` に置いた場合の例(パスは環境に合わせて書き換えてね):
|
|
72
|
+
|
|
73
|
+
**ターミナルで起動する場合**
|
|
74
|
+
```bash
|
|
75
|
+
# リポジトリルート(TAISHO)で
|
|
76
|
+
export GOOGLE_APPLICATION_CREDENTIALS="$(pwd)/googleCloudKey.json"
|
|
77
|
+
cd butler-mcp && npx butler-mcp --driveRootId "あなたのDriveルートフォルダID"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Cursor の MCP 設定(args で渡す)**
|
|
81
|
+
|
|
82
|
+
`googleCloudKey.json` をリポジトリルートに置いているとき、`--credentialsPath` に **絶対パス** を指定する:
|
|
83
|
+
|
|
84
|
+
```json
|
|
85
|
+
"args": [
|
|
86
|
+
"butler-mcp",
|
|
87
|
+
"--driveRootId",
|
|
88
|
+
"あなたのDriveルートフォルダID",
|
|
89
|
+
"--credentialsPath",
|
|
90
|
+
"/Users/charu/Library/CloudStorage/Dropbox/mac_setting/work/private/TAISHO/googleCloudKey.json"
|
|
91
|
+
]
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
※ `googleCloudKey.json` は `.gitignore` に入れてあるので Git にはコミットされない。
|
|
95
|
+
|
|
96
|
+
## Cursor など MCP クライアントでの設定
|
|
97
|
+
|
|
98
|
+
`args` に **キーと値を並べて** 渡す(フラットな羅列):
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"mcpServers": {
|
|
103
|
+
"butler-mcp": {
|
|
104
|
+
"command": "npx",
|
|
105
|
+
"args": [
|
|
106
|
+
"butler-mcp",
|
|
107
|
+
"--driveRootId",
|
|
108
|
+
"YOUR_DRIVE_ROOT_FOLDER_ID",
|
|
109
|
+
"--credentialsPath",
|
|
110
|
+
"/absolute/path/to/service-account.json"
|
|
111
|
+
]
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
- **--driveRootId** … Butler 用 Drive ルートフォルダ ID(必須)
|
|
118
|
+
- **--spreadsheetId** … タスク台帳のスプレッドシート ID(任意。省略時は Drive 内で `"Tasks"` を検索 or 作成)
|
|
119
|
+
- **--credentialsPath** … 秘密鍵(サービスアカウント JSON)の絶対パス(任意。未指定時は環境変数 `GOOGLE_APPLICATION_CREDENTIALS`)
|
|
120
|
+
|
|
121
|
+
設定ファイルでまとめて渡す場合:
|
|
122
|
+
|
|
123
|
+
```json
|
|
124
|
+
"args": ["butler-mcp", "--config", "/absolute/path/to/butler.config.json"]
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## ビルド・実行
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
npm run build
|
|
131
|
+
npm start
|
|
132
|
+
# または
|
|
133
|
+
npx butler-mcp
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## 提供ツール(MCP)
|
|
137
|
+
|
|
138
|
+
- **sheets.queryTasks** … タスク一覧を条件付きで取得(status, due_date_before, priority, limit)
|
|
139
|
+
- **sheets.addTask** … タスク 1 件追加(title 必須)。成功時に監査ログへ追記
|
|
140
|
+
- **sheets.updateTask** … タスク更新(id, dryRun, patch)。dryRun=true で diff のみ返却。適用時に監査ログへ追記
|
|
141
|
+
- **drive.search** … Butler ルート配下のファイル検索(query オプション)
|
|
142
|
+
- **drive.readFile** … fileId または path(例: `config/persona.yml`)でファイル内容取得
|
|
143
|
+
- **drive.writeFile** … path, content, mode(create|replace|append)で書き込み。成功時に監査ログへ追記
|
|
144
|
+
- **drive.getConfigBundle** … persona / policies / templates を Drive から取得(執事名は persona.name)
|
|
145
|
+
- **drive.updateConfig** … target=persona|policies, patch, dryRun 必須で設定更新。適用時に監査ログへ追記
|
|
146
|
+
|
|
147
|
+
監査ログは `Butler/history/audit-log.ndjson` に NDJSON で追記される。
|
|
148
|
+
|
|
149
|
+
## テスト
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
npm test
|
|
153
|
+
```
|
|
154
|
+
(スコープガード・Diff・Persona/Policies スキーマ検証の単体テスト)
|
|
155
|
+
|
|
156
|
+
## 仕様・設計
|
|
157
|
+
|
|
158
|
+
- 要件・設計・タスク: リポジトリルートの `.spec-workflow/specs/butler-mcp/` を参照
|
|
159
|
+
- 開発は TDD(テスト先行)。スコープガード・Diff は単体テストあり
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Auth: Sheets + Drive API 用の認証済みクライアント取得
|
|
3
|
+
*/
|
|
4
|
+
import { google } from "googleapis";
|
|
5
|
+
import type { ButlerConfig } from "./config.js";
|
|
6
|
+
export type AuthClients = {
|
|
7
|
+
sheets: ReturnType<typeof google.sheets>;
|
|
8
|
+
drive: ReturnType<typeof google.drive>;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Returns authenticated Google API clients for Sheets and Drive.
|
|
12
|
+
* Uses GOOGLE_APPLICATION_CREDENTIALS (or config.credentialsPath) for Service Account.
|
|
13
|
+
*/
|
|
14
|
+
export declare function getAuthClients(config: ButlerConfig): Promise<AuthClients>;
|
|
15
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACpC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,UAAU,CAAC,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC;IACzC,KAAK,EAAE,UAAU,CAAC,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;CACxC,CAAC;AAEF;;;GAGG;AACH,wBAAsB,cAAc,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CAa/E"}
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Auth: Sheets + Drive API 用の認証済みクライアント取得
|
|
3
|
+
*/
|
|
4
|
+
import { google } from "googleapis";
|
|
5
|
+
/**
|
|
6
|
+
* Returns authenticated Google API clients for Sheets and Drive.
|
|
7
|
+
* Uses GOOGLE_APPLICATION_CREDENTIALS (or config.credentialsPath) for Service Account.
|
|
8
|
+
*/
|
|
9
|
+
export async function getAuthClients(config) {
|
|
10
|
+
const auth = new google.auth.GoogleAuth({
|
|
11
|
+
keyFile: config.credentialsPath ?? process.env.GOOGLE_APPLICATION_CREDENTIALS,
|
|
12
|
+
scopes: [
|
|
13
|
+
"https://www.googleapis.com/auth/spreadsheets",
|
|
14
|
+
"https://www.googleapis.com/auth/drive",
|
|
15
|
+
"https://www.googleapis.com/auth/drive.file",
|
|
16
|
+
],
|
|
17
|
+
});
|
|
18
|
+
const client = await auth.getClient();
|
|
19
|
+
const sheets = google.sheets({ version: "v4", auth: client });
|
|
20
|
+
const drive = google.drive({ version: "v3", auth: client });
|
|
21
|
+
return { sheets, drive };
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=auth.js.map
|
package/dist/auth.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAQpC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,MAAoB;IACvD,MAAM,IAAI,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;QACtC,OAAO,EAAE,MAAM,CAAC,eAAe,IAAI,OAAO,CAAC,GAAG,CAAC,8BAA8B;QAC7E,MAAM,EAAE;YACN,8CAA8C;YAC9C,uCAAuC;YACvC,4CAA4C;SAC7C;KACF,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;IACtC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAe,EAAE,CAAC,CAAC;IACvE,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAe,EAAE,CAAC,CAAC;IACrE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;AAC3B,CAAC"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CLI entry for npx butler-mcp
|
|
4
|
+
*/
|
|
5
|
+
import { loadConfig } from "./config.js";
|
|
6
|
+
import { runServer } from "./server.js";
|
|
7
|
+
function parseArgs() {
|
|
8
|
+
const args = process.argv.slice(2);
|
|
9
|
+
const out = {};
|
|
10
|
+
for (let i = 0; i < args.length; i++) {
|
|
11
|
+
const a = args[i];
|
|
12
|
+
if (a === "--config" && args[i + 1]) {
|
|
13
|
+
out.configFilePath = args[++i];
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
if (a === "--driveRootId" && args[i + 1]) {
|
|
17
|
+
out.driveRootId = args[++i];
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
if (a === "--spreadsheetId" && args[i + 1]) {
|
|
21
|
+
out.spreadsheetId = args[++i];
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (a === "--credentialsPath" && args[i + 1]) {
|
|
25
|
+
out.credentialsPath = args[++i];
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
async function main() {
|
|
32
|
+
const opts = parseArgs();
|
|
33
|
+
const config = loadConfig(opts);
|
|
34
|
+
await runServer(config);
|
|
35
|
+
}
|
|
36
|
+
main().catch((err) => {
|
|
37
|
+
console.error(err);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
});
|
|
40
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;GAEG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,SAAS,SAAS;IAMhB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,IAAI,CAAC,KAAK,UAAU,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YACpC,GAAG,CAAC,cAAc,GAAG,IAAI,CAAC,EAAE,CAAC,CAAE,CAAC;YAChC,SAAS;QACX,CAAC;QACD,IAAI,CAAC,KAAK,eAAe,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YACzC,GAAG,CAAC,WAAW,GAAG,IAAI,CAAC,EAAE,CAAC,CAAE,CAAC;YAC7B,SAAS;QACX,CAAC;QACD,IAAI,CAAC,KAAK,iBAAiB,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YAC3C,GAAG,CAAC,aAAa,GAAG,IAAI,CAAC,EAAE,CAAC,CAAE,CAAC;YAC/B,SAAS;QACX,CAAC;QACD,IAAI,CAAC,KAAK,mBAAmB,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YAC7C,GAAG,CAAC,eAAe,GAAG,IAAI,CAAC,EAAE,CAAC,CAAE,CAAC;YACjC,SAAS;QACX,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,SAAS,EAAE,CAAC;IACzB,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAChC,MAAM,SAAS,CAAC,MAAM,CAAC,CAAC;AAC1B,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface ButlerConfig {
|
|
2
|
+
driveRootId: string;
|
|
3
|
+
spreadsheetId?: string;
|
|
4
|
+
credentialsPath?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface LoadConfigOptions {
|
|
7
|
+
driveRootId?: string;
|
|
8
|
+
spreadsheetId?: string;
|
|
9
|
+
credentialsPath?: string;
|
|
10
|
+
configFilePath?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function loadConfig(options?: LoadConfigOptions): ButlerConfig;
|
|
13
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAMD,MAAM,WAAW,iBAAiB;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAkBD,wBAAgB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,YAAY,CA6BxE"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config loader: args (--driveRootId, --spreadsheetId, --credentialsPath) + optional --config file + env
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
5
|
+
import { resolve } from "node:path";
|
|
6
|
+
import yaml from "js-yaml";
|
|
7
|
+
const ENV_SPREADSHEET_ID = "BUTLER_SPREADSHEET_ID";
|
|
8
|
+
const ENV_DRIVE_ROOT_ID = "BUTLER_DRIVE_ROOT_ID";
|
|
9
|
+
const ENV_CREDENTIALS = "GOOGLE_APPLICATION_CREDENTIALS";
|
|
10
|
+
function loadFromFile(path) {
|
|
11
|
+
const abs = resolve(path);
|
|
12
|
+
if (!existsSync(abs)) {
|
|
13
|
+
throw new Error(`Config file not found: ${path}`);
|
|
14
|
+
}
|
|
15
|
+
const raw = readFileSync(abs, "utf-8");
|
|
16
|
+
const ext = abs.split(".").pop()?.toLowerCase();
|
|
17
|
+
if (ext === "yaml" || ext === "yml") {
|
|
18
|
+
return yaml.load(raw) ?? {};
|
|
19
|
+
}
|
|
20
|
+
if (ext === "json") {
|
|
21
|
+
return JSON.parse(raw);
|
|
22
|
+
}
|
|
23
|
+
throw new Error(`Unsupported config format: ${ext}. Use .json or .yaml`);
|
|
24
|
+
}
|
|
25
|
+
export function loadConfig(options = {}) {
|
|
26
|
+
const fromFile = options.configFilePath
|
|
27
|
+
? loadFromFile(options.configFilePath)
|
|
28
|
+
: {};
|
|
29
|
+
const driveRootId = options.driveRootId ??
|
|
30
|
+
fromFile.driveRootId ??
|
|
31
|
+
process.env[ENV_DRIVE_ROOT_ID];
|
|
32
|
+
const spreadsheetId = options.spreadsheetId ??
|
|
33
|
+
fromFile.spreadsheetId ??
|
|
34
|
+
process.env[ENV_SPREADSHEET_ID];
|
|
35
|
+
const credentialsPath = options.credentialsPath ??
|
|
36
|
+
fromFile.credentialsPath ??
|
|
37
|
+
process.env[ENV_CREDENTIALS];
|
|
38
|
+
if (!driveRootId) {
|
|
39
|
+
throw new Error(`Missing required: driveRootId. Set --driveRootId in args, or ${ENV_DRIVE_ROOT_ID}, or config file.`);
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
driveRootId,
|
|
43
|
+
spreadsheetId: spreadsheetId ?? undefined,
|
|
44
|
+
credentialsPath: credentialsPath ?? undefined,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,IAAI,MAAM,SAAS,CAAC;AAQ3B,MAAM,kBAAkB,GAAG,uBAAuB,CAAC;AACnD,MAAM,iBAAiB,GAAG,sBAAsB,CAAC;AACjD,MAAM,eAAe,GAAG,gCAAgC,CAAC;AASzD,SAAS,YAAY,CAAC,IAAY;IAChC,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,0BAA0B,IAAI,EAAE,CAAC,CAAC;IACpD,CAAC;IACD,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACvC,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,WAAW,EAAE,CAAC;IAChD,IAAI,GAAG,KAAK,MAAM,IAAI,GAAG,KAAK,KAAK,EAAE,CAAC;QACpC,OAAQ,IAAI,CAAC,IAAI,CAAC,GAAG,CAA2B,IAAI,EAAE,CAAC;IACzD,CAAC;IACD,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;QACnB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAA0B,CAAC;IAClD,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,8BAA8B,GAAG,sBAAsB,CAAC,CAAC;AAC3E,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,UAA6B,EAAE;IACxD,MAAM,QAAQ,GAAG,OAAO,CAAC,cAAc;QACrC,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,cAAc,CAAC;QACtC,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,WAAW,GACf,OAAO,CAAC,WAAW;QACnB,QAAQ,CAAC,WAAW;QACpB,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACjC,MAAM,aAAa,GACjB,OAAO,CAAC,aAAa;QACrB,QAAQ,CAAC,aAAa;QACtB,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IAClC,MAAM,eAAe,GACnB,OAAO,CAAC,eAAe;QACvB,QAAQ,CAAC,eAAe;QACxB,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAE/B,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CACb,gEAAgE,iBAAiB,mBAAmB,CACrG,CAAC;IACJ,CAAC;IAED,OAAO;QACL,WAAW;QACX,aAAa,EAAE,aAAa,IAAI,SAAS;QACzC,eAAe,EAAE,eAAe,IAAI,SAAS;KAC9C,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scope Guard: ensure Drive write targets are under Butler root only
|
|
3
|
+
*/
|
|
4
|
+
export declare class ScopeError extends Error {
|
|
5
|
+
constructor(message: string);
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Asserts that the given path or fileId is under the Butler root.
|
|
9
|
+
* - Path: relative path only, no "..", no leading slash.
|
|
10
|
+
* - fileId: allowed if it equals rootFolderId (same folder).
|
|
11
|
+
* @throws ScopeError if path escapes or is invalid
|
|
12
|
+
*/
|
|
13
|
+
export declare function assertPathUnderButler(pathOrFileId: string, rootFolderId: string): void;
|
|
14
|
+
//# sourceMappingURL=scope-guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope-guard.d.ts","sourceRoot":"","sources":["../src/scope-guard.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,UAAW,SAAQ,KAAK;gBACvB,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,GACnB,IAAI,CAoBN"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scope Guard: ensure Drive write targets are under Butler root only
|
|
3
|
+
*/
|
|
4
|
+
export class ScopeError extends Error {
|
|
5
|
+
constructor(message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "ScopeError";
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Asserts that the given path or fileId is under the Butler root.
|
|
12
|
+
* - Path: relative path only, no "..", no leading slash.
|
|
13
|
+
* - fileId: allowed if it equals rootFolderId (same folder).
|
|
14
|
+
* @throws ScopeError if path escapes or is invalid
|
|
15
|
+
*/
|
|
16
|
+
export function assertPathUnderButler(pathOrFileId, rootFolderId) {
|
|
17
|
+
if (!pathOrFileId || !pathOrFileId.trim()) {
|
|
18
|
+
throw new ScopeError("Path or fileId must not be empty (Butler scope).");
|
|
19
|
+
}
|
|
20
|
+
// fileId: allow only if it's the root folder itself
|
|
21
|
+
if (pathOrFileId === rootFolderId) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
// Path: must be relative, no ".."
|
|
25
|
+
const normalized = pathOrFileId.replace(/\\/g, "/").trim();
|
|
26
|
+
if (normalized.startsWith("/")) {
|
|
27
|
+
throw new ScopeError("指定されたパスは Butler 配下ではありません(絶対パスは不可)。");
|
|
28
|
+
}
|
|
29
|
+
if (normalized.includes("..")) {
|
|
30
|
+
throw new ScopeError("指定されたパスは Butler 配下ではありません(.. による上位参照は不可)。");
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=scope-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope-guard.js","sourceRoot":"","sources":["../src/scope-guard.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,OAAO,UAAW,SAAQ,KAAK;IACnC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;IAC3B,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CACnC,YAAoB,EACpB,YAAoB;IAEpB,IAAI,CAAC,YAAY,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,CAAC;QAC1C,MAAM,IAAI,UAAU,CAAC,kDAAkD,CAAC,CAAC;IAC3E,CAAC;IACD,oDAAoD;IACpD,IAAI,YAAY,KAAK,YAAY,EAAE,CAAC;QAClC,OAAO;IACT,CAAC;IACD,kCAAkC;IAClC,MAAM,UAAU,GAAG,YAAY,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAC3D,IAAI,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,UAAU,CAClB,qCAAqC,CACtC,CAAC;IACJ,CAAC;IACD,IAAI,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,UAAU,CAClB,2CAA2C,CAC5C,CAAC;IACJ,CAAC;AACH,CAAC"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAShD,wBAAsB,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAsCnE"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Server: stdio transport + tool registration
|
|
3
|
+
*/
|
|
4
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
import { getAuthClients } from "./auth.js";
|
|
7
|
+
import { registerSheetsTools } from "./tools/sheets.js";
|
|
8
|
+
import { registerDriveTools } from "./tools/drive.js";
|
|
9
|
+
import { registerConfigTools } from "./tools/config.js";
|
|
10
|
+
import * as auditService from "./services/audit.js";
|
|
11
|
+
import { findOrCreateTaskSpreadsheet } from "./services/sheets.js";
|
|
12
|
+
export async function runServer(config) {
|
|
13
|
+
const server = new McpServer({ name: "butler-mcp", version: "0.1.0" }, {});
|
|
14
|
+
let clientsCache = null;
|
|
15
|
+
const getClients = () => {
|
|
16
|
+
if (!clientsCache)
|
|
17
|
+
clientsCache = getAuthClients(config);
|
|
18
|
+
return clientsCache;
|
|
19
|
+
};
|
|
20
|
+
if (!config.spreadsheetId) {
|
|
21
|
+
const clients = await getClients();
|
|
22
|
+
config.spreadsheetId = await findOrCreateTaskSpreadsheet(clients, config.driveRootId);
|
|
23
|
+
}
|
|
24
|
+
const auditAppend = async (tool, target, diffSummary, success = true) => {
|
|
25
|
+
try {
|
|
26
|
+
const clients = await getClients();
|
|
27
|
+
await auditService.append(clients, config.driveRootId, tool, target, diffSummary, success);
|
|
28
|
+
}
|
|
29
|
+
catch (_) {
|
|
30
|
+
// audit failure should not break the tool response
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
registerSheetsTools(server, config, getClients, auditAppend);
|
|
34
|
+
registerDriveTools(server, config, getClients, auditAppend);
|
|
35
|
+
registerConfigTools(server, config, getClients, auditAppend);
|
|
36
|
+
const transport = new StdioServerTransport();
|
|
37
|
+
await server.connect(transport);
|
|
38
|
+
await new Promise(() => { });
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAGjF,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,KAAK,YAAY,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,2BAA2B,EAAE,MAAM,sBAAsB,CAAC;AAEnE,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,MAAoB;IAClD,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,EACxC,EAAE,CACH,CAAC;IAEF,IAAI,YAAY,GAAgC,IAAI,CAAC;IACrD,MAAM,UAAU,GAAG,GAAyB,EAAE;QAC5C,IAAI,CAAC,YAAY;YAAE,YAAY,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;QACzD,OAAO,YAAY,CAAC;IACtB,CAAC,CAAC;IAEF,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC;QAC1B,MAAM,OAAO,GAAG,MAAM,UAAU,EAAE,CAAC;QACnC,MAAM,CAAC,aAAa,GAAG,MAAM,2BAA2B,CAAC,OAAO,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;IACxF,CAAC;IAED,MAAM,WAAW,GAAG,KAAK,EACvB,IAAY,EACZ,MAAc,EACd,WAAoB,EACpB,OAAO,GAAG,IAAI,EACd,EAAE;QACF,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,UAAU,EAAE,CAAC;YACnC,MAAM,YAAY,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;QAC7F,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,mDAAmD;QACrD,CAAC;IACH,CAAC,CAAC;IAEF,mBAAmB,CAAC,MAAe,EAAE,MAAM,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;IACtE,kBAAkB,CAAC,MAAe,EAAE,MAAM,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;IACrE,mBAAmB,CAAC,MAAe,EAAE,MAAM,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;IAEtE,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,MAAM,IAAI,OAAO,CAAO,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;AACpC,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit Service: append one line to Butler/history/audit-log.ndjson
|
|
3
|
+
*/
|
|
4
|
+
import type { AuthClients } from "../auth.js";
|
|
5
|
+
export interface AuditEntry {
|
|
6
|
+
timestamp: string;
|
|
7
|
+
tool: string;
|
|
8
|
+
target: string;
|
|
9
|
+
diffSummary?: string;
|
|
10
|
+
success: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare function append(clients: AuthClients, driveRootId: string, tool: string, target: string, diffSummary: string | undefined, success: boolean): Promise<void>;
|
|
13
|
+
//# sourceMappingURL=audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/services/audit.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAK9C,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;CAClB;AAMD,wBAAsB,MAAM,CAC1B,OAAO,EAAE,WAAW,EACpB,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,IAAI,CAAC,CAef"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { writeFile } from "./drive.js";
|
|
2
|
+
const AUDIT_LOG_PATH = "history/audit-log.ndjson";
|
|
3
|
+
function formatEntry(entry) {
|
|
4
|
+
return JSON.stringify(entry) + "\n";
|
|
5
|
+
}
|
|
6
|
+
export async function append(clients, driveRootId, tool, target, diffSummary, success) {
|
|
7
|
+
const entry = {
|
|
8
|
+
timestamp: new Date().toISOString(),
|
|
9
|
+
tool,
|
|
10
|
+
target,
|
|
11
|
+
diffSummary,
|
|
12
|
+
success,
|
|
13
|
+
};
|
|
14
|
+
await writeFile(clients, driveRootId, AUDIT_LOG_PATH, formatEntry(entry), "append");
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/services/audit.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,MAAM,cAAc,GAAG,0BAA0B,CAAC;AAUlD,SAAS,WAAW,CAAC,KAAiB;IACpC,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;AACtC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,MAAM,CAC1B,OAAoB,EACpB,WAAmB,EACnB,IAAY,EACZ,MAAc,EACd,WAA+B,EAC/B,OAAgB;IAEhB,MAAM,KAAK,GAAe;QACxB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,IAAI;QACJ,MAAM;QACN,WAAW;QACX,OAAO;KACR,CAAC;IACF,MAAM,SAAS,CACb,OAAO,EACP,WAAW,EACX,cAAc,EACd,WAAW,CAAC,KAAK,CAAC,EAClB,QAAQ,CACT,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config Service: getConfigBundle (persona, policies, templates from Drive)
|
|
3
|
+
*/
|
|
4
|
+
import type { AuthClients } from "../auth.js";
|
|
5
|
+
export interface ConfigBundle {
|
|
6
|
+
persona: Record<string, unknown> & {
|
|
7
|
+
name?: string;
|
|
8
|
+
};
|
|
9
|
+
policies: Record<string, unknown>;
|
|
10
|
+
templates: {
|
|
11
|
+
morning: string;
|
|
12
|
+
next: string;
|
|
13
|
+
review: string;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export declare function getConfigBundle(clients: AuthClients, driveRootId: string): Promise<ConfigBundle>;
|
|
17
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/services/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAI9C,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACrD,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,SAAS,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9D;AAED,wBAAsB,eAAe,CACnC,OAAO,EAAE,WAAW,EACpB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,YAAY,CAAC,CAgBvB"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { readFileByPath } from "./drive.js";
|
|
2
|
+
import yaml from "js-yaml";
|
|
3
|
+
export async function getConfigBundle(clients, driveRootId) {
|
|
4
|
+
const read = (path) => readFileByPath(clients, driveRootId, path);
|
|
5
|
+
const [personaRaw, policiesRaw, morning, next, review] = await Promise.all([
|
|
6
|
+
read("config/persona.yml"),
|
|
7
|
+
read("config/policies.yml"),
|
|
8
|
+
read("config/templates/morning.md"),
|
|
9
|
+
read("config/templates/next.md"),
|
|
10
|
+
read("config/templates/review.md"),
|
|
11
|
+
]);
|
|
12
|
+
const persona = (personaRaw ? yaml.load(personaRaw) : {});
|
|
13
|
+
const policies = (policiesRaw ? yaml.load(policiesRaw) : {});
|
|
14
|
+
return {
|
|
15
|
+
persona: persona ?? {},
|
|
16
|
+
policies: policies ?? {},
|
|
17
|
+
templates: { morning: morning ?? "", next: next ?? "", review: review ?? "" },
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/services/config.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,IAAI,MAAM,SAAS,CAAC;AAQ3B,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,OAAoB,EACpB,WAAmB;IAEnB,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,cAAc,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;IAC1E,MAAM,CAAC,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACzE,IAAI,CAAC,oBAAoB,CAAC;QAC1B,IAAI,CAAC,qBAAqB,CAAC;QAC3B,IAAI,CAAC,6BAA6B,CAAC;QACnC,IAAI,CAAC,0BAA0B,CAAC;QAChC,IAAI,CAAC,4BAA4B,CAAC;KACnC,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,CAAC,UAAU,CAAC,CAAC,CAAE,IAAI,CAAC,IAAI,CAAC,UAAU,CAA6B,CAAC,CAAC,CAAC,EAAE,CAA4B,CAAC;IAClH,MAAM,QAAQ,GAAG,CAAC,WAAW,CAAC,CAAC,CAAE,IAAI,CAAC,IAAI,CAAC,WAAW,CAA6B,CAAC,CAAC,CAAC,EAAE,CAA6B,CAAC;IACtH,OAAO;QACL,OAAO,EAAE,OAAO,IAAI,EAAE;QACtB,QAAQ,EAAE,QAAQ,IAAI,EAAE;QACxB,SAAS,EAAE,EAAE,OAAO,EAAE,OAAO,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,IAAI,EAAE,EAAE;KAC9E,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drive Service: search, readFile, writeFile (scope guard enforced)
|
|
3
|
+
*/
|
|
4
|
+
import type { AuthClients } from "../auth.js";
|
|
5
|
+
export declare function search(clients: AuthClients, rootFolderId: string, query?: string): Promise<{
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
}[]>;
|
|
9
|
+
export declare function readFile(clients: AuthClients, fileId: string): Promise<string>;
|
|
10
|
+
/** Read file by path under root (e.g. "config/persona.yml"). Returns empty string if not found. */
|
|
11
|
+
export declare function readFileByPath(clients: AuthClients, rootFolderId: string, path: string): Promise<string>;
|
|
12
|
+
export type WriteMode = "create" | "replace" | "append";
|
|
13
|
+
export declare function writeFile(clients: AuthClients, rootFolderId: string, path: string, content: string, mode: WriteMode): Promise<void>;
|
|
14
|
+
//# sourceMappingURL=drive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drive.d.ts","sourceRoot":"","sources":["../../src/services/drive.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAgB9C,wBAAsB,MAAM,CAC1B,OAAO,EAAE,WAAW,EACpB,YAAY,EAAE,MAAM,EACpB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,CAUzC;AAED,wBAAsB,QAAQ,CAAC,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQpF;AAED,mGAAmG;AACnG,wBAAsB,cAAc,CAClC,OAAO,EAAE,WAAW,EACpB,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,CAAC,CAKjB;AA6CD,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;AAExD,wBAAsB,SAAS,CAC7B,OAAO,EAAE,WAAW,EACpB,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,SAAS,GACd,OAAO,CAAC,IAAI,CAAC,CAwBf"}
|