@reumbra/forge 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.
Files changed (63) hide show
  1. package/README.md +165 -0
  2. package/bin/forge.js +2 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +225 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/commands/activate.d.ts +1 -0
  7. package/dist/commands/activate.js +55 -0
  8. package/dist/commands/activate.js.map +1 -0
  9. package/dist/commands/config.d.ts +1 -0
  10. package/dist/commands/config.js +35 -0
  11. package/dist/commands/config.js.map +1 -0
  12. package/dist/commands/dashboard.d.ts +1 -0
  13. package/dist/commands/dashboard.js +171 -0
  14. package/dist/commands/dashboard.js.map +1 -0
  15. package/dist/commands/deactivate.d.ts +1 -0
  16. package/dist/commands/deactivate.js +33 -0
  17. package/dist/commands/deactivate.js.map +1 -0
  18. package/dist/commands/doctor.d.ts +5 -0
  19. package/dist/commands/doctor.js +156 -0
  20. package/dist/commands/doctor.js.map +1 -0
  21. package/dist/commands/install.d.ts +1 -0
  22. package/dist/commands/install.js +94 -0
  23. package/dist/commands/install.js.map +1 -0
  24. package/dist/commands/list.d.ts +1 -0
  25. package/dist/commands/list.js +46 -0
  26. package/dist/commands/list.js.map +1 -0
  27. package/dist/commands/status.d.ts +1 -0
  28. package/dist/commands/status.js +75 -0
  29. package/dist/commands/status.js.map +1 -0
  30. package/dist/commands/uninstall.d.ts +1 -0
  31. package/dist/commands/uninstall.js +31 -0
  32. package/dist/commands/uninstall.js.map +1 -0
  33. package/dist/commands/update.d.ts +1 -0
  34. package/dist/commands/update.js +30 -0
  35. package/dist/commands/update.js.map +1 -0
  36. package/dist/lib/api.d.ts +17 -0
  37. package/dist/lib/api.js +53 -0
  38. package/dist/lib/api.js.map +1 -0
  39. package/dist/lib/config.d.ts +4 -0
  40. package/dist/lib/config.js +40 -0
  41. package/dist/lib/config.js.map +1 -0
  42. package/dist/lib/logger.d.ts +11 -0
  43. package/dist/lib/logger.js +40 -0
  44. package/dist/lib/logger.js.map +1 -0
  45. package/dist/lib/machine-id.d.ts +5 -0
  46. package/dist/lib/machine-id.js +11 -0
  47. package/dist/lib/machine-id.js.map +1 -0
  48. package/dist/lib/paths.d.ts +8 -0
  49. package/dist/lib/paths.js +13 -0
  50. package/dist/lib/paths.js.map +1 -0
  51. package/dist/lib/styles.d.ts +32 -0
  52. package/dist/lib/styles.js +50 -0
  53. package/dist/lib/styles.js.map +1 -0
  54. package/dist/lib/ui.d.ts +22 -0
  55. package/dist/lib/ui.js +131 -0
  56. package/dist/lib/ui.js.map +1 -0
  57. package/dist/lib/zip.d.ts +6 -0
  58. package/dist/lib/zip.js +56 -0
  59. package/dist/lib/zip.js.map +1 -0
  60. package/dist/types.d.ts +61 -0
  61. package/dist/types.js +2 -0
  62. package/dist/types.js.map +1 -0
  63. package/package.json +51 -0
package/README.md ADDED
@@ -0,0 +1,165 @@
1
+ # Forge DevKit CLI
2
+
3
+ **Package:** `@reumbra/forge`
4
+ **Role:** CLI tool for installing and managing Forge plugins for Claude Code
5
+
6
+ ## Ecosystem
7
+
8
+ Forge DevKit is a commercial product by [Reumbra](https://reumbra.dev) — AI-powered project scaffolding tools for Claude Code.
9
+
10
+ ```
11
+ ┌──────────────────────────────────────────────────────────────────┐
12
+ │ Forge DevKit Ecosystem │
13
+ ├──────────────────────────────────────────────────────────────────┤
14
+ │ │
15
+ │ forge-devkit-cli (this repo) PUBLIC │
16
+ │ └─ npm @reumbra/forge │
17
+ │ └─ Commands: activate, install, update, list, status, doctor │
18
+ │ └─ User-facing tool, distributed via npm │
19
+ │ │
20
+ │ forge-devkit-api PRIVATE │
21
+ │ └─ License validation, plugin delivery, webhooks │
22
+ │ └─ Node.js (Fastify) + PostgreSQL (Supabase) + AWS S3 │
23
+ │ │
24
+ │ forge-devkit-landing PUBLIC │
25
+ │ └─ reumbra.dev/forge — website, pricing, docs │
26
+ │ └─ Astro + Tailwind, hosted on Cloudflare Pages │
27
+ │ │
28
+ │ forge-devkit-plugins PRIVATE (currently public) │
29
+ │ └─ Plugin source code, tests, design docs │
30
+ │ └─ Currently: github.com/maselious/ai-marketplace │
31
+ │ │
32
+ └──────────────────────────────────────────────────────────────────┘
33
+ ```
34
+
35
+ ## Architecture
36
+
37
+ ```
38
+ ┌──────────────┐ ┌─────────────────┐
39
+ │ forge CLI │──────▶│ Forge API │
40
+ │ (this repo) │ │ (devkit-api) │
41
+ │ │ │ │
42
+ │ activate │ │ /auth/activate │
43
+ │ install │ │ /plugins/list │
44
+ │ update │ │ /plugins/down │
45
+ │ list │ │ /license/check │
46
+ └──────────────┘ └────────┬────────┘
47
+
48
+ ┌────────┴─────────┐
49
+ │ Plugin Storage │
50
+ │ (AWS S3) │
51
+ └───────────────────┘
52
+ ```
53
+
54
+ ## Commands
55
+
56
+ ```
57
+ forge activate <license-key> # Bind license to machine
58
+ forge deactivate # Unbind (for machine transfer)
59
+ forge install <plugin> [version] # Download and install plugin
60
+ forge update [plugin] # Update all or specific plugin
61
+ forge list # Show installed + available
62
+ forge status # License: expiry, binding, limits
63
+ forge doctor # Diagnostics: Claude Code found? Plugins intact?
64
+ ```
65
+
66
+ ## Config Storage
67
+
68
+ ```json
69
+ // ~/.forge/config.json
70
+ {
71
+ "license_key": "FRG-XXXX-XXXX-XXXX",
72
+ "machine_id": "a1b2c3...",
73
+ "api_url": "https://api.reumbra.com/velvet",
74
+ "installed_plugins": {
75
+ "forge-core": { "version": "1.5.0", "installed_at": "2026-02-19T..." },
76
+ "forge-product": { "version": "0.4.0", "installed_at": "2026-02-19T..." }
77
+ }
78
+ }
79
+ ```
80
+
81
+ ## Install Flow
82
+
83
+ ```
84
+ $ forge install core
85
+
86
+ 1. Read config.json -> license_key, machine_id
87
+ 2. POST /plugins/download {license_key, machine_id, plugin: "forge-core", version: "latest"}
88
+ 3. API validates license, expiry, machine_id, plugin access
89
+ 4. Returns signed download URL (R2 presigned, 5 min TTL)
90
+ 5. CLI downloads .zip via signed URL
91
+ 6. Unpacks to ~/.forge/cache/forge-core@1.5.0/
92
+ 7. Links into Claude Code plugin directory
93
+ 8. Updates config.json -> installed_plugins
94
+ 9. "forge-core@1.5.0 installed. Run /forge:setup in your project"
95
+ ```
96
+
97
+ ## Machine ID & Limits
98
+
99
+ License bound to N devices (default: 3). Machine ID = SHA256 hash of hostname + OS + username.
100
+
101
+ ```
102
+ Activate flow:
103
+ CLI: POST /auth/activate {license_key, machine_id}
104
+ API: validate key -> check slots (3 max) -> register machine_id -> 200 OK
105
+ or 403 "All device slots used. Run forge deactivate on another machine"
106
+ ```
107
+
108
+ ## Offline Behavior
109
+
110
+ - Already installed plugins: work forever (just .md files on disk)
111
+ - Install/update: requires API connection
112
+ - After license expiry: installed versions continue working, updates blocked
113
+
114
+ ## Tech Stack
115
+
116
+ - **Runtime:** Node.js >=20
117
+ - **Package:** `@reumbra/forge` (public, scoped npm)
118
+ - **Dependencies:** Commander.js (routing), @clack/prompts (interactive UI)
119
+ - **Build:** TypeScript (strict), Biome (lint + format)
120
+ - **Tests:** Vitest (112 tests, 17 suites)
121
+
122
+ ## Installation
123
+
124
+ ```bash
125
+ npm install -g @reumbra/forge
126
+ ```
127
+
128
+ Or run without installing:
129
+
130
+ ```bash
131
+ npx @reumbra/forge doctor
132
+ ```
133
+
134
+ ## Development
135
+
136
+ ```bash
137
+ pnpm install # Install dev dependencies
138
+ pnpm dev -- doctor # Run any command in dev mode (via tsx)
139
+ pnpm build # Compile TypeScript → dist/
140
+ pnpm check # Lint + format (Biome)
141
+ pnpm test # Run tests
142
+ ```
143
+
144
+ ### Project Structure
145
+
146
+ ```
147
+ src/
148
+ ├── cli.ts # Entry point, arg parser, command dispatch
149
+ ├── types.ts # Shared types (config, API responses)
150
+ ├── commands/
151
+ │ ├── activate.ts # forge activate <key>
152
+ │ ├── deactivate.ts # forge deactivate
153
+ │ ├── install.ts # forge install <plugin> [version]
154
+ │ ├── update.ts # forge update [plugin]
155
+ │ ├── list.ts # forge list
156
+ │ ├── status.ts # forge status
157
+ │ └── doctor.ts # forge doctor (local only)
158
+ └── lib/
159
+ ├── api.ts # HTTP client (fetch), ApiError
160
+ ├── config.ts # ~/.forge/config.json management
161
+ ├── logger.ts # CLI output (colors, tables, icons)
162
+ ├── machine-id.ts # SHA256-based device fingerprint
163
+ ├── paths.ts # ~/.forge/ path constants
164
+ └── zip.ts # ZIP extraction (Node.js built-ins)
165
+ ```
package/bin/forge.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import "../dist/cli.js";
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function createProgram(): Command;
package/dist/cli.js ADDED
@@ -0,0 +1,225 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import * as p from "@clack/prompts";
5
+ import { Command } from "commander";
6
+ import { activate } from "./commands/activate.js";
7
+ import { showConfig } from "./commands/config.js";
8
+ import { dashboard } from "./commands/dashboard.js";
9
+ import { deactivate } from "./commands/deactivate.js";
10
+ import { doctor } from "./commands/doctor.js";
11
+ import { install } from "./commands/install.js";
12
+ import { list } from "./commands/list.js";
13
+ import { status } from "./commands/status.js";
14
+ import { uninstall } from "./commands/uninstall.js";
15
+ import { update } from "./commands/update.js";
16
+ import { loadConfig } from "./lib/config.js";
17
+ import { log } from "./lib/logger.js";
18
+ import { bold, dim, reset } from "./lib/styles.js";
19
+ import { banner } from "./lib/ui.js";
20
+ function getVersion() {
21
+ const __dirname = dirname(fileURLToPath(import.meta.url));
22
+ const pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
23
+ return pkg.version;
24
+ }
25
+ export function createProgram() {
26
+ const program = new Command();
27
+ program
28
+ .name("forge")
29
+ .version(`forge v${getVersion()}`, "-v, --version")
30
+ .addHelpText("before", `\n${banner()}\n`)
31
+ .addHelpText("after", `\n${bold}Examples:${reset}\n ${dim}$${reset} forge activate FRG-XXXX-XXXX-XXXX\n ${dim}$${reset} forge install core\n ${dim}$${reset} forge install forge-product@1.2.0\n ${dim}$${reset} forge list\n`)
32
+ .usage("[command] [options]")
33
+ .showHelpAfterError('Run "forge --help" for available commands.');
34
+ // Catch-all: no command → interactive dashboard (TTY) or help (non-TTY), unknown → error
35
+ program.argument("[command]").action(async (cmd) => {
36
+ if (!cmd) {
37
+ if (process.stdin.isTTY) {
38
+ await dashboard();
39
+ }
40
+ else {
41
+ program.help();
42
+ }
43
+ }
44
+ else {
45
+ log.error(`Unknown command: ${cmd}`);
46
+ log.info('Run "forge --help" for available commands.');
47
+ process.exit(1);
48
+ }
49
+ });
50
+ program
51
+ .command("activate")
52
+ .description("Bind license to this machine")
53
+ .argument("[license-key]", "Forge license key (FRG-XXXX-XXXX-XXXX)")
54
+ .action(async (key) => {
55
+ if (!key) {
56
+ if (!process.stdin.isTTY) {
57
+ log.error("Usage: forge activate <license-key>");
58
+ process.exit(1);
59
+ }
60
+ const input = await p.text({
61
+ message: "Enter your license key:",
62
+ placeholder: "FRG-XXXX-XXXX-XXXX",
63
+ validate: (v) => {
64
+ if (!v || !/^FRG-[A-Z2-9]{4}-[A-Z2-9]{4}-[A-Z2-9]{4}$/.test(v)) {
65
+ return "Invalid format. Expected: FRG-XXXX-XXXX-XXXX";
66
+ }
67
+ },
68
+ });
69
+ if (p.isCancel(input)) {
70
+ p.cancel("Activation cancelled.");
71
+ process.exit(0);
72
+ }
73
+ key = input;
74
+ }
75
+ await activate(key);
76
+ });
77
+ program
78
+ .command("deactivate")
79
+ .description("Unbind this machine (free a slot)")
80
+ .action(async () => {
81
+ if (process.stdin.isTTY) {
82
+ const confirmed = await p.confirm({
83
+ message: "Deactivate this machine? This will free up a machine slot.",
84
+ });
85
+ if (p.isCancel(confirmed) || !confirmed) {
86
+ p.cancel("Deactivation cancelled.");
87
+ return;
88
+ }
89
+ }
90
+ await deactivate();
91
+ });
92
+ program
93
+ .command("install")
94
+ .description("Download and install a plugin")
95
+ .argument("[plugin]", "Plugin name (e.g. core, forge-product@1.2.0)")
96
+ .argument("[version]", "Specific version")
97
+ .action(async (plugin, version) => {
98
+ if (!plugin) {
99
+ if (!process.stdin.isTTY) {
100
+ log.error("Usage: forge install <plugin> [version]");
101
+ process.exit(1);
102
+ }
103
+ const input = await p.text({
104
+ message: "Plugin name to install:",
105
+ placeholder: "e.g. core, forge-product@1.2.0",
106
+ validate: (v) => {
107
+ if (!v?.trim())
108
+ return "Plugin name is required";
109
+ },
110
+ });
111
+ if (p.isCancel(input)) {
112
+ p.cancel("Installation cancelled.");
113
+ process.exit(0);
114
+ }
115
+ plugin = input.trim();
116
+ }
117
+ if (plugin.includes("@")) {
118
+ const [name, ver] = plugin.split("@");
119
+ await install(name, ver);
120
+ }
121
+ else {
122
+ await install(plugin, version);
123
+ }
124
+ });
125
+ program
126
+ .command("uninstall")
127
+ .alias("remove")
128
+ .description("Remove an installed plugin")
129
+ .argument("[plugin]", "Plugin name to remove")
130
+ .action(async (plugin) => {
131
+ if (!plugin) {
132
+ if (!process.stdin.isTTY) {
133
+ log.error("Usage: forge uninstall <plugin>");
134
+ process.exit(1);
135
+ }
136
+ const config = loadConfig();
137
+ const installed = Object.keys(config.installed_plugins);
138
+ if (installed.length === 0) {
139
+ log.warn("No plugins installed.");
140
+ process.exit(0);
141
+ }
142
+ const selected = await p.select({
143
+ message: "Select plugin to uninstall:",
144
+ options: installed.map((name) => ({
145
+ value: name,
146
+ label: name,
147
+ hint: `v${config.installed_plugins[name].version}`,
148
+ })),
149
+ });
150
+ if (p.isCancel(selected)) {
151
+ p.cancel("Uninstall cancelled.");
152
+ process.exit(0);
153
+ }
154
+ plugin = selected;
155
+ }
156
+ if (process.stdin.isTTY) {
157
+ const confirmed = await p.confirm({
158
+ message: `Uninstall ${bold}${plugin}${reset}?`,
159
+ });
160
+ if (p.isCancel(confirmed) || !confirmed) {
161
+ p.cancel("Uninstall cancelled.");
162
+ return;
163
+ }
164
+ }
165
+ uninstall(plugin);
166
+ });
167
+ program
168
+ .command("update")
169
+ .description("Update all or specific plugin")
170
+ .argument("[plugin]", "Plugin to update (omit for all)")
171
+ .action(async (plugin) => {
172
+ await update(plugin);
173
+ });
174
+ program
175
+ .command("list")
176
+ .alias("ls")
177
+ .description("Show available plugins")
178
+ .action(async () => {
179
+ await list();
180
+ });
181
+ program
182
+ .command("status")
183
+ .description("License info: plan, expiry, devices")
184
+ .action(async () => {
185
+ await status();
186
+ });
187
+ program
188
+ .command("config")
189
+ .description("Show current configuration")
190
+ .action(() => {
191
+ showConfig();
192
+ });
193
+ program
194
+ .command("doctor")
195
+ .description("Run diagnostics")
196
+ .action(async () => {
197
+ const result = await doctor();
198
+ if (result.issues > 0)
199
+ process.exit(1);
200
+ });
201
+ return program;
202
+ }
203
+ async function main() {
204
+ const program = createProgram();
205
+ try {
206
+ await program.parseAsync();
207
+ }
208
+ catch (err) {
209
+ if (err instanceof Error) {
210
+ if (err.message.includes("fetch failed") || err.message.includes("ECONNREFUSED")) {
211
+ log.error("Could not connect to Forge API.");
212
+ log.info("Check your internet connection or try again later.");
213
+ }
214
+ else {
215
+ log.error(err.message);
216
+ }
217
+ }
218
+ else {
219
+ log.error("An unexpected error occurred.");
220
+ }
221
+ process.exit(1);
222
+ }
223
+ }
224
+ main();
225
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function activate(licenseKey: string): Promise<void>;
@@ -0,0 +1,55 @@
1
+ import { ApiError, apiRequest } from "../lib/api.js";
2
+ import { loadConfig, saveConfig } from "../lib/config.js";
3
+ import { log } from "../lib/logger.js";
4
+ import { bold, dim, green, reset } from "../lib/styles.js";
5
+ import { box, createSpinner, statusBadge } from "../lib/ui.js";
6
+ export async function activate(licenseKey) {
7
+ if (!/^FRG-[A-Z2-9]{4}-[A-Z2-9]{4}-[A-Z2-9]{4}$/.test(licenseKey)) {
8
+ log.error("Invalid license key format. Expected: FRG-XXXX-XXXX-XXXX");
9
+ process.exit(1);
10
+ }
11
+ const config = loadConfig();
12
+ config.license_key = licenseKey;
13
+ const spinner = createSpinner("Activating license...");
14
+ try {
15
+ const result = await apiRequest(config, {
16
+ method: "POST",
17
+ path: "/auth/activate",
18
+ body: {
19
+ license_key: licenseKey,
20
+ machine_id: config.machine_id,
21
+ },
22
+ });
23
+ saveConfig(config);
24
+ const expires = new Date(result.license.expires_at);
25
+ spinner.stop(`${green}✓${reset} License activated!`);
26
+ console.log(box([
27
+ `${bold}Plan:${reset} ${result.license.plan}`,
28
+ `${bold}Expires:${reset} ${expires.toLocaleDateString()}`,
29
+ `${bold}Machines:${reset} ${result.license.machines_used}/${result.license.max_machines}`,
30
+ `${bold}Status:${reset} ${statusBadge("active")}`,
31
+ ], { title: "License", borderColor: dim }));
32
+ log.info(`${dim}Run \`forge install <plugin>\` to get started.${reset}`);
33
+ }
34
+ catch (err) {
35
+ spinner.stop();
36
+ if (err instanceof ApiError) {
37
+ if (err.code === "MACHINE_LIMIT") {
38
+ log.error("All device slots are used.");
39
+ log.info("Run `forge deactivate` on another machine first.");
40
+ }
41
+ else if (err.code === "INVALID_LICENSE") {
42
+ log.error("Invalid license key. Double-check your key and try again.");
43
+ }
44
+ else if (err.code === "LICENSE_EXPIRED") {
45
+ log.error("This license has expired. Renew at https://reumbra.dev/forge");
46
+ }
47
+ else {
48
+ log.error(err.message);
49
+ }
50
+ process.exit(1);
51
+ }
52
+ throw err;
53
+ }
54
+ }
55
+ //# sourceMappingURL=activate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activate.js","sourceRoot":"","sources":["../../src/commands/activate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function showConfig(): void;
@@ -0,0 +1,35 @@
1
+ import { existsSync } from "node:fs";
2
+ import { loadConfig } from "../lib/config.js";
3
+ import { log } from "../lib/logger.js";
4
+ import { CONFIG_PATH } from "../lib/paths.js";
5
+ import { bold, dim, reset } from "../lib/styles.js";
6
+ import { box, table } from "../lib/ui.js";
7
+ export function showConfig() {
8
+ if (!existsSync(CONFIG_PATH)) {
9
+ log.warn("No config file found. Run `forge activate <key>` to create one.");
10
+ return;
11
+ }
12
+ const config = loadConfig();
13
+ const maskedKey = config.license_key
14
+ ? `${config.license_key.slice(0, 8)}..${config.license_key.slice(-4)}`
15
+ : "(not set)";
16
+ console.log(box([
17
+ `${bold}License:${reset} ${maskedKey}`,
18
+ `${bold}Machine ID:${reset} ${config.machine_id}`,
19
+ `${bold}API URL:${reset} ${config.api_url}`,
20
+ `${bold}Config:${reset} ${dim}${CONFIG_PATH}${reset}`,
21
+ ], { title: "Forge Config", borderColor: dim }));
22
+ const plugins = Object.entries(config.installed_plugins);
23
+ if (plugins.length > 0) {
24
+ const rows = plugins.map(([name, info]) => [
25
+ name,
26
+ `v${info.version}`,
27
+ dim + new Date(info.installed_at).toLocaleDateString() + reset,
28
+ ]);
29
+ console.log(table(rows, { header: ["Plugin", "Version", "Installed"] }));
30
+ }
31
+ else {
32
+ log.info("No plugins installed.");
33
+ }
34
+ }
35
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAE1C,MAAM,UAAU,UAAU;IACxB,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC7B,GAAG,CAAC,IAAI,CAAC,iEAAiE,CAAC,CAAC;QAC5E,OAAO;IACT,CAAC;IAED,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAE5B,MAAM,SAAS,GAAG,MAAM,CAAC,WAAW;QAClC,CAAC,CAAC,GAAG,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE;QACtE,CAAC,CAAC,WAAW,CAAC;IAEhB,OAAO,CAAC,GAAG,CACT,GAAG,CACD;QACE,GAAG,IAAI,WAAW,KAAK,OAAO,SAAS,EAAE;QACzC,GAAG,IAAI,cAAc,KAAK,IAAI,MAAM,CAAC,UAAU,EAAE;QACjD,GAAG,IAAI,WAAW,KAAK,OAAO,MAAM,CAAC,OAAO,EAAE;QAC9C,GAAG,IAAI,UAAU,KAAK,QAAQ,GAAG,GAAG,WAAW,GAAG,KAAK,EAAE;KAC1D,EACD,EAAE,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,GAAG,EAAE,CAC5C,CACF,CAAC;IAEF,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;IACzD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC;YACzC,IAAI;YACJ,IAAI,IAAI,CAAC,OAAO,EAAE;YAClB,GAAG,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,kBAAkB,EAAE,GAAG,KAAK;SAC/D,CAAC,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3E,CAAC;SAAM,CAAC;QACN,GAAG,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IACpC,CAAC;AACH,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function dashboard(): Promise<void>;