bizgate-mcp-server 0.4.1 → 0.7.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 +30 -99
- package/dist/admin/cli.d.ts +2 -0
- package/dist/admin/cli.js +70 -0
- package/dist/admin/common.d.ts +14 -0
- package/dist/admin/common.js +54 -0
- package/dist/admin/issue-token.d.ts +1 -0
- package/dist/admin/issue-token.js +82 -0
- package/dist/admin/list-users.d.ts +1 -0
- package/dist/admin/list-users.js +61 -0
- package/dist/admin/reissue-token.d.ts +1 -0
- package/dist/admin/reissue-token.js +43 -0
- package/dist/admin/revoke-token.d.ts +1 -0
- package/dist/admin/revoke-token.js +37 -0
- package/dist/admin/update-limit.d.ts +1 -0
- package/dist/admin/update-limit.js +50 -0
- package/dist/admin-ui/page.html +480 -0
- package/dist/admin-ui/router.d.ts +2 -0
- package/dist/admin-ui/router.js +293 -0
- package/dist/auth/token.d.ts +11 -0
- package/dist/auth/token.js +18 -0
- package/dist/auth/user-store.d.ts +50 -0
- package/dist/auth/user-store.js +130 -0
- package/dist/bizgate-client.d.ts +1 -1
- package/dist/bizgate-client.js +9 -9
- package/dist/cloud/neon-client.d.ts +4 -10
- package/dist/cloud/neon-client.js +11 -22
- package/dist/enrich/core.d.ts +90 -0
- package/dist/enrich/core.js +444 -0
- package/dist/enrich/tools.d.ts +3 -0
- package/dist/enrich/tools.js +255 -0
- package/dist/enrich/variants.d.ts +24 -0
- package/dist/enrich/variants.js +88 -0
- package/dist/http-server.d.ts +14 -0
- package/dist/http-server.js +218 -0
- package/dist/index.d.ts +15 -4
- package/dist/index.js +404 -1024
- package/dist/install-skill.js +41 -228
- package/dist/shared/logger.js +0 -1
- package/dist/usage-tracker.d.ts +40 -15
- package/dist/usage-tracker.js +105 -31
- package/package.json +14 -10
- package/skills/bizgate/SKILL.md +142 -0
- package/dist/jpx-cache.d.ts +0 -36
- package/dist/jpx-cache.js +0 -230
- package/dist/papatto/credentials.d.ts +0 -12
- package/dist/papatto/credentials.js +0 -40
- package/dist/papatto/csv-parser.d.ts +0 -9
- package/dist/papatto/csv-parser.js +0 -134
- package/dist/papatto/friendly-errors.d.ts +0 -8
- package/dist/papatto/friendly-errors.js +0 -103
- package/dist/papatto/master-extractor.d.ts +0 -51
- package/dist/papatto/master-extractor.js +0 -295
- package/dist/papatto/page-extract.d.ts +0 -55
- package/dist/papatto/page-extract.js +0 -324
- package/dist/papatto/pipeline.d.ts +0 -67
- package/dist/papatto/pipeline.js +0 -295
- package/dist/papatto/playwright-session.d.ts +0 -11
- package/dist/papatto/playwright-session.js +0 -85
- package/dist/papatto/search.d.ts +0 -72
- package/dist/papatto/search.js +0 -512
- package/dist/papatto/tools.d.ts +0 -2
- package/dist/papatto/tools.js +0 -770
- package/dist/prospect-history.d.ts +0 -63
- package/dist/prospect-history.js +0 -155
- package/dist/queue/playwright-queue.d.ts +0 -9
- package/dist/queue/playwright-queue.js +0 -23
- package/dist/seed-cache.d.ts +0 -33
- package/dist/seed-cache.js +0 -169
- package/dist/sheets/client.d.ts +0 -15
- package/dist/sheets/client.js +0 -128
package/README.md
CHANGED
|
@@ -120,36 +120,46 @@ claude
|
|
|
120
120
|
| Excelに入力したい | 「○○の情報を調べてExcelに入れて」 |
|
|
121
121
|
| DigiManのサービスに合う部署を探したい | 「○○にDigiManのサービスが売れそうな部署を探して」 |
|
|
122
122
|
|
|
123
|
-
###
|
|
123
|
+
### /bizgate ルーター(単一入口)
|
|
124
124
|
|
|
125
|
-
|
|
125
|
+
BizGate の機能をすべてまとめた **入口スキル** です。どの機能を使えばいいか迷ったら、まず `/bizgate` と打つだけ。メニューが表示され、選ぶだけで進めます。
|
|
126
126
|
|
|
127
127
|
```
|
|
128
|
-
/
|
|
128
|
+
/bizgate
|
|
129
129
|
```
|
|
130
130
|
|
|
131
|
-
|
|
131
|
+
または自然言語で:
|
|
132
132
|
```
|
|
133
|
-
|
|
133
|
+
「BizGate 使いたい」
|
|
134
|
+
「営業リストを補強したい」
|
|
134
135
|
```
|
|
135
136
|
|
|
136
|
-
|
|
137
|
-
- サービスごとの推薦部署(最大5件ずつ)+ 電話番号
|
|
138
|
-
- マッチ理由
|
|
139
|
-
- 最優先アプローチ先
|
|
137
|
+
**メニュー (3 つから選択):**
|
|
140
138
|
|
|
141
|
-
|
|
139
|
+
1. **会社リストを補強する (enrich)** — 既にある会社名リストに、代表者・売上・業種・部署・キーマン情報を追加
|
|
140
|
+
2. **1 社を詳しく調べる** — 特定の 1 社の基本情報 + 部署 + キーマンを一括取得
|
|
141
|
+
3. **今日の残り利用回数を確認する**
|
|
142
|
+
|
|
143
|
+
**会社名は厳密でなくて OK:**
|
|
144
|
+
|
|
145
|
+
- `トヨタ` → 自動で `株式会社トヨタ` / `トヨタ株式会社` も試します
|
|
146
|
+
- `toyota` → 自動で `トヨタ` も試します
|
|
147
|
+
- `SONY` (全角) → 半角に整えて試します
|
|
148
|
+
|
|
149
|
+
確証マッチが取れなかった社は CSV に「未マッチ」または「別会社候補」と理由が記録されます。
|
|
150
|
+
|
|
151
|
+
**インストール:**
|
|
152
|
+
|
|
153
|
+
`bizgate-mcp-server` を npx で実行すると同梱されます。すでに入っている方は以下でスキルだけ追加:
|
|
142
154
|
|
|
143
155
|
```bash
|
|
144
|
-
|
|
156
|
+
npx bizgate-mcp-server --install-skill
|
|
145
157
|
```
|
|
146
158
|
|
|
147
|
-
新規インストールの場合は `install.sh` にスキルも含まれています。
|
|
148
|
-
|
|
149
159
|
### 注意点
|
|
150
160
|
|
|
151
|
-
-
|
|
152
|
-
- 1
|
|
161
|
+
- **会社名は厳密でなくて OK** です。`トヨタ` / `toyota` / `SONY` のような表記揺れも自動で複数パターン試します。それでも見つからない場合は正式名称 (例: `ソフトバンク株式会社`) を試してください
|
|
162
|
+
- 1 日に使える回数は管理者が設定します(デフォルト **1000 回**。残り回数は `今日の残り利用回数を教えて` で確認できます)
|
|
153
163
|
- 企業検索は、結果が見つからなくても **1回分消費** されます
|
|
154
164
|
- 部署・マーケ・キーマン検索は、データが見つからなかった場合は **消費されません**
|
|
155
165
|
- 担当者検索は、**1名あたり1回分消費** されます(デフォルト上位10名)
|
|
@@ -199,7 +209,8 @@ bash install-skill.sh
|
|
|
199
209
|
| `BIZGATE_SKEY_MARKETING` | | マーケティングタグAPIのサービスキー |
|
|
200
210
|
| `BIZGATE_SKEY_KEYMAN` | | キーマン(人名なし)APIのサービスキー |
|
|
201
211
|
| `BIZGATE_SKEY_KEYMAN_NAME` | | キーマン(人名あり)APIのサービスキー |
|
|
202
|
-
| `BIZGATE_DAILY_LIMIT` | | 1日のAPI上限(デフォルト:
|
|
212
|
+
| `BIZGATE_DAILY_LIMIT` | | 1日のAPI上限(デフォルト: 1000) |
|
|
213
|
+
| `BIZGATE_ENRICH_CONCURRENCY` | | enrich の同時実行数(デフォルト: 5、範囲 1〜20) |
|
|
203
214
|
|
|
204
215
|
---
|
|
205
216
|
|
|
@@ -225,93 +236,13 @@ bash install-skill.sh
|
|
|
225
236
|
|
|
226
237
|
### 「該当する企業が見つかりませんでした」と表示される
|
|
227
238
|
|
|
228
|
-
|
|
229
|
-
- 悪い例:「トヨタ」
|
|
230
|
-
- 良い例:「トヨタ自動車株式会社」
|
|
239
|
+
サーバーが自動で `株式会社○○` / `○○株式会社` / カタカナ↔英文 など複数パターンを試しても見つからなかった場合に表示されます。
|
|
231
240
|
|
|
232
|
-
|
|
241
|
+
- 法人番号 (13 桁) がわかる場合はそれを添えて検索してください
|
|
242
|
+
- それでも見つからないときは BizGate DB に未登録の可能性があります
|
|
233
243
|
|
|
234
244
|
### 「複数の企業が存在します」と表示される
|
|
235
245
|
|
|
236
246
|
検索条件が曖昧で、複数の企業がヒットしています。
|
|
237
247
|
会社名をより正確に入力するか、メールアドレスやホームページURLを追加で伝えてください。
|
|
238
248
|
|
|
239
|
-
---
|
|
240
|
-
|
|
241
|
-
## v0.4.0 — Papatto Cloud 連携 (NEW)
|
|
242
|
-
|
|
243
|
-
自然言語の ICP (理想顧客像) を受け取って、Papatto Cloud から自動で企業リストを抽出し、CSV → R2 → Neon → Google Sheets まで一気通貫で実行します。
|
|
244
|
-
|
|
245
|
-
```
|
|
246
|
-
[Slack DM / Claude Code]
|
|
247
|
-
"DOMO 営業 IT, 売上 50億以上, 展示会出展した 100社"
|
|
248
|
-
↓
|
|
249
|
-
[papatto-prospect スキル]
|
|
250
|
-
↓ master_summary 参照 → 条件マッピング
|
|
251
|
-
[papatto__extract]
|
|
252
|
-
↓ Playwright 自動ログイン + 検索 + CSV ダウンロード
|
|
253
|
-
[R2 papatto-bizgate-logs] ← CSV 保存
|
|
254
|
-
[Neon papatto.campaigns] ← メタ記録
|
|
255
|
-
↓
|
|
256
|
-
[papatto__export_to_sheets]
|
|
257
|
-
↓
|
|
258
|
-
[Google Sheets] ← 行データ書き込み
|
|
259
|
-
↓
|
|
260
|
-
返答: シート URL + campaign_id
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
### 追加 MCP ツール (7 個)
|
|
264
|
-
|
|
265
|
-
| ツール | 用途 |
|
|
266
|
-
|--------|------|
|
|
267
|
-
| `papatto__credential_set` | Papatto 認証情報を OS Keychain に保存 + Neon users へ upsert |
|
|
268
|
-
| `papatto__master_refresh` | 検索画面の選択肢 (業種 / マーケタグ / インテント) をマスタ化 |
|
|
269
|
-
| `papatto__master_summary` | LLM が自然言語マッピングするための分類別オプション JSON |
|
|
270
|
-
| `papatto__extract` | 構造化 conditions → 検索 → 件数確認 → CSV → R2 → Neon |
|
|
271
|
-
| `papatto__export_to_sheets` | CSV → 新規 / 既存スプレッドシート + タブ |
|
|
272
|
-
| `papatto__campaign_status` | キャンペーン状態照会 (単件 / オーナー別リスト) |
|
|
273
|
-
| `papatto__queue_status` | Playwright 並列実行キューの状態 |
|
|
274
|
-
|
|
275
|
-
### 追加スキル
|
|
276
|
-
|
|
277
|
-
- **`papatto-prospect`** — 自然言語 ICP → conditions → extract → sheets の対話フロー
|
|
278
|
-
|
|
279
|
-
### 環境変数 (Papatto 部分)
|
|
280
|
-
|
|
281
|
-
`.env` (リポジトリルート) に以下を追加:
|
|
282
|
-
|
|
283
|
-
```bash
|
|
284
|
-
# Cloudflare R2 (papatto-bizgate-logs)
|
|
285
|
-
R2_ACCESS_KEY_ID=...
|
|
286
|
-
R2_SECRET_ACCESS_KEY=...
|
|
287
|
-
R2_BUCKET=papatto-bizgate-logs
|
|
288
|
-
R2_ENDPOINT=https://<account_id>.r2.cloudflarestorage.com
|
|
289
|
-
|
|
290
|
-
# Neon (digiman-internal-db / papatto_mcp role)
|
|
291
|
-
NEON_DATABASE_URL=postgresql://papatto_mcp:<pw>@<host>/neondb?sslmode=require
|
|
292
|
-
|
|
293
|
-
# Google Sheets (サービスアカウント)
|
|
294
|
-
GOOGLE_SHEETS_CREDENTIALS_PATH=/Users/<you>/.papatto-bizgate/google_credentials.json
|
|
295
|
-
GOOGLE_SHEETS_DEFAULT_FOLDER_ID=<Drive folder id>
|
|
296
|
-
|
|
297
|
-
# Playwright (任意)
|
|
298
|
-
PAPATTO_PLAYWRIGHT_CONCURRENCY=2
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
> Papatto 認証情報はサーバ起動時の `.env` には入れず、`papatto__credential_set` 経由で OS Keychain (keytar) に保存されます。
|
|
302
|
-
|
|
303
|
-
### セットアップ
|
|
304
|
-
|
|
305
|
-
詳細は [`docs/setup-mac-mini.md`](docs/setup-mac-mini.md) を参照。
|
|
306
|
-
ユーザー向け Slack コマンド一覧は [`docs/slack-commands.md`](docs/slack-commands.md)。
|
|
307
|
-
|
|
308
|
-
### Papatto サイト構造のメモ (実装根拠)
|
|
309
|
-
|
|
310
|
-
- ベース URL: `https://www.papatto.info/papatto/papatto.php` — SPA 単一ページ、6 タブ
|
|
311
|
-
- 業種 = `chk_gyoshu[]` 18 大分類 + 個別 input 93 中分類
|
|
312
|
-
- マーケ / インテント / 活動 / トレンド / 分野タグ = `.kw_group a[data-query]` アンカー (`<a onclick>` ではない)
|
|
313
|
-
- 検索ボタン = `Papatto検索`
|
|
314
|
-
- CSV / Excel ボタン = `<span>:has-text("CSVダウンロード")`
|
|
315
|
-
- 件数 = `N件 ( 会社数 M 社 )` 正規表現で抽出
|
|
316
|
-
- 都道府県 / 売上 / 従業員レンジは検索フォームに無く、結果ページ facet drill または事前定義タグ (`2146_売上50億以上` 等) で代用
|
|
317
|
-
- ダウンロード月間上限 (例: 4,000 社) を結果ページから自動感知
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// bizgate-admin <subcommand> [options]
|
|
3
|
+
import "../shared/env.js";
|
|
4
|
+
function printHelp() {
|
|
5
|
+
console.log(`
|
|
6
|
+
使い方:
|
|
7
|
+
bizgate-admin <command> [options]
|
|
8
|
+
|
|
9
|
+
コマンド:
|
|
10
|
+
issue 新規ユーザー発行 + Slack DM テンプレート出力
|
|
11
|
+
reissue トークン再発行(旧トークンは即時無効)
|
|
12
|
+
update-limit 日次上限変更(プール上限を再検証)
|
|
13
|
+
revoke 無効化(退職・流出時)
|
|
14
|
+
list ユーザー一覧 + 本日使用量
|
|
15
|
+
|
|
16
|
+
各コマンドの詳細ヘルプ:
|
|
17
|
+
bizgate-admin <command> --help
|
|
18
|
+
|
|
19
|
+
例:
|
|
20
|
+
bizgate-admin issue --name "金営業" --limit 200
|
|
21
|
+
bizgate-admin list
|
|
22
|
+
bizgate-admin update-limit --user "金営業" --limit 500
|
|
23
|
+
bizgate-admin revoke --user "金営業"
|
|
24
|
+
bizgate-admin reissue --user "金営業"
|
|
25
|
+
`);
|
|
26
|
+
}
|
|
27
|
+
async function main() {
|
|
28
|
+
const sub = process.argv[2];
|
|
29
|
+
if (!sub || sub === "--help" || sub === "-h" || sub === "help") {
|
|
30
|
+
printHelp();
|
|
31
|
+
process.exit(sub ? 0 : 1);
|
|
32
|
+
}
|
|
33
|
+
// サブコマンドを argv から取り除き、各モジュールの parseArgs が正しく動くようにする
|
|
34
|
+
process.argv.splice(2, 1);
|
|
35
|
+
switch (sub) {
|
|
36
|
+
case "issue": {
|
|
37
|
+
const { runIssueToken } = await import("./issue-token.js");
|
|
38
|
+
await runIssueToken();
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
case "reissue": {
|
|
42
|
+
const { runReissueToken } = await import("./reissue-token.js");
|
|
43
|
+
await runReissueToken();
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
case "update-limit": {
|
|
47
|
+
const { runUpdateLimit } = await import("./update-limit.js");
|
|
48
|
+
await runUpdateLimit();
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
case "revoke": {
|
|
52
|
+
const { runRevokeToken } = await import("./revoke-token.js");
|
|
53
|
+
await runRevokeToken();
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
case "list": {
|
|
57
|
+
const { runListUsers } = await import("./list-users.js");
|
|
58
|
+
await runListUsers();
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
default:
|
|
62
|
+
console.error(`❌ 未知のサブコマンド: "${sub}"`);
|
|
63
|
+
printHelp();
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
main().catch((e) => {
|
|
68
|
+
console.error("❌ エラー:", e);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import "../shared/env.js";
|
|
2
|
+
export declare function requireNeon(): void;
|
|
3
|
+
export declare function getPoolLimit(): number;
|
|
4
|
+
export declare function getMcpUrl(): string;
|
|
5
|
+
/** macOS の pbcopy にテキストをコピー(best-effort、失敗しても無視)。 */
|
|
6
|
+
export declare function copyToClipboard(text: string): boolean;
|
|
7
|
+
/** Slack DM 用テンプレート(トークン受け取り側の手順を含む)。 */
|
|
8
|
+
export declare function slackTemplate(args: {
|
|
9
|
+
name: string;
|
|
10
|
+
token: string;
|
|
11
|
+
daily_limit: number;
|
|
12
|
+
mcp_url: string;
|
|
13
|
+
}): string;
|
|
14
|
+
export declare function exitWith(msg: string, code?: number): never;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import "../shared/env.js";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { isConfigured as isNeonConfigured } from "../cloud/neon-client.js";
|
|
4
|
+
export function requireNeon() {
|
|
5
|
+
if (!isNeonConfigured()) {
|
|
6
|
+
console.error("❌ NEON_DATABASE_URL が設定されていません。.env を確認してください。");
|
|
7
|
+
process.exit(1);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export function getPoolLimit() {
|
|
11
|
+
return Number(process.env.BIZGATE_POOL_LIMIT ?? process.env.BIZGATE_DAILY_LIMIT ?? "1000");
|
|
12
|
+
}
|
|
13
|
+
export function getMcpUrl() {
|
|
14
|
+
return process.env.BIZGATE_MCP_URL ?? "https://YOUR-MAC-MINI.ts.net/mcp";
|
|
15
|
+
}
|
|
16
|
+
/** macOS の pbcopy にテキストをコピー(best-effort、失敗しても無視)。 */
|
|
17
|
+
export function copyToClipboard(text) {
|
|
18
|
+
try {
|
|
19
|
+
const r = spawnSync("pbcopy", [], { input: text, encoding: "utf-8" });
|
|
20
|
+
return r.status === 0;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/** Slack DM 用テンプレート(トークン受け取り側の手順を含む)。 */
|
|
27
|
+
export function slackTemplate(args) {
|
|
28
|
+
return [
|
|
29
|
+
`${args.name} さん、BizGate MCP のアクセストークンを発行しました。`,
|
|
30
|
+
``,
|
|
31
|
+
`■ お渡しするトークン(1日 ${args.daily_limit} 回まで利用可)`,
|
|
32
|
+
`${args.token}`,
|
|
33
|
+
``,
|
|
34
|
+
`■ 設定方法`,
|
|
35
|
+
`Claude Code の MCP 設定(~/.claude.json 等)に下記を追記し、Claude Code を再起動してください。`,
|
|
36
|
+
``,
|
|
37
|
+
"```json",
|
|
38
|
+
`"bizgate-funnel": {`,
|
|
39
|
+
` "url": "${args.mcp_url}",`,
|
|
40
|
+
` "headers": { "Authorization": "Bearer ${args.token}" }`,
|
|
41
|
+
`}`,
|
|
42
|
+
"```",
|
|
43
|
+
``,
|
|
44
|
+
`■ 注意事項`,
|
|
45
|
+
`- このトークンは ${args.name} さん専用です。他の方と共有しないでください。`,
|
|
46
|
+
`- Git リポジトリや共有フォルダにはアップロードしないでください。`,
|
|
47
|
+
`- 紛失・漏洩した場合はすぐに管理者までご連絡ください(再発行します)。`,
|
|
48
|
+
`- 本日の残り利用回数は MCP の "bizgate__usage_status" ツールで確認できます。`,
|
|
49
|
+
].join("\n");
|
|
50
|
+
}
|
|
51
|
+
export function exitWith(msg, code = 1) {
|
|
52
|
+
console.error(msg);
|
|
53
|
+
process.exit(code);
|
|
54
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runIssueToken(): Promise<void>;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { parseArgs } from "node:util";
|
|
2
|
+
import { copyToClipboard, getMcpUrl, getPoolLimit, requireNeon, slackTemplate, } from "./common.js";
|
|
3
|
+
import { createUser, findUserByName, sumActiveDailyLimits, } from "../auth/user-store.js";
|
|
4
|
+
import { closePool } from "../cloud/neon-client.js";
|
|
5
|
+
export async function runIssueToken() {
|
|
6
|
+
requireNeon();
|
|
7
|
+
const { values } = parseArgs({
|
|
8
|
+
options: {
|
|
9
|
+
name: { type: "string" },
|
|
10
|
+
email: { type: "string" },
|
|
11
|
+
limit: { type: "string", default: "200" },
|
|
12
|
+
force: { type: "boolean", default: false },
|
|
13
|
+
help: { type: "boolean", short: "h", default: false },
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
if (values.help || !values.name) {
|
|
17
|
+
console.log(`使い方:
|
|
18
|
+
bizgate-admin issue --name "氏名" [--email mail@x] [--limit 200] [--force]
|
|
19
|
+
|
|
20
|
+
オプション:
|
|
21
|
+
--name 必須。ユーザー名(一意性は強制しないが推奨)。
|
|
22
|
+
--email 任意。重複不可。
|
|
23
|
+
--limit 1日のAPI利用上限(デフォルト: 200)。
|
|
24
|
+
--force プール上限(${getPoolLimit()})超過時も強制発行。
|
|
25
|
+
`);
|
|
26
|
+
process.exit(values.help ? 0 : 1);
|
|
27
|
+
}
|
|
28
|
+
const name = values.name;
|
|
29
|
+
const email = values.email;
|
|
30
|
+
const newLimit = Number(values.limit);
|
|
31
|
+
if (!Number.isFinite(newLimit) || newLimit <= 0) {
|
|
32
|
+
console.error("❌ --limit は正の整数を指定してください。");
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
// 同名 active ユーザーの警告
|
|
36
|
+
const existing = await findUserByName(name);
|
|
37
|
+
if (existing && existing.active) {
|
|
38
|
+
console.error(`❌ 同名のユーザー「${name}」が既に存在します(id=${existing.id})。`);
|
|
39
|
+
console.error(` 再発行する場合は: node dist/admin/reissue-token.js --user "${name}"`);
|
|
40
|
+
console.error(` 別人として登録する場合は --name を変更してください。`);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
// プール上限チェック
|
|
44
|
+
const poolLimit = getPoolLimit();
|
|
45
|
+
const currentSum = await sumActiveDailyLimits();
|
|
46
|
+
const projected = currentSum + newLimit;
|
|
47
|
+
if (projected > poolLimit && !values.force) {
|
|
48
|
+
console.error(`❌ プール上限超過: 現在 ${currentSum} + 新規 ${newLimit} = ${projected} > ${poolLimit}`);
|
|
49
|
+
console.error(` 解決方法:`);
|
|
50
|
+
console.error(` 1) 既存ユーザーの上限を update-limit.js で下げる`);
|
|
51
|
+
console.error(` 2) BIZGATE_POOL_LIMIT を引き上げる(BizGate 本側の上限を要確認)`);
|
|
52
|
+
console.error(` 3) どうしても発行する場合は --force を付ける`);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
const { user, token } = await createUser({
|
|
56
|
+
name,
|
|
57
|
+
email,
|
|
58
|
+
daily_limit: newLimit,
|
|
59
|
+
is_admin: false,
|
|
60
|
+
});
|
|
61
|
+
const mcpUrl = getMcpUrl();
|
|
62
|
+
const template = slackTemplate({
|
|
63
|
+
name,
|
|
64
|
+
token: token.token,
|
|
65
|
+
daily_limit: newLimit,
|
|
66
|
+
mcp_url: mcpUrl,
|
|
67
|
+
});
|
|
68
|
+
const copied = copyToClipboard(template);
|
|
69
|
+
console.log(``);
|
|
70
|
+
console.log(`✅ ユーザー「${name}」のトークンを発行しました(id=${user.id})`);
|
|
71
|
+
console.log(` prefix : ${token.prefix}...`);
|
|
72
|
+
console.log(` limit : ${newLimit} 回/日`);
|
|
73
|
+
console.log(` プール : ${projected}/${poolLimit} (残り ${poolLimit - projected})`);
|
|
74
|
+
console.log(``);
|
|
75
|
+
console.log(`📋 Slack DM 貼付テンプレート${copied ? "(クリップボードにコピー済み)" : ""}:`);
|
|
76
|
+
console.log(`────────────────────────────────────────────────────────────`);
|
|
77
|
+
console.log(template);
|
|
78
|
+
console.log(`────────────────────────────────────────────────────────────`);
|
|
79
|
+
console.log(``);
|
|
80
|
+
console.log(`⚠️ このトークンは再表示できません。Slack DM へお送りください。`);
|
|
81
|
+
await closePool();
|
|
82
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runListUsers(): Promise<void>;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { parseArgs } from "node:util";
|
|
2
|
+
import { getPoolLimit, requireNeon } from "./common.js";
|
|
3
|
+
import { listUsersWithUsage } from "../auth/user-store.js";
|
|
4
|
+
import { todayJST } from "../usage-tracker.js";
|
|
5
|
+
import { closePool } from "../cloud/neon-client.js";
|
|
6
|
+
export async function runListUsers() {
|
|
7
|
+
requireNeon();
|
|
8
|
+
const { values } = parseArgs({
|
|
9
|
+
options: {
|
|
10
|
+
all: { type: "boolean", default: false },
|
|
11
|
+
help: { type: "boolean", short: "h", default: false },
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
if (values.help) {
|
|
15
|
+
console.log(`使い方:
|
|
16
|
+
bizgate-admin list # active ユーザーのみ
|
|
17
|
+
bizgate-admin list --all # revoke 済みも含む
|
|
18
|
+
`);
|
|
19
|
+
process.exit(0);
|
|
20
|
+
}
|
|
21
|
+
const date = todayJST();
|
|
22
|
+
const all = await listUsersWithUsage(date);
|
|
23
|
+
const rows = values.all ? all : all.filter((u) => u.active);
|
|
24
|
+
if (rows.length === 0) {
|
|
25
|
+
console.log("ユーザーが登録されていません。");
|
|
26
|
+
await closePool();
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const fmt = (s, w) => s.padEnd(w);
|
|
30
|
+
const headers = ["ID", "NAME", "LIMIT", "TODAY", "PREFIX", "STATUS", "ADMIN"];
|
|
31
|
+
const widths = [4, 18, 6, 6, 22, 8, 6];
|
|
32
|
+
console.log(`本日(JST: ${date})の利用状況`);
|
|
33
|
+
console.log("─".repeat(80));
|
|
34
|
+
console.log(headers.map((h, i) => fmt(h, widths[i])).join(""));
|
|
35
|
+
console.log("─".repeat(80));
|
|
36
|
+
let activeLimitSum = 0;
|
|
37
|
+
let activeUsedSum = 0;
|
|
38
|
+
for (const u of rows) {
|
|
39
|
+
const status = u.active ? "active" : "revoked";
|
|
40
|
+
const admin = u.is_admin ? "yes" : "";
|
|
41
|
+
const cols = [
|
|
42
|
+
String(u.id),
|
|
43
|
+
u.name,
|
|
44
|
+
String(u.daily_limit),
|
|
45
|
+
String(u.today_count),
|
|
46
|
+
`${u.token_prefix}...`,
|
|
47
|
+
status,
|
|
48
|
+
admin,
|
|
49
|
+
];
|
|
50
|
+
console.log(cols.map((c, i) => fmt(c, widths[i])).join(""));
|
|
51
|
+
if (u.active) {
|
|
52
|
+
activeLimitSum += u.daily_limit;
|
|
53
|
+
activeUsedSum += u.today_count;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const poolLimit = getPoolLimit();
|
|
57
|
+
console.log("─".repeat(80));
|
|
58
|
+
console.log(`合計(active): 上限 ${activeLimitSum}/${poolLimit} ` +
|
|
59
|
+
`(プール余裕: ${poolLimit - activeLimitSum}) / 本日使用 ${activeUsedSum}`);
|
|
60
|
+
await closePool();
|
|
61
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runReissueToken(): Promise<void>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { parseArgs } from "node:util";
|
|
2
|
+
import { copyToClipboard, getMcpUrl, requireNeon, slackTemplate, } from "./common.js";
|
|
3
|
+
import { findUserByName, reissueToken } from "../auth/user-store.js";
|
|
4
|
+
import { closePool } from "../cloud/neon-client.js";
|
|
5
|
+
export async function runReissueToken() {
|
|
6
|
+
requireNeon();
|
|
7
|
+
const { values } = parseArgs({
|
|
8
|
+
options: {
|
|
9
|
+
user: { type: "string" },
|
|
10
|
+
help: { type: "boolean", short: "h", default: false },
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
if (values.help || !values.user) {
|
|
14
|
+
console.log(`使い方:
|
|
15
|
+
bizgate-admin reissue --user "氏名"
|
|
16
|
+
|
|
17
|
+
指定ユーザーのトークンを再発行します(紛失時など)。旧トークンは即時無効化。
|
|
18
|
+
`);
|
|
19
|
+
process.exit(values.help ? 0 : 1);
|
|
20
|
+
}
|
|
21
|
+
const user = await findUserByName(values.user);
|
|
22
|
+
if (!user) {
|
|
23
|
+
console.error(`❌ ユーザー「${values.user}」が見つかりません。`);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
const newToken = await reissueToken(user.id);
|
|
27
|
+
const template = slackTemplate({
|
|
28
|
+
name: user.name,
|
|
29
|
+
token: newToken.token,
|
|
30
|
+
daily_limit: user.daily_limit,
|
|
31
|
+
mcp_url: getMcpUrl(),
|
|
32
|
+
});
|
|
33
|
+
const copied = copyToClipboard(template);
|
|
34
|
+
console.log(`✅ ${user.name} さんのトークンを再発行しました。`);
|
|
35
|
+
console.log(` 旧 prefix: (無効化済み)`);
|
|
36
|
+
console.log(` 新 prefix: ${newToken.prefix}...`);
|
|
37
|
+
console.log(``);
|
|
38
|
+
console.log(`📋 Slack DM 貼付テンプレート${copied ? "(クリップボードにコピー済み)" : ""}:`);
|
|
39
|
+
console.log(`────────────────────────────────────────────────────────────`);
|
|
40
|
+
console.log(template);
|
|
41
|
+
console.log(`────────────────────────────────────────────────────────────`);
|
|
42
|
+
await closePool();
|
|
43
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runRevokeToken(): Promise<void>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { parseArgs } from "node:util";
|
|
2
|
+
import { requireNeon } from "./common.js";
|
|
3
|
+
import { findUserByName, revokeUser } from "../auth/user-store.js";
|
|
4
|
+
import { closePool } from "../cloud/neon-client.js";
|
|
5
|
+
export async function runRevokeToken() {
|
|
6
|
+
requireNeon();
|
|
7
|
+
const { values } = parseArgs({
|
|
8
|
+
options: {
|
|
9
|
+
user: { type: "string" },
|
|
10
|
+
help: { type: "boolean", short: "h", default: false },
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
if (values.help || !values.user) {
|
|
14
|
+
console.log(`使い方:
|
|
15
|
+
bizgate-admin revoke --user "氏名"
|
|
16
|
+
|
|
17
|
+
指定ユーザーのトークンを無効化します(退職・流出時)。
|
|
18
|
+
再有効化する場合は 'bizgate-admin reissue --user "氏名"' で新トークンを発行してください。
|
|
19
|
+
`);
|
|
20
|
+
process.exit(values.help ? 0 : 1);
|
|
21
|
+
}
|
|
22
|
+
const user = await findUserByName(values.user);
|
|
23
|
+
if (!user) {
|
|
24
|
+
console.error(`❌ ユーザー「${values.user}」が見つかりません。`);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
if (!user.active) {
|
|
28
|
+
console.log(`ℹ️ ユーザー「${user.name}」は既に revoke 済みです。`);
|
|
29
|
+
await closePool();
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
await revokeUser(user.id);
|
|
33
|
+
console.log(`✅ ${user.name} さん(id=${user.id})のトークンを無効化しました。`);
|
|
34
|
+
console.log(` 旧トークン prefix: ${user.token_prefix}...`);
|
|
35
|
+
console.log(` 既存セッションも次回リクエスト時に 401 になります。`);
|
|
36
|
+
await closePool();
|
|
37
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runUpdateLimit(): Promise<void>;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { parseArgs } from "node:util";
|
|
2
|
+
import { getPoolLimit, requireNeon } from "./common.js";
|
|
3
|
+
import { findUserByName, sumActiveDailyLimits, updateDailyLimit, } from "../auth/user-store.js";
|
|
4
|
+
import { closePool } from "../cloud/neon-client.js";
|
|
5
|
+
export async function runUpdateLimit() {
|
|
6
|
+
requireNeon();
|
|
7
|
+
const { values } = parseArgs({
|
|
8
|
+
options: {
|
|
9
|
+
user: { type: "string" },
|
|
10
|
+
limit: { type: "string" },
|
|
11
|
+
force: { type: "boolean", default: false },
|
|
12
|
+
help: { type: "boolean", short: "h", default: false },
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
if (values.help || !values.user || !values.limit) {
|
|
16
|
+
console.log(`使い方:
|
|
17
|
+
bizgate-admin update-limit --user "氏名" --limit 500 [--force]
|
|
18
|
+
|
|
19
|
+
プール上限(${getPoolLimit()})超過時はエラー。--force で強制適用。
|
|
20
|
+
`);
|
|
21
|
+
process.exit(values.help ? 0 : 1);
|
|
22
|
+
}
|
|
23
|
+
const user = await findUserByName(values.user);
|
|
24
|
+
if (!user) {
|
|
25
|
+
console.error(`❌ ユーザー「${values.user}」が見つかりません。`);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
if (!user.active) {
|
|
29
|
+
console.error(`❌ ユーザー「${values.user}」は revoke 済みです。`);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
const newLimit = Number(values.limit);
|
|
33
|
+
if (!Number.isFinite(newLimit) || newLimit <= 0) {
|
|
34
|
+
console.error("❌ --limit は正の整数を指定してください。");
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
const poolLimit = getPoolLimit();
|
|
38
|
+
const otherSum = await sumActiveDailyLimits(user.id);
|
|
39
|
+
const projected = otherSum + newLimit;
|
|
40
|
+
if (projected > poolLimit && !values.force) {
|
|
41
|
+
console.error(`❌ プール上限超過: 他ユーザー ${otherSum} + 新値 ${newLimit} = ${projected} > ${poolLimit}`);
|
|
42
|
+
console.error(` --force を付けると強制適用します。`);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
const oldLimit = user.daily_limit;
|
|
46
|
+
await updateDailyLimit(user.id, newLimit);
|
|
47
|
+
console.log(`✅ ${user.name} さんの上限を ${oldLimit} → ${newLimit} に変更しました。`);
|
|
48
|
+
console.log(` プール: ${projected}/${poolLimit}(残り ${poolLimit - projected})`);
|
|
49
|
+
await closePool();
|
|
50
|
+
}
|