@lnar/cli 0.0.1-dev.1da013d
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 +96 -0
- package/dist/api-client.d.ts +17 -0
- package/dist/api-client.js +35 -0
- package/dist/api-client.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +91 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/daemon.d.ts +5 -0
- package/dist/commands/daemon.js +158 -0
- package/dist/commands/daemon.js.map +1 -0
- package/dist/commands/down.d.ts +1 -0
- package/dist/commands/down.js +12 -0
- package/dist/commands/down.js.map +1 -0
- package/dist/commands/login.d.ts +6 -0
- package/dist/commands/login.js +86 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/scan.d.ts +4 -0
- package/dist/commands/scan.js +36 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/commands/status.d.ts +1 -0
- package/dist/commands/status.js +32 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/sync.d.ts +4 -0
- package/dist/commands/sync.js +96 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/up.d.ts +3 -0
- package/dist/commands/up.js +27 -0
- package/dist/commands/up.js.map +1 -0
- package/dist/config.d.ts +25 -0
- package/dist/config.js +82 -0
- package/dist/config.js.map +1 -0
- package/dist/hash.d.ts +7 -0
- package/dist/hash.js +12 -0
- package/dist/hash.js.map +1 -0
- package/dist/oauth-client.d.ts +32 -0
- package/dist/oauth-client.js +62 -0
- package/dist/oauth-client.js.map +1 -0
- package/dist/pending-client.d.ts +40 -0
- package/dist/pending-client.js +48 -0
- package/dist/pending-client.js.map +1 -0
- package/dist/scanners/_normalize.d.ts +13 -0
- package/dist/scanners/_normalize.js +76 -0
- package/dist/scanners/_normalize.js.map +1 -0
- package/dist/scanners/chatgpt.d.ts +20 -0
- package/dist/scanners/chatgpt.js +49 -0
- package/dist/scanners/chatgpt.js.map +1 -0
- package/dist/scanners/claude-code-plugins.d.ts +2 -0
- package/dist/scanners/claude-code-plugins.js +104 -0
- package/dist/scanners/claude-code-plugins.js.map +1 -0
- package/dist/scanners/claude-code.d.ts +13 -0
- package/dist/scanners/claude-code.js +221 -0
- package/dist/scanners/claude-code.js.map +1 -0
- package/dist/scanners/claude-desktop.d.ts +1 -0
- package/dist/scanners/claude-desktop.js +2 -0
- package/dist/scanners/claude-desktop.js.map +1 -0
- package/dist/scanners/codex.d.ts +5 -0
- package/dist/scanners/codex.js +69 -0
- package/dist/scanners/codex.js.map +1 -0
- package/dist/scanners/cursor.d.ts +9 -0
- package/dist/scanners/cursor.js +25 -0
- package/dist/scanners/cursor.js.map +1 -0
- package/dist/scanners/gemini-cli.d.ts +21 -0
- package/dist/scanners/gemini-cli.js +37 -0
- package/dist/scanners/gemini-cli.js.map +1 -0
- package/dist/scanners/index.d.ts +6 -0
- package/dist/scanners/index.js +16 -0
- package/dist/scanners/index.js.map +1 -0
- package/dist/service/files.d.ts +26 -0
- package/dist/service/files.js +119 -0
- package/dist/service/files.js.map +1 -0
- package/dist/service/install.d.ts +14 -0
- package/dist/service/install.js +231 -0
- package/dist/service/install.js.map +1 -0
- package/dist/types.d.ts +161 -0
- package/dist/types.js +53 -0
- package/dist/types.js.map +1 -0
- package/dist/writers/_fs.d.ts +22 -0
- package/dist/writers/_fs.js +55 -0
- package/dist/writers/_fs.js.map +1 -0
- package/dist/writers/chatgpt.d.ts +8 -0
- package/dist/writers/chatgpt.js +12 -0
- package/dist/writers/chatgpt.js.map +1 -0
- package/dist/writers/claude-code-plugins.d.ts +3 -0
- package/dist/writers/claude-code-plugins.js +74 -0
- package/dist/writers/claude-code-plugins.js.map +1 -0
- package/dist/writers/claude-code.d.ts +4 -0
- package/dist/writers/claude-code.js +128 -0
- package/dist/writers/claude-code.js.map +1 -0
- package/dist/writers/codex.d.ts +4 -0
- package/dist/writers/codex.js +88 -0
- package/dist/writers/codex.js.map +1 -0
- package/dist/writers/cursor.d.ts +4 -0
- package/dist/writers/cursor.js +74 -0
- package/dist/writers/cursor.js.map +1 -0
- package/dist/writers/gemini-cli.d.ts +4 -0
- package/dist/writers/gemini-cli.js +87 -0
- package/dist/writers/gemini-cli.js.map +1 -0
- package/dist/writers/index.d.ts +18 -0
- package/dist/writers/index.js +11 -0
- package/dist/writers/index.js.map +1 -0
- package/dist/writers/registry.d.ts +7 -0
- package/dist/writers/registry.js +31 -0
- package/dist/writers/registry.js.map +1 -0
- package/package.json +38 -0
package/README.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# @lnar/cli
|
|
2
|
+
|
|
3
|
+
Lnar CLI that scans your AI agent configuration files (Claude / Claude Code, Codex CLI, Cursor, Gemini CLI, ChatGPT, etc.) and reports the **list of connected MCP servers** to your lnar account.
|
|
4
|
+
|
|
5
|
+
Read-only: the CLI only reads agent config files. Environment variable **keys** are recorded, but their **values are never sent** to the server.
|
|
6
|
+
|
|
7
|
+
## Install / use
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
# 1. Install the CLI globally
|
|
11
|
+
npm install -g @lnar/cli
|
|
12
|
+
|
|
13
|
+
# 2. Sign in + start the always-on background service (one command)
|
|
14
|
+
lnar up
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`lnar up` handles everything:
|
|
18
|
+
|
|
19
|
+
- If you are not yet authenticated, it runs the OAuth device-code flow (opens a URL + shows a short code).
|
|
20
|
+
- It registers a background service that auto-starts on login / boot and re-launches if it crashes:
|
|
21
|
+
- macOS → launchd LaunchAgent (`~/Library/LaunchAgents/ai.lnar.daemon.plist`)
|
|
22
|
+
- Linux → systemd `--user` unit (`~/.config/systemd/user/lnar-daemon.service`)
|
|
23
|
+
- Windows → Task Scheduler (`LnarDaemon`, logon trigger + auto-restart)
|
|
24
|
+
|
|
25
|
+
The service syncs the latest MCP-server list with lnar every 30s and applies any changes queued from the dashboard (e.g. "Add MCP server", "Disconnect").
|
|
26
|
+
|
|
27
|
+
### Other commands
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
lnar status # show authentication + service status
|
|
31
|
+
lnar down # stop and remove the background service
|
|
32
|
+
lnar scan # one-shot: print detected MCP servers, no upload
|
|
33
|
+
lnar sync # one-shot: scan and upload, then exit
|
|
34
|
+
lnar login # re-authenticate (normally `lnar up` is enough)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Production vs develop verification
|
|
38
|
+
|
|
39
|
+
| Environment | Command | Default API |
|
|
40
|
+
|---|---|---|
|
|
41
|
+
| Production | `npm install -g @lnar/cli && lnar up` | `https://api.lnar.ai` |
|
|
42
|
+
| Develop (pre-release verification) | `npm install -g @lnar/cli@dev && lnar up` | `https://api-dev.lnar.ai` |
|
|
43
|
+
|
|
44
|
+
The `@dev` tag is published from the `develop` branch and is intended for verifying behaviour before promoting changes to production. To force a specific endpoint regardless of tag, pass `--api-base-url` or set `LNAR_API_BASE_URL`.
|
|
45
|
+
|
|
46
|
+
## Supported agents
|
|
47
|
+
|
|
48
|
+
| Agent | Status | Config locations scanned |
|
|
49
|
+
|----------------------|-------------|-------------------------------------------------------|
|
|
50
|
+
| Claude / Claude Code | implemented | `~/.claude.json`, `.mcp.json` (project), `~/.claude/plugins`, `claude mcp list` (claude.ai remote) |
|
|
51
|
+
| Codex CLI | implemented | `~/.codex/config.toml` |
|
|
52
|
+
| Cursor | implemented | `~/.cursor/mcp.json`, `.cursor/mcp.json` (project) |
|
|
53
|
+
| Gemini CLI | implemented | `~/.gemini/settings.json`, `.gemini/settings.json` (project) |
|
|
54
|
+
| ChatGPT | placeholder | `~/Library/Application Support/com.openai.chat` (existence only — connector data is encrypted) |
|
|
55
|
+
|
|
56
|
+
## Development (working on this CLI itself)
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
pnpm install
|
|
60
|
+
pnpm dev scan # run from source against your default API
|
|
61
|
+
pnpm test # run vitest
|
|
62
|
+
pnpm build # compile to dist/
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Release workflow
|
|
66
|
+
|
|
67
|
+
公開戦略は **canary + manual prod** (Next.js / Claude Code 系):
|
|
68
|
+
|
|
69
|
+
| トリガー | dist-tag | デフォルト API |
|
|
70
|
+
|---|---|---|
|
|
71
|
+
| `develop` への push | `@dev` (canary) | `api-dev.lnar.ai` |
|
|
72
|
+
| `cli-v*` の git tag push | `@latest` (本番) | `api.lnar.ai` |
|
|
73
|
+
| GitHub Actions の `workflow_dispatch` (手動) | 任意 (dev / latest / beta / rc) | tag に応じる |
|
|
74
|
+
|
|
75
|
+
### dev (canary) リリース
|
|
76
|
+
|
|
77
|
+
develop ブランチに変更を merge するだけ。CI が自動で `0.0.1-dev.abc1234` のような
|
|
78
|
+
SHA suffix 付きで `@dev` タグに publish する。
|
|
79
|
+
|
|
80
|
+
### 本番リリース
|
|
81
|
+
|
|
82
|
+
```sh
|
|
83
|
+
git checkout main
|
|
84
|
+
git merge develop # 本番化したい develop の内容を取り込む
|
|
85
|
+
cd cli
|
|
86
|
+
npm version minor # 0.0.1 → 0.1.0、自動で commit + tag (v0.1.0) が作られる
|
|
87
|
+
# ※ ただし tag 名は CI 側で "cli-v*" を期待するので
|
|
88
|
+
# git tag -a cli-v0.1.0 -m "Release 0.1.0" の方が安全
|
|
89
|
+
git push --follow-tags
|
|
90
|
+
# → cli-v0.1.0 tag が CI を発火し、@latest で publish
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 緊急時の手動公開
|
|
94
|
+
|
|
95
|
+
GitHub Actions の "Publish CLI" workflow を `workflow_dispatch` から起動し、
|
|
96
|
+
`tag` 入力 (`dev` / `latest` / `beta` / `rc`) を選んで実行。
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ScanResult } from './types.js';
|
|
2
|
+
export type SnapshotResponse = {
|
|
3
|
+
agents: Array<{
|
|
4
|
+
id: string;
|
|
5
|
+
hostname: string;
|
|
6
|
+
agent_kind: string;
|
|
7
|
+
agent_version: string | null;
|
|
8
|
+
last_seen_at: string;
|
|
9
|
+
mcp_server_count: number;
|
|
10
|
+
}>;
|
|
11
|
+
};
|
|
12
|
+
export declare class ApiError extends Error {
|
|
13
|
+
status: number;
|
|
14
|
+
body: string;
|
|
15
|
+
constructor(status: number, body: string);
|
|
16
|
+
}
|
|
17
|
+
export declare const postSnapshot: (baseUrl: string, apiKey: string, scan: ScanResult) => Promise<SnapshotResponse>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export class ApiError extends Error {
|
|
2
|
+
status;
|
|
3
|
+
body;
|
|
4
|
+
constructor(status, body) {
|
|
5
|
+
super(`API error ${status}: ${body.slice(0, 200)}`);
|
|
6
|
+
this.status = status;
|
|
7
|
+
this.body = body;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export const postSnapshot = async (baseUrl, apiKey, scan) => {
|
|
11
|
+
const url = new URL('/v1/monitoring/snapshots', baseUrl).toString();
|
|
12
|
+
const response = await fetch(url, {
|
|
13
|
+
method: 'POST',
|
|
14
|
+
headers: {
|
|
15
|
+
'Content-Type': 'application/json',
|
|
16
|
+
Authorization: `Bearer ${apiKey}`,
|
|
17
|
+
},
|
|
18
|
+
body: JSON.stringify({
|
|
19
|
+
hostname: scan.hostname,
|
|
20
|
+
scannedAt: scan.scannedAt,
|
|
21
|
+
agents: scan.agents.map((a) => ({
|
|
22
|
+
agentKind: a.agentKind,
|
|
23
|
+
agentVersion: a.agentVersion,
|
|
24
|
+
servers: a.servers,
|
|
25
|
+
plugins: a.plugins ?? [],
|
|
26
|
+
})),
|
|
27
|
+
}),
|
|
28
|
+
});
|
|
29
|
+
if (!response.ok) {
|
|
30
|
+
const text = await response.text().catch(() => '');
|
|
31
|
+
throw new ApiError(response.status, text);
|
|
32
|
+
}
|
|
33
|
+
return (await response.json());
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=api-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"AAaA,MAAM,OAAO,QAAS,SAAQ,KAAK;IACjC,MAAM,CAAS;IACf,IAAI,CAAS;IACb,YAAY,MAAc,EAAE,IAAY;QACtC,KAAK,CAAC,aAAa,MAAM,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QACpD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAED,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAC/B,OAAe,EACf,MAAc,EACd,IAAgB,EACW,EAAE;IAC7B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,0BAA0B,EAAE,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;IACpE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAChC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,aAAa,EAAE,UAAU,MAAM,EAAE;SAClC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC9B,SAAS,EAAE,CAAC,CAAC,SAAS;gBACtB,YAAY,EAAE,CAAC,CAAC,YAAY;gBAC5B,OAAO,EAAE,CAAC,CAAC,OAAO;gBAClB,OAAO,EAAE,CAAC,CAAC,OAAO,IAAI,EAAE;aACzB,CAAC,CAAC;SACJ,CAAC;KACH,CAAC,CAAC;IACH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QACnD,MAAM,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAqB,CAAC;AACrD,CAAC,CAAC"}
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { runDaemon } from './commands/daemon.js';
|
|
4
|
+
import { runDown } from './commands/down.js';
|
|
5
|
+
import { runLogin } from './commands/login.js';
|
|
6
|
+
import { runScan } from './commands/scan.js';
|
|
7
|
+
import { runStatus } from './commands/status.js';
|
|
8
|
+
import { runSync } from './commands/sync.js';
|
|
9
|
+
import { runUp } from './commands/up.js';
|
|
10
|
+
const program = new Command();
|
|
11
|
+
program
|
|
12
|
+
.name('lnar')
|
|
13
|
+
.description('Lnar CLI — scan local AI agents for connected MCP servers and report to lnar.')
|
|
14
|
+
.version('0.0.1');
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Primary commands (Tailscale 風)
|
|
17
|
+
// lnar up — 認証 (未認証時のみ) + 常駐サービス登録 + 起動
|
|
18
|
+
// lnar down — 常駐サービス停止 + 登録解除
|
|
19
|
+
// lnar status — 認証状態 + サービス状態
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
program
|
|
22
|
+
.command('up')
|
|
23
|
+
.description('Authenticate (if needed) and install lnar as a background service that runs forever.')
|
|
24
|
+
.option('--api-base-url <url>', 'lnar API base URL')
|
|
25
|
+
.option('--client-id <id>', 'OAuth client_id (default: lnar-cli)')
|
|
26
|
+
.action(async (opts) => {
|
|
27
|
+
await runUp({
|
|
28
|
+
apiBaseUrl: opts.apiBaseUrl,
|
|
29
|
+
clientId: opts.clientId,
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
program
|
|
33
|
+
.command('down')
|
|
34
|
+
.description('Stop and remove the lnar background service.')
|
|
35
|
+
.action(async () => {
|
|
36
|
+
await runDown();
|
|
37
|
+
});
|
|
38
|
+
program
|
|
39
|
+
.command('status')
|
|
40
|
+
.description('Show authentication state and background-service state.')
|
|
41
|
+
.action(async () => {
|
|
42
|
+
await runStatus();
|
|
43
|
+
});
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// Secondary commands
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
program
|
|
48
|
+
.command('login')
|
|
49
|
+
.description('Re-authorize this device via the OAuth Device Code flow (normally not needed — `lnar up` handles login).')
|
|
50
|
+
.option('--api-base-url <url>', 'lnar API base URL')
|
|
51
|
+
.option('--client-id <id>', 'OAuth client_id (default: lnar-cli)')
|
|
52
|
+
.action(async (opts) => {
|
|
53
|
+
await runLogin({
|
|
54
|
+
apiBaseUrl: opts.apiBaseUrl,
|
|
55
|
+
clientId: opts.clientId,
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
program
|
|
59
|
+
.command('scan')
|
|
60
|
+
.description('Scan local AI agent configs and print detected MCP servers (no upload).')
|
|
61
|
+
.option('--json', 'Output machine-readable JSON instead of human-readable text')
|
|
62
|
+
.action(async (opts) => {
|
|
63
|
+
await runScan({ json: Boolean(opts.json) });
|
|
64
|
+
});
|
|
65
|
+
program
|
|
66
|
+
.command('sync')
|
|
67
|
+
.description('Scan local AI agents once and upload the snapshot to lnar.')
|
|
68
|
+
.option('--dry-run', 'Print what would be uploaded, but do not contact the API')
|
|
69
|
+
.action(async (opts) => {
|
|
70
|
+
await runSync({ dryRun: Boolean(opts.dryRun) });
|
|
71
|
+
});
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
// Hidden internal command: launchd / systemd / Task Scheduler が exec する実体。
|
|
74
|
+
// ユーザー向けには `lnar up` が完全に隠蔽するため、--help に出さない。
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
program
|
|
77
|
+
.command('daemon', { hidden: true })
|
|
78
|
+
.description('(internal) Foreground polling loop used by the installed service.')
|
|
79
|
+
.option('--once', 'Run a single cycle and exit')
|
|
80
|
+
.option('--interval <seconds>', 'Poll interval in seconds (default: 30)', (v) => Number.parseInt(v, 10))
|
|
81
|
+
.action(async (opts) => {
|
|
82
|
+
await runDaemon({
|
|
83
|
+
once: Boolean(opts.once),
|
|
84
|
+
intervalSeconds: typeof opts.interval === 'number' ? opts.interval : undefined,
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
program.parseAsync(process.argv).catch((err) => {
|
|
88
|
+
process.stderr.write(`lnar: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
89
|
+
process.exit(1);
|
|
90
|
+
});
|
|
91
|
+
//# 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":"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"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { hostname } from 'node:os';
|
|
2
|
+
import { ApiError, postSnapshot } from '../api-client.js';
|
|
3
|
+
import { DEFAULT_CLIENT_ID, loadConfig, saveConfig } from '../config.js';
|
|
4
|
+
import { OAuthError, refreshAccessToken } from '../oauth-client.js';
|
|
5
|
+
import { listPendingForHost, reportApplied, reportFailed, } from '../pending-client.js';
|
|
6
|
+
import { runAllScanners } from '../scanners/index.js';
|
|
7
|
+
import { getWriter } from '../writers/registry.js';
|
|
8
|
+
const DEFAULT_INTERVAL_SECONDS = 30;
|
|
9
|
+
const ACCESS_TOKEN_REFRESH_SKEW_SECONDS = 60;
|
|
10
|
+
const sleep = (ms) => new Promise((resolve) => {
|
|
11
|
+
setTimeout(resolve, ms);
|
|
12
|
+
});
|
|
13
|
+
const isAccessTokenExpired = (config) => {
|
|
14
|
+
if (!config.accessTokenExpiresAt)
|
|
15
|
+
return false;
|
|
16
|
+
const expires = Date.parse(config.accessTokenExpiresAt);
|
|
17
|
+
if (Number.isNaN(expires))
|
|
18
|
+
return true;
|
|
19
|
+
return expires - Date.now() < ACCESS_TOKEN_REFRESH_SKEW_SECONDS * 1000;
|
|
20
|
+
};
|
|
21
|
+
const refreshIfNeeded = async (config) => {
|
|
22
|
+
if (!config.accessToken)
|
|
23
|
+
return config;
|
|
24
|
+
if (!isAccessTokenExpired(config))
|
|
25
|
+
return config;
|
|
26
|
+
if (!config.refreshToken) {
|
|
27
|
+
process.stderr.write('lnar: access token expired and no refresh token. Run `lnar login`.\n');
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
const clientId = config.clientId ?? DEFAULT_CLIENT_ID;
|
|
31
|
+
let token;
|
|
32
|
+
try {
|
|
33
|
+
token = await refreshAccessToken(config.apiBaseUrl, clientId, config.refreshToken);
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
if (err instanceof OAuthError && err.errorCode === 'invalid_grant') {
|
|
37
|
+
process.stderr.write('lnar: refresh token expired. Run `lnar login`.\n');
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
throw err;
|
|
41
|
+
}
|
|
42
|
+
const accessTokenExpiresAt = new Date(Date.now() + token.expires_in * 1000).toISOString();
|
|
43
|
+
const next = {
|
|
44
|
+
apiBaseUrl: config.apiBaseUrl,
|
|
45
|
+
accessToken: token.access_token,
|
|
46
|
+
refreshToken: token.refresh_token ?? config.refreshToken,
|
|
47
|
+
accessTokenExpiresAt,
|
|
48
|
+
scopes: token.scope ? token.scope.split(' ') : config.scopes,
|
|
49
|
+
clientId,
|
|
50
|
+
};
|
|
51
|
+
await saveConfig(next);
|
|
52
|
+
return { ...next, savedAt: new Date().toISOString() };
|
|
53
|
+
};
|
|
54
|
+
const bearerFor = (config) => {
|
|
55
|
+
if (config.accessToken)
|
|
56
|
+
return config.accessToken;
|
|
57
|
+
if (config.apiKey)
|
|
58
|
+
return config.apiKey;
|
|
59
|
+
throw new Error('No credentials available');
|
|
60
|
+
};
|
|
61
|
+
const runScanAndSync = async (config) => {
|
|
62
|
+
const agents = await runAllScanners();
|
|
63
|
+
const scan = {
|
|
64
|
+
hostname: hostname(),
|
|
65
|
+
scannedAt: new Date().toISOString(),
|
|
66
|
+
agents,
|
|
67
|
+
};
|
|
68
|
+
if (agents.length === 0)
|
|
69
|
+
return;
|
|
70
|
+
await postSnapshot(config.apiBaseUrl, bearerFor(config), scan);
|
|
71
|
+
};
|
|
72
|
+
const applyPending = async (config, change) => {
|
|
73
|
+
const token = bearerFor(config);
|
|
74
|
+
try {
|
|
75
|
+
const writer = await getWriter(change.agent_kind);
|
|
76
|
+
if (writer == null) {
|
|
77
|
+
await reportFailed(config.apiBaseUrl, token, change.id, `No writer registered for agent_kind=${change.agent_kind}`);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const result = await writer.apply(change);
|
|
81
|
+
if (result === 'unsupported') {
|
|
82
|
+
await reportFailed(config.apiBaseUrl, token, change.id, `agent_kind=${change.agent_kind} does not support write operations`);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
await reportApplied(config.apiBaseUrl, token, change.id);
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
89
|
+
try {
|
|
90
|
+
await reportFailed(config.apiBaseUrl, token, change.id, message);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// best-effort 報告失敗は無視 (次のループで再 poll する)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
const runOnce = async (config) => {
|
|
98
|
+
// 1. 最新スナップショットを送信 → サーバ側 agent ID が最新化される
|
|
99
|
+
await runScanAndSync(config);
|
|
100
|
+
// 2. 自 host 向けの pending を取得
|
|
101
|
+
const host = hostname();
|
|
102
|
+
const pending = await listPendingForHost(config.apiBaseUrl, bearerFor(config), host);
|
|
103
|
+
if (pending.length === 0) {
|
|
104
|
+
process.stdout.write(`lnar daemon: no pending changes for ${host}\n`);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
process.stdout.write(`lnar daemon: applying ${pending.length} pending change${pending.length === 1 ? '' : 's'} on ${host}\n`);
|
|
108
|
+
for (const change of pending) {
|
|
109
|
+
process.stdout.write(` - [${change.action}] ${change.target_name} (agent=${change.agent_kind})\n`);
|
|
110
|
+
await applyPending(config, change);
|
|
111
|
+
}
|
|
112
|
+
// 3. 反映後に再スキャン送信して dashboard の表示も最新化
|
|
113
|
+
await runScanAndSync(config);
|
|
114
|
+
};
|
|
115
|
+
export const runDaemon = async (options = {}) => {
|
|
116
|
+
const initial = await loadConfig();
|
|
117
|
+
if (initial == null) {
|
|
118
|
+
process.stderr.write('lnar: not logged in. Run `lnar login` first.\n');
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
const interval = (options.intervalSeconds ?? DEFAULT_INTERVAL_SECONDS) * 1000;
|
|
122
|
+
let running = true;
|
|
123
|
+
const stop = () => {
|
|
124
|
+
running = false;
|
|
125
|
+
process.stdout.write('\nlnar daemon: shutting down…\n');
|
|
126
|
+
};
|
|
127
|
+
process.on('SIGINT', stop);
|
|
128
|
+
process.on('SIGTERM', stop);
|
|
129
|
+
while (running) {
|
|
130
|
+
let config;
|
|
131
|
+
try {
|
|
132
|
+
config = await refreshIfNeeded(initial);
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
136
|
+
process.stderr.write(`lnar daemon: token refresh failed: ${message}\n`);
|
|
137
|
+
if (options.once)
|
|
138
|
+
process.exit(1);
|
|
139
|
+
await sleep(interval);
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
await runOnce(config);
|
|
144
|
+
}
|
|
145
|
+
catch (err) {
|
|
146
|
+
if (err instanceof ApiError && err.status === 401) {
|
|
147
|
+
process.stderr.write('lnar daemon: 401 — Run `lnar login` to refresh.\n');
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
150
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
151
|
+
process.stderr.write(`lnar daemon: cycle error: ${message}\n`);
|
|
152
|
+
}
|
|
153
|
+
if (options.once)
|
|
154
|
+
break;
|
|
155
|
+
await sleep(interval);
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
//# sourceMappingURL=daemon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../../src/commands/daemon.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const runDown: () => Promise<void>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `lnar down` — 常駐サービスを停止して登録を解除する。
|
|
3
|
+
*
|
|
4
|
+
* 認証情報 (~/.config/lnar/auth.json) には触らない。再度 `lnar up` を実行すれば
|
|
5
|
+
* 同じ資格情報のままサービスが再開する。資格情報も消したい場合は `lnar logout` を使う
|
|
6
|
+
* (現状未実装 — 必要になったら追加)。
|
|
7
|
+
*/
|
|
8
|
+
import { runDaemonUninstall } from '../service/install.js';
|
|
9
|
+
export const runDown = async () => {
|
|
10
|
+
await runDaemonUninstall();
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=down.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"down.js","sourceRoot":"","sources":["../../src/commands/down.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAE3D,MAAM,CAAC,MAAM,OAAO,GAAG,KAAK,IAAmB,EAAE;IAC/C,MAAM,kBAAkB,EAAE,CAAC;AAC7B,CAAC,CAAC"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { DEFAULT_API_BASE_URL, DEFAULT_CLIENT_ID, saveConfig } from '../config.js';
|
|
2
|
+
import { OAuthError, pollForToken, requestDeviceAuthorization, sleep, } from '../oauth-client.js';
|
|
3
|
+
const DEFAULT_SCOPES = ['monitoring:read', 'monitoring:write'];
|
|
4
|
+
const resolveBaseUrl = (override) => {
|
|
5
|
+
const value = override ?? process.env.LNAR_API_BASE_URL ?? DEFAULT_API_BASE_URL;
|
|
6
|
+
try {
|
|
7
|
+
new URL(value);
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
throw new Error(`Invalid API base URL: ${value}`);
|
|
11
|
+
}
|
|
12
|
+
return value;
|
|
13
|
+
};
|
|
14
|
+
const printAuthorizationInstructions = (verificationUri, verificationUriComplete, userCode, expiresInSeconds) => {
|
|
15
|
+
const minutes = Math.max(1, Math.round(expiresInSeconds / 60));
|
|
16
|
+
process.stdout.write('\n');
|
|
17
|
+
process.stdout.write('To authorize this device, visit:\n');
|
|
18
|
+
process.stdout.write(` ${verificationUri}\n`);
|
|
19
|
+
process.stdout.write('and enter the code:\n\n');
|
|
20
|
+
process.stdout.write(` ${userCode}\n\n`);
|
|
21
|
+
process.stdout.write('Or open the URL with the code pre-filled:\n');
|
|
22
|
+
process.stdout.write(` ${verificationUriComplete}\n\n`);
|
|
23
|
+
process.stdout.write(`Waiting for authorization (this code expires in ${minutes} minute${minutes === 1 ? '' : 's'})…\n`);
|
|
24
|
+
};
|
|
25
|
+
export const runLogin = async (options = {}) => {
|
|
26
|
+
const apiBaseUrl = resolveBaseUrl(options.apiBaseUrl);
|
|
27
|
+
const clientId = options.clientId ?? DEFAULT_CLIENT_ID;
|
|
28
|
+
const scopes = options.scopes ?? DEFAULT_SCOPES;
|
|
29
|
+
let auth;
|
|
30
|
+
try {
|
|
31
|
+
auth = await requestDeviceAuthorization(apiBaseUrl, clientId, scopes);
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
if (err instanceof OAuthError) {
|
|
35
|
+
process.stderr.write(`lnar: device authorization failed: ${err.message}\n`);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
throw err;
|
|
39
|
+
}
|
|
40
|
+
printAuthorizationInstructions(auth.verification_uri, auth.verification_uri_complete, auth.user_code, auth.expires_in);
|
|
41
|
+
// RFC 8628 §3.5: ポーリング間隔はサーバ指定の `interval` 以上、
|
|
42
|
+
// `slow_down` を受け取ったら +5 秒 (サーバ側で自動加算済みだが、念のためここでも対応)。
|
|
43
|
+
let intervalSeconds = Math.max(1, auth.interval || 5);
|
|
44
|
+
const deadline = Date.now() + auth.expires_in * 1000;
|
|
45
|
+
while (Date.now() < deadline) {
|
|
46
|
+
await sleep(intervalSeconds * 1000);
|
|
47
|
+
try {
|
|
48
|
+
const token = await pollForToken(apiBaseUrl, clientId, auth.device_code);
|
|
49
|
+
const accessTokenExpiresAt = new Date(Date.now() + token.expires_in * 1000).toISOString();
|
|
50
|
+
const path = await saveConfig({
|
|
51
|
+
apiBaseUrl,
|
|
52
|
+
accessToken: token.access_token,
|
|
53
|
+
refreshToken: token.refresh_token,
|
|
54
|
+
accessTokenExpiresAt,
|
|
55
|
+
scopes: token.scope ? token.scope.split(' ') : Array.from(scopes),
|
|
56
|
+
clientId,
|
|
57
|
+
});
|
|
58
|
+
process.stdout.write(`\nAuthorized. Credentials saved to ${path}\n`);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
if (!(err instanceof OAuthError))
|
|
63
|
+
throw err;
|
|
64
|
+
if (err.errorCode === 'authorization_pending') {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (err.errorCode === 'slow_down') {
|
|
68
|
+
intervalSeconds += 5;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (err.errorCode === 'expired_token') {
|
|
72
|
+
process.stderr.write('\nlnar: the device code expired. Re-run `lnar login`.\n');
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
if (err.errorCode === 'access_denied') {
|
|
76
|
+
process.stderr.write('\nlnar: authorization was denied.\n');
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
process.stderr.write(`\nlnar: ${err.message}\n`);
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
process.stderr.write('\nlnar: device code expired. Run `lnar login` again.\n');
|
|
84
|
+
process.exit(1);
|
|
85
|
+
};
|
|
86
|
+
//# sourceMappingURL=login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { hostname } from 'node:os';
|
|
2
|
+
import { runAllScanners } from '../scanners/index.js';
|
|
3
|
+
const formatHuman = (result) => {
|
|
4
|
+
if (result.agents.length === 0) {
|
|
5
|
+
return 'No AI agents with MCP servers detected on this machine.';
|
|
6
|
+
}
|
|
7
|
+
const lines = [`Host: ${result.hostname} Scanned: ${result.scannedAt}`, ''];
|
|
8
|
+
for (const agent of result.agents) {
|
|
9
|
+
lines.push(`# ${agent.agentKind} (${agent.servers.length} server${agent.servers.length === 1 ? '' : 's'})`);
|
|
10
|
+
if (agent.servers.length === 0) {
|
|
11
|
+
lines.push(' (no MCP servers configured)');
|
|
12
|
+
}
|
|
13
|
+
for (const s of agent.servers) {
|
|
14
|
+
const transport = s.transport.padEnd(6);
|
|
15
|
+
const target = s.transport === 'stdio' ? (s.command ?? '?') : (s.url ?? '?');
|
|
16
|
+
const scope = `[${s.sourceScope}]`.padEnd(10);
|
|
17
|
+
lines.push(` - ${s.name.padEnd(24)} ${transport} ${scope} ${target}`);
|
|
18
|
+
}
|
|
19
|
+
lines.push('');
|
|
20
|
+
}
|
|
21
|
+
return lines.join('\n');
|
|
22
|
+
};
|
|
23
|
+
export const runScan = async (options = {}) => {
|
|
24
|
+
const agents = await runAllScanners();
|
|
25
|
+
const result = {
|
|
26
|
+
hostname: hostname(),
|
|
27
|
+
scannedAt: new Date().toISOString(),
|
|
28
|
+
agents,
|
|
29
|
+
};
|
|
30
|
+
if (options.json) {
|
|
31
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
process.stdout.write(`${formatHuman(result)}\n`);
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=scan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan.js","sourceRoot":"","sources":["../../src/commands/scan.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAOtD,MAAM,WAAW,GAAG,CAAC,MAAkB,EAAU,EAAE;IACjD,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,OAAO,yDAAyD,CAAC;IACnE,CAAC;IACD,MAAM,KAAK,GAAa,CAAC,SAAS,MAAM,CAAC,QAAQ,gBAAgB,MAAM,CAAC,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC;IACzF,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClC,KAAK,CAAC,IAAI,CACR,KAAK,KAAK,CAAC,SAAS,MAAM,KAAK,CAAC,OAAO,CAAC,MAAM,UAAU,KAAK,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CACjG,CAAC;QACF,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/B,KAAK,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;QAC9C,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YAC9B,MAAM,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACxC,MAAM,MAAM,GAAG,CAAC,CAAC,SAAS,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC;YAC7E,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,WAAW,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAC9C,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,SAAS,IAAI,KAAK,IAAI,MAAM,EAAE,CAAC,CAAC;QACzE,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,OAAO,GAAG,KAAK,EAAE,UAA8B,EAAE,EAAiB,EAAE;IAC/E,MAAM,MAAM,GAAG,MAAM,cAAc,EAAE,CAAC;IACtC,MAAM,MAAM,GAAe;QACzB,QAAQ,EAAE,QAAQ,EAAE;QACpB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,MAAM;KACP,CAAC;IACF,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QAC7D,OAAO;IACT,CAAC;IACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AACnD,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const runStatus: () => Promise<void>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `lnar status` — 認証状態と常駐サービスの状態をまとめて表示する。
|
|
3
|
+
*
|
|
4
|
+
* Exit code:
|
|
5
|
+
* 0 — 認証済み かつ サービス稼働中
|
|
6
|
+
* 1 — どちらかが欠けている (未認証 / サービス未起動 / 未インストール)
|
|
7
|
+
*
|
|
8
|
+
* これにより `lnar status && echo ok` のようなシェル連携が書ける (Tailscale 同様)。
|
|
9
|
+
*/
|
|
10
|
+
import { loadConfig } from '../config.js';
|
|
11
|
+
import { runDaemonStatus } from '../service/install.js';
|
|
12
|
+
export const runStatus = async () => {
|
|
13
|
+
const config = await loadConfig();
|
|
14
|
+
let authed = false;
|
|
15
|
+
if (config == null) {
|
|
16
|
+
process.stdout.write('Authentication: not signed in (run `lnar up` to log in)\n');
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
authed = true;
|
|
20
|
+
process.stdout.write(`Authentication: signed in to ${config.apiBaseUrl}\n`);
|
|
21
|
+
if (config.accessTokenExpiresAt) {
|
|
22
|
+
process.stdout.write(` access token expires: ${config.accessTokenExpiresAt}\n`);
|
|
23
|
+
}
|
|
24
|
+
if (config.scopes && config.scopes.length > 0) {
|
|
25
|
+
process.stdout.write(` scopes: ${config.scopes.join(' ')}\n`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
process.stdout.write('\nBackground service:\n');
|
|
29
|
+
const running = await runDaemonStatus();
|
|
30
|
+
process.exit(authed && running ? 0 : 1);
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,IAAmB,EAAE;IACjD,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;IAClC,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;QACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,2DAA2D,CAAC,CAAC;IACpF,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,IAAI,CAAC;QACd,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gCAAgC,MAAM,CAAC,UAAU,IAAI,CAAC,CAAC;QAC5E,IAAI,MAAM,CAAC,oBAAoB,EAAE,CAAC;YAChC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,2BAA2B,MAAM,CAAC,oBAAoB,IAAI,CAAC,CAAC;QACnF,CAAC;QACD,IAAI,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACjE,CAAC;IACH,CAAC;IACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,MAAM,eAAe,EAAE,CAAC;IACxC,OAAO,CAAC,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC1C,CAAC,CAAC"}
|