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.
Files changed (70) hide show
  1. package/README.md +30 -99
  2. package/dist/admin/cli.d.ts +2 -0
  3. package/dist/admin/cli.js +70 -0
  4. package/dist/admin/common.d.ts +14 -0
  5. package/dist/admin/common.js +54 -0
  6. package/dist/admin/issue-token.d.ts +1 -0
  7. package/dist/admin/issue-token.js +82 -0
  8. package/dist/admin/list-users.d.ts +1 -0
  9. package/dist/admin/list-users.js +61 -0
  10. package/dist/admin/reissue-token.d.ts +1 -0
  11. package/dist/admin/reissue-token.js +43 -0
  12. package/dist/admin/revoke-token.d.ts +1 -0
  13. package/dist/admin/revoke-token.js +37 -0
  14. package/dist/admin/update-limit.d.ts +1 -0
  15. package/dist/admin/update-limit.js +50 -0
  16. package/dist/admin-ui/page.html +480 -0
  17. package/dist/admin-ui/router.d.ts +2 -0
  18. package/dist/admin-ui/router.js +293 -0
  19. package/dist/auth/token.d.ts +11 -0
  20. package/dist/auth/token.js +18 -0
  21. package/dist/auth/user-store.d.ts +50 -0
  22. package/dist/auth/user-store.js +130 -0
  23. package/dist/bizgate-client.d.ts +1 -1
  24. package/dist/bizgate-client.js +9 -9
  25. package/dist/cloud/neon-client.d.ts +4 -10
  26. package/dist/cloud/neon-client.js +11 -22
  27. package/dist/enrich/core.d.ts +90 -0
  28. package/dist/enrich/core.js +444 -0
  29. package/dist/enrich/tools.d.ts +3 -0
  30. package/dist/enrich/tools.js +255 -0
  31. package/dist/enrich/variants.d.ts +24 -0
  32. package/dist/enrich/variants.js +88 -0
  33. package/dist/http-server.d.ts +14 -0
  34. package/dist/http-server.js +218 -0
  35. package/dist/index.d.ts +15 -4
  36. package/dist/index.js +404 -1024
  37. package/dist/install-skill.js +41 -228
  38. package/dist/shared/logger.js +0 -1
  39. package/dist/usage-tracker.d.ts +40 -15
  40. package/dist/usage-tracker.js +105 -31
  41. package/package.json +14 -10
  42. package/skills/bizgate/SKILL.md +142 -0
  43. package/dist/jpx-cache.d.ts +0 -36
  44. package/dist/jpx-cache.js +0 -230
  45. package/dist/papatto/credentials.d.ts +0 -12
  46. package/dist/papatto/credentials.js +0 -40
  47. package/dist/papatto/csv-parser.d.ts +0 -9
  48. package/dist/papatto/csv-parser.js +0 -134
  49. package/dist/papatto/friendly-errors.d.ts +0 -8
  50. package/dist/papatto/friendly-errors.js +0 -103
  51. package/dist/papatto/master-extractor.d.ts +0 -51
  52. package/dist/papatto/master-extractor.js +0 -295
  53. package/dist/papatto/page-extract.d.ts +0 -55
  54. package/dist/papatto/page-extract.js +0 -324
  55. package/dist/papatto/pipeline.d.ts +0 -67
  56. package/dist/papatto/pipeline.js +0 -295
  57. package/dist/papatto/playwright-session.d.ts +0 -11
  58. package/dist/papatto/playwright-session.js +0 -85
  59. package/dist/papatto/search.d.ts +0 -72
  60. package/dist/papatto/search.js +0 -512
  61. package/dist/papatto/tools.d.ts +0 -2
  62. package/dist/papatto/tools.js +0 -770
  63. package/dist/prospect-history.d.ts +0 -63
  64. package/dist/prospect-history.js +0 -155
  65. package/dist/queue/playwright-queue.d.ts +0 -9
  66. package/dist/queue/playwright-queue.js +0 -23
  67. package/dist/seed-cache.d.ts +0 -33
  68. package/dist/seed-cache.js +0 -169
  69. package/dist/sheets/client.d.ts +0 -15
  70. 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
- ### prospect-match スキル(部署マッチング)
123
+ ### /bizgate ルーター(単一入口)
124
124
 
125
- 企業の部署一覧から、DigiManの各サービス(営業代行・Solution・AI)に適した営業ターゲット部署を自動で提案するスキルです。
125
+ BizGate の機能をすべてまとめた **入口スキル** です。どの機能を使えばいいか迷ったら、まず `/bizgate` と打つだけ。メニューが表示され、選ぶだけで進めます。
126
126
 
127
127
  ```
128
- /prospect-match 兼松株式会社
128
+ /bizgate
129
129
  ```
130
130
 
131
- または自然言語でもOK:
131
+ または自然言語で:
132
132
  ```
133
- 「兼松株式会社にDigiManのサービスが売れそうな部署を探して」
133
+ 「BizGate 使いたい」
134
+ 「営業リストを補強したい」
134
135
  ```
135
136
 
136
- **出力内容:**
137
- - サービスごとの推薦部署(最大5件ずつ)+ 電話番号
138
- - マッチ理由
139
- - 最優先アプローチ先
137
+ **メニュー (3 つから選択):**
140
138
 
141
- **すでにBizGate MCPをインストール済みの方** は、以下でスキルだけ追加できます:
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
- bash install-skill.sh
156
+ npx bizgate-mcp-server --install-skill
145
157
  ```
146
158
 
147
- 新規インストールの場合は `install.sh` にスキルも含まれています。
148
-
149
159
  ### 注意点
150
160
 
151
- - **会社名は正確に** 入力してください(例:「ソフバン」→ 「ソフトバンク株式会社」)
152
- - 1日に使える回数は **200回** までです(残り回数は毎回表示されます)
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上限(デフォルト: 200) |
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,2 @@
1
+ #!/usr/bin/env node
2
+ import "../shared/env.js";
@@ -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
+ }