@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
@@ -0,0 +1,31 @@
1
+ import { existsSync, rmSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { loadConfig, saveConfig } from "../lib/config.js";
4
+ import { log } from "../lib/logger.js";
5
+ import { CACHE_DIR, claudePluginDir } from "../lib/paths.js";
6
+ export function uninstall(pluginName) {
7
+ const fullName = pluginName.startsWith("forge-") ? pluginName : `forge-${pluginName}`;
8
+ const config = loadConfig();
9
+ if (!config.installed_plugins[fullName]) {
10
+ log.error(`${fullName} is not installed.`);
11
+ process.exit(1);
12
+ }
13
+ const version = config.installed_plugins[fullName].version;
14
+ // Remove from Claude Code plugins directory
15
+ const pluginPath = join(claudePluginDir(), fullName);
16
+ if (existsSync(pluginPath)) {
17
+ rmSync(pluginPath, { recursive: true });
18
+ log.step(`Removed ${pluginPath}`);
19
+ }
20
+ // Remove cached archive
21
+ const cachePath = join(CACHE_DIR, `${fullName}@${version}`);
22
+ if (existsSync(cachePath)) {
23
+ rmSync(cachePath, { recursive: true });
24
+ log.step(`Cleared cache ${cachePath}`);
25
+ }
26
+ // Update config
27
+ delete config.installed_plugins[fullName];
28
+ saveConfig(config);
29
+ log.success(`${fullName}@${version} uninstalled.`);
30
+ }
31
+ //# sourceMappingURL=uninstall.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uninstall.js","sourceRoot":"","sources":["../../src/commands/uninstall.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAE7D,MAAM,UAAU,SAAS,CAAC,UAAkB;IAC1C,MAAM,QAAQ,GAAG,UAAU,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,UAAU,EAAE,CAAC;IACtF,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAE5B,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC;QACxC,GAAG,CAAC,KAAK,CAAC,GAAG,QAAQ,oBAAoB,CAAC,CAAC;QAC3C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC;IAE3D,4CAA4C;IAC5C,MAAM,UAAU,GAAG,IAAI,CAAC,eAAe,EAAE,EAAE,QAAQ,CAAC,CAAC;IACrD,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3B,MAAM,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACxC,GAAG,CAAC,IAAI,CAAC,WAAW,UAAU,EAAE,CAAC,CAAC;IACpC,CAAC;IAED,wBAAwB;IACxB,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,EAAE,GAAG,QAAQ,IAAI,OAAO,EAAE,CAAC,CAAC;IAC5D,IAAI,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC1B,MAAM,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACvC,GAAG,CAAC,IAAI,CAAC,iBAAiB,SAAS,EAAE,CAAC,CAAC;IACzC,CAAC;IAED,gBAAgB;IAChB,OAAO,MAAM,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAC1C,UAAU,CAAC,MAAM,CAAC,CAAC;IAEnB,GAAG,CAAC,OAAO,CAAC,GAAG,QAAQ,IAAI,OAAO,eAAe,CAAC,CAAC;AACrD,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function update(pluginName?: string): Promise<void>;
@@ -0,0 +1,30 @@
1
+ import { loadConfig } from "../lib/config.js";
2
+ import { log } from "../lib/logger.js";
3
+ import { install } from "./install.js";
4
+ export async function update(pluginName) {
5
+ const config = loadConfig();
6
+ if (!config.license_key) {
7
+ log.error("No license key configured. Run `forge activate <key>` first.");
8
+ process.exit(1);
9
+ }
10
+ const installed = Object.keys(config.installed_plugins);
11
+ if (installed.length === 0) {
12
+ log.warn("No plugins installed. Run `forge install <plugin>` first.");
13
+ return;
14
+ }
15
+ if (pluginName) {
16
+ const fullName = pluginName.startsWith("forge-") ? pluginName : `forge-${pluginName}`;
17
+ if (!config.installed_plugins[fullName]) {
18
+ log.error(`${fullName} is not installed. Run \`forge install ${pluginName}\` first.`);
19
+ process.exit(1);
20
+ }
21
+ await install(fullName);
22
+ }
23
+ else {
24
+ log.header(`Updating ${installed.length} plugin(s)...`);
25
+ for (const name of installed) {
26
+ await install(name);
27
+ }
28
+ }
29
+ }
30
+ //# sourceMappingURL=update.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update.js","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,UAAmB;IAC9C,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAE5B,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;QACxB,GAAG,CAAC,KAAK,CAAC,8DAA8D,CAAC,CAAC;QAC1E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAExD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,GAAG,CAAC,IAAI,CAAC,2DAA2D,CAAC,CAAC;QACtE,OAAO;IACT,CAAC;IAED,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,QAAQ,GAAG,UAAU,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,UAAU,EAAE,CAAC;QACtF,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC;YACxC,GAAG,CAAC,KAAK,CAAC,GAAG,QAAQ,0CAA0C,UAAU,WAAW,CAAC,CAAC;YACtF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC1B,CAAC;SAAM,CAAC;QACN,GAAG,CAAC,MAAM,CAAC,YAAY,SAAS,CAAC,MAAM,eAAe,CAAC,CAAC;QACxD,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;YAC7B,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { ForgeConfig } from "../types.js";
2
+ export declare class ApiError extends Error {
3
+ statusCode: number;
4
+ code: string;
5
+ constructor(statusCode: number, code: string, message: string);
6
+ }
7
+ interface RequestOptions {
8
+ method: "GET" | "POST";
9
+ path: string;
10
+ body?: Record<string, unknown>;
11
+ headers?: Record<string, string>;
12
+ query?: Record<string, string>;
13
+ }
14
+ export declare function apiRequest<T>(config: ForgeConfig, opts: RequestOptions): Promise<T>;
15
+ /** Download a file from a URL to a buffer. */
16
+ export declare function downloadFile(url: string): Promise<Buffer>;
17
+ export {};
@@ -0,0 +1,53 @@
1
+ export class ApiError extends Error {
2
+ statusCode;
3
+ code;
4
+ constructor(statusCode, code, message) {
5
+ super(message);
6
+ this.statusCode = statusCode;
7
+ this.code = code;
8
+ this.name = "ApiError";
9
+ }
10
+ }
11
+ export async function apiRequest(config, opts) {
12
+ const base = config.api_url.replace(/\/+$/, "");
13
+ const url = new URL(`${base}${opts.path}`);
14
+ if (opts.query) {
15
+ for (const [k, v] of Object.entries(opts.query)) {
16
+ url.searchParams.set(k, v);
17
+ }
18
+ }
19
+ const headers = {
20
+ ...opts.headers,
21
+ };
22
+ if (config.license_key) {
23
+ headers["x-license-key"] = config.license_key;
24
+ }
25
+ if (config.machine_id) {
26
+ headers["x-machine-id"] = config.machine_id;
27
+ }
28
+ let body;
29
+ if (opts.body) {
30
+ headers["content-type"] = "application/json";
31
+ body = JSON.stringify(opts.body);
32
+ }
33
+ const response = await fetch(url.toString(), {
34
+ method: opts.method,
35
+ headers,
36
+ body,
37
+ });
38
+ const data = (await response.json());
39
+ if (!response.ok) {
40
+ throw new ApiError(response.status, data.error ?? "UNKNOWN_ERROR", data.message ?? `API error ${response.status}`);
41
+ }
42
+ return data;
43
+ }
44
+ /** Download a file from a URL to a buffer. */
45
+ export async function downloadFile(url) {
46
+ const response = await fetch(url);
47
+ if (!response.ok) {
48
+ throw new Error(`Download failed: ${response.status} ${response.statusText}`);
49
+ }
50
+ const arrayBuffer = await response.arrayBuffer();
51
+ return Buffer.from(arrayBuffer);
52
+ }
53
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/lib/api.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,QAAS,SAAQ,KAAK;IAExB;IACA;IAFT,YACS,UAAkB,EAClB,IAAY,EACnB,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAC;QAJR,eAAU,GAAV,UAAU,CAAQ;QAClB,SAAI,GAAJ,IAAI,CAAQ;QAInB,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IACzB,CAAC;CACF;AAUD,MAAM,CAAC,KAAK,UAAU,UAAU,CAAI,MAAmB,EAAE,IAAoB;IAC3E,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAChD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAE3C,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAChD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAA2B;QACtC,GAAG,IAAI,CAAC,OAAO;KAChB,CAAC;IAEF,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;QACvB,OAAO,CAAC,eAAe,CAAC,GAAG,MAAM,CAAC,WAAW,CAAC;IAChD,CAAC;IACD,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QACtB,OAAO,CAAC,cAAc,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC;IAC9C,CAAC;IAED,IAAI,IAAwB,CAAC;IAC7B,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAC;QAC7C,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;QAC3C,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,OAAO;QACP,IAAI;KACL,CAAC,CAAC;IAEH,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA6C,CAAC;IAEjF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,QAAQ,CAChB,QAAQ,CAAC,MAAM,EACf,IAAI,CAAC,KAAK,IAAI,eAAe,EAC7B,IAAI,CAAC,OAAO,IAAI,aAAa,QAAQ,CAAC,MAAM,EAAE,CAC/C,CAAC;IACJ,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,8CAA8C;AAC9C,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAW;IAC5C,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;IAElC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,oBAAoB,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IAChF,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC;IACjD,OAAO,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;AAClC,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { ForgeConfig } from "../types.js";
2
+ export declare function loadConfig(): ForgeConfig;
3
+ export declare function saveConfig(config: ForgeConfig): void;
4
+ export declare function ensureForgeDir(): void;
@@ -0,0 +1,40 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { dirname } from "node:path";
3
+ import { getMachineId } from "./machine-id.js";
4
+ import { CONFIG_PATH, FORGE_DIR } from "./paths.js";
5
+ const DEFAULT_API_URL = "https://api.reumbra.com/velvet";
6
+ function defaultConfig() {
7
+ return {
8
+ license_key: null,
9
+ machine_id: getMachineId(),
10
+ api_url: process.env.FORGE_API_URL ?? DEFAULT_API_URL,
11
+ installed_plugins: {},
12
+ };
13
+ }
14
+ export function loadConfig() {
15
+ if (!existsSync(CONFIG_PATH)) {
16
+ return defaultConfig();
17
+ }
18
+ try {
19
+ const raw = readFileSync(CONFIG_PATH, "utf-8");
20
+ const parsed = JSON.parse(raw);
21
+ const defaults = defaultConfig();
22
+ return {
23
+ license_key: parsed.license_key ?? defaults.license_key,
24
+ machine_id: parsed.machine_id ?? defaults.machine_id,
25
+ api_url: parsed.api_url ?? defaults.api_url,
26
+ installed_plugins: parsed.installed_plugins ?? defaults.installed_plugins,
27
+ };
28
+ }
29
+ catch {
30
+ return defaultConfig();
31
+ }
32
+ }
33
+ export function saveConfig(config) {
34
+ mkdirSync(dirname(CONFIG_PATH), { recursive: true });
35
+ writeFileSync(CONFIG_PATH, `${JSON.stringify(config, null, 2)}\n`, "utf-8");
36
+ }
37
+ export function ensureForgeDir() {
38
+ mkdirSync(FORGE_DIR, { recursive: true });
39
+ }
40
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEpD,MAAM,eAAe,GAAG,gCAAgC,CAAC;AAEzD,SAAS,aAAa;IACpB,OAAO;QACL,WAAW,EAAE,IAAI;QACjB,UAAU,EAAE,YAAY,EAAE;QAC1B,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,eAAe;QACrD,iBAAiB,EAAE,EAAE;KACtB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC7B,OAAO,aAAa,EAAE,CAAC;IACzB,CAAC;IAED,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAyB,CAAC;QACvD,MAAM,QAAQ,GAAG,aAAa,EAAE,CAAC;QACjC,OAAO;YACL,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,QAAQ,CAAC,WAAW;YACvD,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,QAAQ,CAAC,UAAU;YACpD,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,QAAQ,CAAC,OAAO;YAC3C,iBAAiB,EAAE,MAAM,CAAC,iBAAiB,IAAI,QAAQ,CAAC,iBAAiB;SAC1E,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,aAAa,EAAE,CAAC;IACzB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAmB;IAC5C,SAAS,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,aAAa,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC9E,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAC5C,CAAC"}
@@ -0,0 +1,11 @@
1
+ export declare const log: {
2
+ info(msg: string): void;
3
+ success(msg: string): void;
4
+ warn(msg: string): void;
5
+ error(msg: string): void;
6
+ step(msg: string): void;
7
+ header(msg: string): void;
8
+ plain(msg: string): void;
9
+ /** Simple key-value table (no borders). Use ui.table() for bordered tables. */
10
+ table(rows: string[][]): void;
11
+ };
@@ -0,0 +1,40 @@
1
+ import { bold, cyan, dim, green, red, reset, visualLength, yellow } from "./styles.js";
2
+ export const log = {
3
+ info(msg) {
4
+ console.log(`${cyan}i${reset} ${msg}`);
5
+ },
6
+ success(msg) {
7
+ console.log(`${green}✓${reset} ${msg}`);
8
+ },
9
+ warn(msg) {
10
+ console.log(`${yellow}!${reset} ${msg}`);
11
+ },
12
+ error(msg) {
13
+ console.error(`${red}✗${reset} ${msg}`);
14
+ },
15
+ step(msg) {
16
+ console.log(`${dim}→${reset} ${msg}`);
17
+ },
18
+ header(msg) {
19
+ console.log(`\n${bold}${msg}${reset}`);
20
+ },
21
+ plain(msg) {
22
+ console.log(msg);
23
+ },
24
+ /** Simple key-value table (no borders). Use ui.table() for bordered tables. */
25
+ table(rows) {
26
+ if (rows.length === 0)
27
+ return;
28
+ const widths = rows[0].map((_, i) => Math.max(...rows.map((r) => visualLength(r[i] ?? ""))));
29
+ for (const row of rows) {
30
+ const line = row
31
+ .map((cell, i) => {
32
+ const pad = widths[i] - visualLength(cell);
33
+ return cell + " ".repeat(Math.max(0, pad));
34
+ })
35
+ .join(" ");
36
+ console.log(` ${line}`);
37
+ }
38
+ },
39
+ };
40
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/lib/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAEvF,MAAM,CAAC,MAAM,GAAG,GAAG;IACjB,IAAI,CAAC,GAAW;QACd,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,IAAI,KAAK,IAAI,GAAG,EAAE,CAAC,CAAC;IACzC,CAAC;IAED,OAAO,CAAC,GAAW;QACjB,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,IAAI,KAAK,IAAI,GAAG,EAAE,CAAC,CAAC;IAC1C,CAAC;IAED,IAAI,CAAC,GAAW;QACd,OAAO,CAAC,GAAG,CAAC,GAAG,MAAM,IAAI,KAAK,IAAI,GAAG,EAAE,CAAC,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,GAAW;QACf,OAAO,CAAC,KAAK,CAAC,GAAG,GAAG,IAAI,KAAK,IAAI,GAAG,EAAE,CAAC,CAAC;IAC1C,CAAC;IAED,IAAI,CAAC,GAAW;QACd,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,IAAI,KAAK,IAAI,GAAG,EAAE,CAAC,CAAC;IACxC,CAAC;IAED,MAAM,CAAC,GAAW;QAChB,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,GAAG,GAAG,GAAG,KAAK,EAAE,CAAC,CAAC;IACzC,CAAC;IAED,KAAK,CAAC,GAAW;QACf,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC;IAED,+EAA+E;IAC/E,KAAK,CAAC,IAAgB;QACpB,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAC9B,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7F,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,IAAI,GAAG,GAAG;iBACb,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;gBACf,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;gBAC3C,OAAO,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;YAC7C,CAAC,CAAC;iBACD,IAAI,CAAC,IAAI,CAAC,CAAC;YACd,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;CACF,CAAC"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Deterministic machine ID: SHA256(hostname + platform + username).
3
+ * Same machine always produces the same ID.
4
+ */
5
+ export declare function getMachineId(): string;
@@ -0,0 +1,11 @@
1
+ import { createHash } from "node:crypto";
2
+ import { hostname, platform, userInfo } from "node:os";
3
+ /**
4
+ * Deterministic machine ID: SHA256(hostname + platform + username).
5
+ * Same machine always produces the same ID.
6
+ */
7
+ export function getMachineId() {
8
+ const raw = `${hostname()}|${platform()}|${userInfo().username}`;
9
+ return createHash("sha256").update(raw).digest("hex").slice(0, 32);
10
+ }
11
+ //# sourceMappingURL=machine-id.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"machine-id.js","sourceRoot":"","sources":["../../src/lib/machine-id.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEvD;;;GAGG;AACH,MAAM,UAAU,YAAY;IAC1B,MAAM,GAAG,GAAG,GAAG,QAAQ,EAAE,IAAI,QAAQ,EAAE,IAAI,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC;IACjE,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACrE,CAAC"}
@@ -0,0 +1,8 @@
1
+ /** Root: ~/.forge/ */
2
+ export declare const FORGE_DIR: string;
3
+ /** Config: ~/.forge/config.json */
4
+ export declare const CONFIG_PATH: string;
5
+ /** Cache dir: ~/.forge/cache/ */
6
+ export declare const CACHE_DIR: string;
7
+ /** Claude Code plugin directory */
8
+ export declare function claudePluginDir(): string;
@@ -0,0 +1,13 @@
1
+ import { homedir } from "node:os";
2
+ import { join } from "node:path";
3
+ /** Root: ~/.forge/ */
4
+ export const FORGE_DIR = join(homedir(), ".forge");
5
+ /** Config: ~/.forge/config.json */
6
+ export const CONFIG_PATH = join(FORGE_DIR, "config.json");
7
+ /** Cache dir: ~/.forge/cache/ */
8
+ export const CACHE_DIR = join(FORGE_DIR, "cache");
9
+ /** Claude Code plugin directory */
10
+ export function claudePluginDir() {
11
+ return join(homedir(), ".claude", "plugins");
12
+ }
13
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/lib/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,sBAAsB;AACtB,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;AAEnD,mCAAmC;AACnC,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;AAE1D,iCAAiC;AACjC,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;AAElD,mCAAmC;AACnC,MAAM,UAAU,eAAe;IAC7B,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,32 @@
1
+ export declare const reset: string;
2
+ export declare const bold: string;
3
+ export declare const dim: string;
4
+ export declare const italic: string;
5
+ export declare const underline: string;
6
+ export declare const inverse: string;
7
+ export declare const strikethrough: string;
8
+ export declare const black: string;
9
+ export declare const red: string;
10
+ export declare const green: string;
11
+ export declare const yellow: string;
12
+ export declare const blue: string;
13
+ export declare const magenta: string;
14
+ export declare const cyan: string;
15
+ export declare const white: string;
16
+ export declare const gray: string;
17
+ export declare const brightRed: string;
18
+ export declare const brightGreen: string;
19
+ export declare const brightYellow: string;
20
+ export declare const brightBlue: string;
21
+ export declare const brightMagenta: string;
22
+ export declare const brightCyan: string;
23
+ export declare const bgRed: string;
24
+ export declare const bgGreen: string;
25
+ export declare const bgYellow: string;
26
+ export declare const bgBlue: string;
27
+ export declare const bgMagenta: string;
28
+ export declare const bgCyan: string;
29
+ export declare const bgWhite: string;
30
+ export declare const bgGray: string;
31
+ export declare function stripAnsi(str: string): string;
32
+ export declare function visualLength(str: string): number;
@@ -0,0 +1,50 @@
1
+ // ANSI escape codes — zero dependency styling
2
+ const isColorSupported = process.env.NO_COLOR == null && process.env.TERM !== "dumb";
3
+ function code(n) {
4
+ return isColorSupported ? `\x1b[${n}m` : "";
5
+ }
6
+ // Reset
7
+ export const reset = code("0");
8
+ // Modifiers
9
+ export const bold = code("1");
10
+ export const dim = code("2");
11
+ export const italic = code("3");
12
+ export const underline = code("4");
13
+ export const inverse = code("7");
14
+ export const strikethrough = code("9");
15
+ // Colors
16
+ export const black = code("30");
17
+ export const red = code("31");
18
+ export const green = code("32");
19
+ export const yellow = code("33");
20
+ export const blue = code("34");
21
+ export const magenta = code("35");
22
+ export const cyan = code("36");
23
+ export const white = code("37");
24
+ export const gray = code("90");
25
+ // Bright colors
26
+ export const brightRed = code("91");
27
+ export const brightGreen = code("92");
28
+ export const brightYellow = code("93");
29
+ export const brightBlue = code("94");
30
+ export const brightMagenta = code("95");
31
+ export const brightCyan = code("96");
32
+ // Background colors
33
+ export const bgRed = code("41");
34
+ export const bgGreen = code("42");
35
+ export const bgYellow = code("43");
36
+ export const bgBlue = code("44");
37
+ export const bgMagenta = code("45");
38
+ export const bgCyan = code("46");
39
+ export const bgWhite = code("47");
40
+ export const bgGray = code("100");
41
+ // Strip ANSI codes for length calculation
42
+ // biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI escape sequences require control chars
43
+ const ANSI_REGEX = /\x1b\[\d+m/g;
44
+ export function stripAnsi(str) {
45
+ return str.replace(ANSI_REGEX, "");
46
+ }
47
+ export function visualLength(str) {
48
+ return stripAnsi(str).length;
49
+ }
50
+ //# sourceMappingURL=styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.js","sourceRoot":"","sources":["../../src/lib/styles.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ export interface BoxOptions {
2
+ title?: string;
3
+ padding?: number;
4
+ borderColor?: string;
5
+ width?: number;
6
+ }
7
+ export declare function box(lines: string[], opts?: BoxOptions): string;
8
+ export interface Spinner {
9
+ update(msg: string): void;
10
+ stop(finalMsg?: string): void;
11
+ }
12
+ export declare function createSpinner(initialMsg: string): Spinner;
13
+ export declare function progressBar(current: number, total: number, width?: number): string;
14
+ export declare function badge(label: string, variant: "success" | "warning" | "error" | "info" | "neutral"): string;
15
+ export declare function statusBadge(status: "active" | "expiring" | "expired" | "inactive"): string;
16
+ export interface TableOptions {
17
+ header?: string[];
18
+ borderColor?: string;
19
+ }
20
+ export declare function table(rows: string[][], opts?: TableOptions): string;
21
+ export declare function divider(width?: number): string;
22
+ export declare function banner(): string;
package/dist/lib/ui.js ADDED
@@ -0,0 +1,131 @@
1
+ import { bgBlue, bgGreen, bgRed, bgYellow, black, bold, brightCyan, cyan, dim, green, magenta, reset, visualLength, white, } from "./styles.js";
2
+ // ─── Box Drawing ─────────────────────────────────────────────
3
+ const BOX = { tl: "╭", tr: "╮", bl: "╰", br: "╯", h: "─", v: "│" };
4
+ export function box(lines, opts = {}) {
5
+ const { title, padding = 1, borderColor = dim, width: fixedWidth } = opts;
6
+ const pad = " ".repeat(padding);
7
+ const contentWidth = fixedWidth ??
8
+ Math.max(...lines.map((l) => visualLength(l)), title ? visualLength(title) + 2 : 0);
9
+ const innerWidth = contentWidth + padding * 2;
10
+ const bc = borderColor;
11
+ const topLabel = title ? ` ${bold}${title}${reset}${bc} ` : "";
12
+ const topLabelLen = title ? visualLength(title) + 2 : 0;
13
+ const topLine = `${bc}${BOX.tl}${BOX.h.repeat(topLabelLen ? 1 : 0)}${topLabel}${BOX.h.repeat(innerWidth - topLabelLen - (topLabelLen ? 1 : 0))}${BOX.tr}${reset}`;
14
+ const bottomLine = `${bc}${BOX.bl}${BOX.h.repeat(innerWidth)}${BOX.br}${reset}`;
15
+ const body = lines.map((line) => {
16
+ const padRight = contentWidth - visualLength(line);
17
+ return `${bc}${BOX.v}${reset}${pad}${line}${" ".repeat(Math.max(0, padRight))}${pad}${bc}${BOX.v}${reset}`;
18
+ });
19
+ return [topLine, ...body, bottomLine].join("\n");
20
+ }
21
+ // ─── Spinner ─────────────────────────────────────────────────
22
+ const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
23
+ export function createSpinner(initialMsg) {
24
+ let frame = 0;
25
+ let message = initialMsg;
26
+ let stopped = false;
27
+ const interval = setInterval(() => {
28
+ if (stopped)
29
+ return;
30
+ const icon = SPINNER_FRAMES[frame % SPINNER_FRAMES.length];
31
+ process.stdout.write(`\r${cyan}${icon}${reset} ${message}\x1b[K`);
32
+ frame++;
33
+ }, 80);
34
+ return {
35
+ update(msg) {
36
+ message = msg;
37
+ },
38
+ stop(finalMsg) {
39
+ stopped = true;
40
+ clearInterval(interval);
41
+ process.stdout.write("\r\x1b[K");
42
+ if (finalMsg) {
43
+ process.stdout.write(`${finalMsg}\n`);
44
+ }
45
+ },
46
+ };
47
+ }
48
+ // ─── Progress Bar ────────────────────────────────────────────
49
+ export function progressBar(current, total, width = 30) {
50
+ const ratio = Math.min(current / total, 1);
51
+ const filled = Math.round(width * ratio);
52
+ const empty = width - filled;
53
+ const bar = `${green}${"█".repeat(filled)}${dim}${"░".repeat(empty)}${reset}`;
54
+ const pct = `${Math.round(ratio * 100)}%`;
55
+ return `${bar} ${pct}`;
56
+ }
57
+ // ─── Badges ──────────────────────────────────────────────────
58
+ export function badge(label, variant) {
59
+ const styles = {
60
+ success: `${bgGreen}${black}`,
61
+ warning: `${bgYellow}${black}`,
62
+ error: `${bgRed}${white}`,
63
+ info: `${bgBlue}${white}`,
64
+ neutral: `${dim}`,
65
+ };
66
+ return `${styles[variant]} ${label} ${reset}`;
67
+ }
68
+ export function statusBadge(status) {
69
+ const map = {
70
+ active: { label: "✓ active", variant: "success" },
71
+ expiring: { label: "⚠ expiring", variant: "warning" },
72
+ expired: { label: "✗ expired", variant: "error" },
73
+ inactive: { label: "○ inactive", variant: "neutral" },
74
+ };
75
+ const { label, variant } = map[status] ?? map.inactive;
76
+ return badge(label, variant);
77
+ }
78
+ export function table(rows, opts = {}) {
79
+ if (rows.length === 0)
80
+ return "";
81
+ const { header, borderColor = dim } = opts;
82
+ const allRows = header ? [header, ...rows] : rows;
83
+ const colCount = Math.max(...allRows.map((r) => r.length));
84
+ const widths = [];
85
+ for (let i = 0; i < colCount; i++) {
86
+ widths.push(Math.max(...allRows.map((r) => visualLength(r[i] ?? ""))));
87
+ }
88
+ const bc = borderColor;
89
+ const line = (left, mid, right, fill) => `${bc}${left}${widths.map((w) => fill.repeat(w + 2)).join(mid)}${right}${reset}`;
90
+ const row = (cells) => {
91
+ const padded = cells.map((c, i) => {
92
+ const pad = widths[i] - visualLength(c);
93
+ return ` ${c}${" ".repeat(Math.max(0, pad))} `;
94
+ });
95
+ return `${bc}│${reset}${padded.join(`${bc}│${reset}`)}${bc}│${reset}`;
96
+ };
97
+ const result = [];
98
+ result.push(line("╭", "┬", "╮", "─"));
99
+ if (header) {
100
+ result.push(row(header.map((h) => `${bold}${h}${reset}`)));
101
+ result.push(line("├", "┼", "┤", "─"));
102
+ for (const r of rows) {
103
+ result.push(row(r));
104
+ }
105
+ }
106
+ else {
107
+ for (const r of allRows) {
108
+ result.push(row(r));
109
+ }
110
+ }
111
+ result.push(line("╰", "┴", "╯", "─"));
112
+ return result.join("\n");
113
+ }
114
+ // ─── Divider ─────────────────────────────────────────────────
115
+ export function divider(width = 50) {
116
+ return `${dim}${"─".repeat(width)}${reset}`;
117
+ }
118
+ // ─── Banner ──────────────────────────────────────────────────
119
+ export function banner() {
120
+ const gradient = [magenta, brightCyan, cyan];
121
+ const text = " ⚒ Forge";
122
+ const letters = [...text];
123
+ const colored = letters
124
+ .map((ch, i) => {
125
+ const color = gradient[Math.floor((i / letters.length) * gradient.length)];
126
+ return `${color}${ch}`;
127
+ })
128
+ .join("");
129
+ return `${bold}${colored}${reset} ${dim}— Plugin manager for Claude Code${reset}`;
130
+ }
131
+ //# sourceMappingURL=ui.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui.js","sourceRoot":"","sources":["../../src/lib/ui.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Extract a zip buffer to a directory.
3
+ * Uses Node.js built-in zlib — works for simple zip files.
4
+ * For complex zips (multiple entries), we parse the zip format manually.
5
+ */
6
+ export declare function extractZip(zipBuffer: Buffer, destDir: string): Promise<void>;
@@ -0,0 +1,56 @@
1
+ import { createWriteStream, mkdirSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { Readable } from "node:stream";
4
+ import { pipeline } from "node:stream/promises";
5
+ import { createInflateRaw } from "node:zlib";
6
+ /**
7
+ * Extract a zip buffer to a directory.
8
+ * Uses Node.js built-in zlib — works for simple zip files.
9
+ * For complex zips (multiple entries), we parse the zip format manually.
10
+ */
11
+ export async function extractZip(zipBuffer, destDir) {
12
+ mkdirSync(destDir, { recursive: true });
13
+ // Parse ZIP Central Directory
14
+ const entries = parseZipEntries(zipBuffer);
15
+ for (const entry of entries) {
16
+ const fullPath = join(destDir, entry.name);
17
+ if (entry.name.endsWith("/")) {
18
+ mkdirSync(fullPath, { recursive: true });
19
+ continue;
20
+ }
21
+ // Ensure parent dir exists
22
+ mkdirSync(join(fullPath, ".."), { recursive: true });
23
+ if (entry.compressionMethod === 0) {
24
+ // Stored (no compression)
25
+ const data = zipBuffer.subarray(entry.dataOffset, entry.dataOffset + entry.compressedSize);
26
+ const readable = Readable.from(data);
27
+ await pipeline(readable, createWriteStream(fullPath));
28
+ }
29
+ else if (entry.compressionMethod === 8) {
30
+ // Deflated
31
+ const data = zipBuffer.subarray(entry.dataOffset, entry.dataOffset + entry.compressedSize);
32
+ const readable = Readable.from(data);
33
+ const inflate = createInflateRaw();
34
+ await pipeline(readable, inflate, createWriteStream(fullPath));
35
+ }
36
+ }
37
+ }
38
+ function parseZipEntries(buf) {
39
+ const entries = [];
40
+ let offset = 0;
41
+ while (offset < buf.length - 4) {
42
+ const sig = buf.readUInt32LE(offset);
43
+ if (sig !== 0x04034b50)
44
+ break; // Local file header signature
45
+ const compressionMethod = buf.readUInt16LE(offset + 8);
46
+ const compressedSize = buf.readUInt32LE(offset + 18);
47
+ const nameLen = buf.readUInt16LE(offset + 26);
48
+ const extraLen = buf.readUInt16LE(offset + 28);
49
+ const name = buf.subarray(offset + 30, offset + 30 + nameLen).toString("utf-8");
50
+ const dataOffset = offset + 30 + nameLen + extraLen;
51
+ entries.push({ name, compressionMethod, compressedSize, dataOffset });
52
+ offset = dataOffset + compressedSize;
53
+ }
54
+ return entries;
55
+ }
56
+ //# sourceMappingURL=zip.js.map