@gtkx/cli 1.0.0-rc.1 → 1.0.0-rc.2
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/dist/builder.d.ts +3 -2
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +2 -1
- package/dist/builder.js.map +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +21 -6
- package/dist/cli.js.map +1 -1
- package/dist/codegen/freshness.d.ts +5 -5
- package/dist/codegen/freshness.d.ts.map +1 -1
- package/dist/codegen/freshness.js +26 -23
- package/dist/codegen/freshness.js.map +1 -1
- package/dist/codegen/report.d.ts +2 -1
- package/dist/codegen/report.d.ts.map +1 -1
- package/dist/codegen/report.js +4 -2
- package/dist/codegen/report.js.map +1 -1
- package/dist/codegen/run-codegen.d.ts +8 -8
- package/dist/codegen/run-codegen.d.ts.map +1 -1
- package/dist/codegen/run-codegen.js +72 -43
- package/dist/codegen/run-codegen.js.map +1 -1
- package/dist/codegen/store-resolver.d.ts +8 -5
- package/dist/codegen/store-resolver.d.ts.map +1 -1
- package/dist/codegen/store-resolver.js +26 -6
- package/dist/codegen/store-resolver.js.map +1 -1
- package/dist/commands/build.d.ts +4 -3
- package/dist/commands/build.d.ts.map +1 -1
- package/dist/commands/build.js +2 -1
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/codegen.d.ts +2 -1
- package/dist/commands/codegen.d.ts.map +1 -1
- package/dist/commands/codegen.js +6 -4
- package/dist/commands/codegen.js.map +1 -1
- package/dist/commands/dev.d.ts +2 -1
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +2 -1
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/docs.d.ts +2 -1
- package/dist/commands/docs.d.ts.map +1 -1
- package/dist/commands/docs.js +22 -7
- package/dist/commands/docs.js.map +1 -1
- package/dist/dev/icon-dir.d.ts +2 -1
- package/dist/dev/icon-dir.d.ts.map +1 -1
- package/dist/dev/icon-dir.js +6 -3
- package/dist/dev/icon-dir.js.map +1 -1
- package/dist/dev/refresh-tracker.d.ts +5 -4
- package/dist/dev/refresh-tracker.d.ts.map +1 -1
- package/dist/dev/refresh-tracker.js +6 -5
- package/dist/dev/refresh-tracker.js.map +1 -1
- package/dist/dev/runner-deps.d.ts +2 -1
- package/dist/dev/runner-deps.d.ts.map +1 -1
- package/dist/dev/runner-deps.js +20 -4
- package/dist/dev/runner-deps.js.map +1 -1
- package/dist/dev/runner-main.d.ts +2 -1
- package/dist/dev/runner-main.d.ts.map +1 -1
- package/dist/dev/runner-main.js +3 -2
- package/dist/dev/runner-main.js.map +1 -1
- package/dist/dev/runner.d.ts +6 -5
- package/dist/dev/runner.d.ts.map +1 -1
- package/dist/dev/runner.js +69 -38
- package/dist/dev/runner.js.map +1 -1
- package/dist/dev/schema-dir.d.ts +2 -1
- package/dist/dev/schema-dir.d.ts.map +1 -1
- package/dist/dev/schema-dir.js +4 -2
- package/dist/dev/schema-dir.js.map +1 -1
- package/dist/dev/supervisor.d.ts +7 -6
- package/dist/dev/supervisor.d.ts.map +1 -1
- package/dist/dev/supervisor.js +107 -67
- package/dist/dev/supervisor.js.map +1 -1
- package/dist/dev/vite-dev-server.d.ts +3 -3
- package/dist/dev/vite-dev-server.d.ts.map +1 -1
- package/dist/dev/vite-dev-server.js +2 -1
- package/dist/dev/vite-dev-server.js.map +1 -1
- package/dist/internal/banner.d.ts +2 -1
- package/dist/internal/banner.d.ts.map +1 -1
- package/dist/internal/banner.js +2 -1
- package/dist/internal/banner.js.map +1 -1
- package/dist/internal/data-dir.d.ts +3 -2
- package/dist/internal/data-dir.d.ts.map +1 -1
- package/dist/internal/data-dir.js +29 -14
- package/dist/internal/data-dir.js.map +1 -1
- package/dist/internal/entry-arg.d.ts +5 -4
- package/dist/internal/entry-arg.d.ts.map +1 -1
- package/dist/internal/entry-arg.js +10 -7
- package/dist/internal/entry-arg.js.map +1 -1
- package/dist/internal/errors.d.ts +3 -2
- package/dist/internal/errors.d.ts.map +1 -1
- package/dist/internal/errors.js +7 -5
- package/dist/internal/errors.js.map +1 -1
- package/dist/internal/list-files.d.ts +3 -2
- package/dist/internal/list-files.d.ts.map +1 -1
- package/dist/internal/list-files.js +5 -3
- package/dist/internal/list-files.js.map +1 -1
- package/dist/internal/resolve-cli-tool.d.ts +2 -1
- package/dist/internal/resolve-cli-tool.d.ts.map +1 -1
- package/dist/internal/resolve-cli-tool.js +17 -10
- package/dist/internal/resolve-cli-tool.js.map +1 -1
- package/dist/internal/staging-dir.d.ts +3 -2
- package/dist/internal/staging-dir.d.ts.map +1 -1
- package/dist/internal/staging-dir.js +3 -2
- package/dist/internal/staging-dir.js.map +1 -1
- package/dist/internal/xdg-data-dirs.d.ts +2 -1
- package/dist/internal/xdg-data-dirs.d.ts.map +1 -1
- package/dist/internal/xdg-data-dirs.js +4 -2
- package/dist/internal/xdg-data-dirs.js.map +1 -1
- package/dist/mcp/client.d.ts +9 -4
- package/dist/mcp/client.d.ts.map +1 -1
- package/dist/mcp/client.js +106 -81
- package/dist/mcp/client.js.map +1 -1
- package/dist/mcp/handlers.d.ts +3 -2
- package/dist/mcp/handlers.d.ts.map +1 -1
- package/dist/mcp/handlers.js +89 -61
- package/dist/mcp/handlers.js.map +1 -1
- package/dist/mcp/index.d.ts +2 -2
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +30 -13
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/serialize-widget.d.ts +3 -3
- package/dist/mcp/serialize-widget.d.ts.map +1 -1
- package/dist/mcp/serialize-widget.js +9 -6
- package/dist/mcp/serialize-widget.js.map +1 -1
- package/dist/mcp/testing-loader.d.ts +3 -4
- package/dist/mcp/testing-loader.d.ts.map +1 -1
- package/dist/mcp/testing-loader.js +43 -21
- package/dist/mcp/testing-loader.js.map +1 -1
- package/dist/mcp/widget-registry.d.ts +3 -2
- package/dist/mcp/widget-registry.d.ts.map +1 -1
- package/dist/mcp/widget-registry.js +4 -3
- package/dist/mcp/widget-registry.js.map +1 -1
- package/dist/refresh-globals.d.ts +2 -0
- package/dist/refresh-globals.d.ts.map +1 -0
- package/dist/refresh-globals.js +8 -0
- package/dist/refresh-globals.js.map +1 -0
- package/dist/refresh-runtime.d.ts +5 -11
- package/dist/refresh-runtime.d.ts.map +1 -1
- package/dist/refresh-runtime.js +16 -14
- package/dist/refresh-runtime.js.map +1 -1
- package/dist/settings/compile.d.ts +2 -1
- package/dist/settings/compile.d.ts.map +1 -1
- package/dist/settings/compile.js +6 -4
- package/dist/settings/compile.js.map +1 -1
- package/dist/settings/parser.d.ts +6 -5
- package/dist/settings/parser.d.ts.map +1 -1
- package/dist/settings/parser.js +26 -22
- package/dist/settings/parser.js.map +1 -1
- package/dist/settings/render.d.ts +3 -2
- package/dist/settings/render.d.ts.map +1 -1
- package/dist/settings/render.js +38 -64
- package/dist/settings/render.js.map +1 -1
- package/dist/settings/schema.d.ts +8 -8
- package/dist/settings/schema.d.ts.map +1 -1
- package/dist/settings/schema.js +61 -43
- package/dist/settings/schema.js.map +1 -1
- package/dist/vite-plugins/asset-extensions.d.ts +4 -3
- package/dist/vite-plugins/asset-extensions.d.ts.map +1 -1
- package/dist/vite-plugins/asset-extensions.js +4 -3
- package/dist/vite-plugins/asset-extensions.js.map +1 -1
- package/dist/vite-plugins/built-url.d.ts +2 -1
- package/dist/vite-plugins/built-url.d.ts.map +1 -1
- package/dist/vite-plugins/built-url.js +19 -13
- package/dist/vite-plugins/built-url.js.map +1 -1
- package/dist/vite-plugins/css.d.ts +2 -1
- package/dist/vite-plugins/css.d.ts.map +1 -1
- package/dist/vite-plugins/css.js +23 -15
- package/dist/vite-plugins/css.js.map +1 -1
- package/dist/vite-plugins/fast-refresh/refresh-filter.d.ts +5 -4
- package/dist/vite-plugins/fast-refresh/refresh-filter.d.ts.map +1 -1
- package/dist/vite-plugins/fast-refresh/refresh-filter.js +11 -7
- package/dist/vite-plugins/fast-refresh/refresh-filter.js.map +1 -1
- package/dist/vite-plugins/fast-refresh/swc-refresh.d.ts +4 -3
- package/dist/vite-plugins/fast-refresh/swc-refresh.d.ts.map +1 -1
- package/dist/vite-plugins/fast-refresh/swc-refresh.js +45 -41
- package/dist/vite-plugins/fast-refresh/swc-refresh.js.map +1 -1
- package/dist/vite-plugins/icons.d.ts +2 -1
- package/dist/vite-plugins/icons.d.ts.map +1 -1
- package/dist/vite-plugins/icons.js +24 -20
- package/dist/vite-plugins/icons.js.map +1 -1
- package/dist/vite-plugins/index.d.ts +2 -1
- package/dist/vite-plugins/index.d.ts.map +1 -1
- package/dist/vite-plugins/index.js +3 -2
- package/dist/vite-plugins/index.js.map +1 -1
- package/dist/vite-plugins/native.d.ts +2 -1
- package/dist/vite-plugins/native.d.ts.map +1 -1
- package/dist/vite-plugins/native.js +6 -4
- package/dist/vite-plugins/native.js.map +1 -1
- package/dist/vite-plugins/react-compiler.d.ts +3 -2
- package/dist/vite-plugins/react-compiler.d.ts.map +1 -1
- package/dist/vite-plugins/react-compiler.js +26 -20
- package/dist/vite-plugins/react-compiler.js.map +1 -1
- package/dist/vite-plugins/react-dom-prebundle.d.ts +2 -1
- package/dist/vite-plugins/react-dom-prebundle.d.ts.map +1 -1
- package/dist/vite-plugins/react-dom-prebundle.js +2 -1
- package/dist/vite-plugins/react-dom-prebundle.js.map +1 -1
- package/dist/vite-plugins/resource-init-module.d.ts +2 -1
- package/dist/vite-plugins/resource-init-module.d.ts.map +1 -1
- package/dist/vite-plugins/resource-init-module.js +39 -38
- package/dist/vite-plugins/resource-init-module.js.map +1 -1
- package/dist/vite-plugins/resource-shared.d.ts +7 -5
- package/dist/vite-plugins/resource-shared.d.ts.map +1 -1
- package/dist/vite-plugins/resource-shared.js +17 -10
- package/dist/vite-plugins/resource-shared.js.map +1 -1
- package/dist/vite-plugins/resources.d.ts +3 -2
- package/dist/vite-plugins/resources.d.ts.map +1 -1
- package/dist/vite-plugins/resources.js +94 -54
- package/dist/vite-plugins/resources.js.map +1 -1
- package/dist/vite-plugins/settings-worker-env.d.ts +2 -1
- package/dist/vite-plugins/settings-worker-env.d.ts.map +1 -1
- package/dist/vite-plugins/settings-worker-env.js +4 -2
- package/dist/vite-plugins/settings-worker-env.js.map +1 -1
- package/dist/vite-plugins/settings.d.ts +2 -1
- package/dist/vite-plugins/settings.d.ts.map +1 -1
- package/dist/vite-plugins/settings.js +56 -32
- package/dist/vite-plugins/settings.js.map +1 -1
- package/dist/vite-plugins/strip-query.d.ts +3 -0
- package/dist/vite-plugins/strip-query.d.ts.map +1 -0
- package/dist/vite-plugins/strip-query.js +6 -0
- package/dist/vite-plugins/strip-query.js.map +1 -0
- package/dist/vite-plugins/undeclared-library.d.ts +2 -1
- package/dist/vite-plugins/undeclared-library.d.ts.map +1 -1
- package/dist/vite-plugins/undeclared-library.js +8 -5
- package/dist/vite-plugins/undeclared-library.js.map +1 -1
- package/dist/vite-plugins/virtual-module.d.ts +2 -2
- package/dist/vite-plugins/virtual-module.d.ts.map +1 -1
- package/dist/vite-plugins/virtual-module.js +5 -3
- package/dist/vite-plugins/virtual-module.js.map +1 -1
- package/dist/vitest-plugin.d.ts.map +1 -1
- package/dist/vitest-plugin.js.map +1 -1
- package/env.d.ts +21 -61
- package/package.json +12 -14
- package/src/babel-modules.d.ts +0 -2
- package/src/builder.ts +7 -6
- package/src/cli.ts +26 -6
- package/src/codegen/freshness.ts +38 -25
- package/src/codegen/report.ts +10 -2
- package/src/codegen/run-codegen.ts +135 -60
- package/src/codegen/store-resolver.ts +56 -15
- package/src/commands/build.ts +3 -2
- package/src/commands/codegen.ts +9 -4
- package/src/commands/dev.ts +3 -4
- package/src/commands/docs.ts +36 -7
- package/src/dev/icon-dir.ts +13 -3
- package/src/dev/refresh-tracker.ts +12 -8
- package/src/dev/runner-deps.ts +30 -7
- package/src/dev/runner-main.ts +4 -3
- package/src/dev/runner.ts +100 -41
- package/src/dev/schema-dir.ts +7 -2
- package/src/dev/supervisor.ts +169 -71
- package/src/dev/vite-dev-server.ts +4 -2
- package/src/internal/banner.ts +5 -1
- package/src/internal/data-dir.ts +47 -16
- package/src/internal/entry-arg.ts +16 -8
- package/src/internal/errors.ts +15 -7
- package/src/internal/list-files.ts +10 -4
- package/src/internal/resolve-cli-tool.ts +28 -12
- package/src/internal/staging-dir.ts +5 -2
- package/src/internal/xdg-data-dirs.ts +8 -2
- package/src/mcp/client.ts +143 -85
- package/src/mcp/handlers.ts +126 -69
- package/src/mcp/index.ts +38 -14
- package/src/mcp/serialize-widget.ts +15 -8
- package/src/mcp/testing-loader.ts +64 -23
- package/src/mcp/widget-registry.ts +11 -5
- package/src/refresh-globals.ts +10 -0
- package/src/refresh-runtime.ts +20 -16
- package/src/settings/compile.ts +7 -4
- package/src/settings/parser.ts +69 -30
- package/src/settings/render.ts +66 -66
- package/src/settings/schema.ts +100 -46
- package/src/vite-plugins/asset-extensions.ts +4 -3
- package/src/vite-plugins/built-url.ts +30 -15
- package/src/vite-plugins/css.ts +35 -20
- package/src/vite-plugins/fast-refresh/refresh-filter.ts +18 -10
- package/src/vite-plugins/fast-refresh/swc-refresh.ts +60 -47
- package/src/vite-plugins/icons.ts +40 -24
- package/src/vite-plugins/index.ts +6 -3
- package/src/vite-plugins/native.ts +14 -5
- package/src/vite-plugins/react-compiler.ts +44 -30
- package/src/vite-plugins/react-dom-prebundle.ts +4 -1
- package/src/vite-plugins/resource-init-module.ts +41 -38
- package/src/vite-plugins/resource-shared.ts +27 -14
- package/src/vite-plugins/resources.ts +153 -59
- package/src/vite-plugins/settings-worker-env.ts +7 -2
- package/src/vite-plugins/settings.ts +107 -42
- package/src/vite-plugins/strip-query.ts +7 -0
- package/src/vite-plugins/undeclared-library.ts +21 -12
- package/src/vite-plugins/virtual-module.ts +9 -2
- package/src/vitest-plugin.ts +1 -2
package/src/internal/banner.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import type { Rollup } from "vite";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const prependBanner = (options: Rollup.OutputOptions, banner: string): Rollup.OutputOptions => {
|
|
4
4
|
const existing = options.banner;
|
|
5
|
+
|
|
5
6
|
if (typeof existing === "function") {
|
|
6
7
|
return { ...options, banner: async (chunk) => `${banner}\n${await existing(chunk)}` };
|
|
7
8
|
}
|
|
9
|
+
|
|
8
10
|
return { ...options, banner: existing ? `${banner}\n${existing}` : banner };
|
|
9
11
|
};
|
|
12
|
+
|
|
13
|
+
export { prependBanner };
|
package/src/internal/data-dir.ts
CHANGED
|
@@ -1,39 +1,70 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
const DATA_IMPORT_PREFIX = "#data";
|
|
5
|
+
const DATA_IMPORT_KEY = `${DATA_IMPORT_PREFIX}/*`;
|
|
6
|
+
const CONDITION_PRIORITY = ["default", "import", "node"] as const;
|
|
7
|
+
const DATA_TARGET_PATTERN = /^\.?\/?(.+?)\/\*$/;
|
|
5
8
|
|
|
6
|
-
const
|
|
9
|
+
const isConditionMap = (entry: unknown): entry is Record<string, unknown> =>
|
|
10
|
+
entry !== null && typeof entry === "object" && !Array.isArray(entry);
|
|
7
11
|
|
|
8
|
-
const
|
|
12
|
+
const conditionTarget = (conditions: Record<string, unknown>): string | null => {
|
|
13
|
+
for (const condition of CONDITION_PRIORITY) {
|
|
14
|
+
const value = conditions[condition];
|
|
9
15
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
if (entry !== null && typeof entry === "object" && !Array.isArray(entry)) {
|
|
13
|
-
const conditions = entry as Record<string, unknown>;
|
|
14
|
-
for (const condition of CONDITION_PRIORITY) {
|
|
15
|
-
const value = conditions[condition];
|
|
16
|
-
if (typeof value === "string") return value;
|
|
16
|
+
if (typeof value === "string") {
|
|
17
|
+
return value;
|
|
17
18
|
}
|
|
18
19
|
}
|
|
20
|
+
|
|
19
21
|
return null;
|
|
20
22
|
};
|
|
21
23
|
|
|
22
|
-
const
|
|
24
|
+
const targetString = (entry: unknown): string | null => {
|
|
25
|
+
if (typeof entry === "string") {
|
|
26
|
+
return entry;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (isConditionMap(entry)) {
|
|
30
|
+
return conditionTarget(entry);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return null;
|
|
34
|
+
};
|
|
23
35
|
|
|
24
36
|
const directoryFromTarget = (target: string): string | null => {
|
|
25
37
|
const match = DATA_TARGET_PATTERN.exec(target);
|
|
38
|
+
|
|
26
39
|
return match?.[1] ?? null;
|
|
27
40
|
};
|
|
28
41
|
|
|
29
|
-
|
|
30
|
-
let manifest:
|
|
42
|
+
const resolveDataDir = (root: string): string | null => {
|
|
43
|
+
let manifest: unknown;
|
|
44
|
+
|
|
31
45
|
try {
|
|
32
|
-
manifest = JSON.parse(readFileSync(join(root, "package.json"), "
|
|
46
|
+
manifest = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
|
|
33
47
|
} catch {
|
|
34
48
|
return null;
|
|
35
49
|
}
|
|
36
|
-
|
|
37
|
-
if (
|
|
50
|
+
|
|
51
|
+
if (!isConditionMap(manifest)) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const imports = manifest.imports;
|
|
56
|
+
|
|
57
|
+
if (!isConditionMap(imports)) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const target = targetString(imports[DATA_IMPORT_KEY]);
|
|
62
|
+
|
|
63
|
+
if (target === null) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
38
67
|
return directoryFromTarget(target);
|
|
39
68
|
};
|
|
69
|
+
|
|
70
|
+
export { DATA_IMPORT_PREFIX, resolveDataDir };
|
|
@@ -1,19 +1,20 @@
|
|
|
1
|
+
import type { PositionalArgDef, StringArgDef } from "citty";
|
|
1
2
|
import { existsSync } from "node:fs";
|
|
2
3
|
import { resolve } from "node:path";
|
|
3
|
-
import type { PositionalArgDef, StringArgDef } from "citty";
|
|
4
4
|
|
|
5
5
|
const DEFAULT_ENTRY_BASE = "src/index";
|
|
6
|
-
const
|
|
6
|
+
const DEFAULT_ENTRY_EXTENSION = ".tsx";
|
|
7
|
+
const DEFAULT_ENTRY_EXTENSIONS = [DEFAULT_ENTRY_EXTENSION, ".jsx", ".ts", ".js"];
|
|
7
8
|
const DEFAULT_ENTRY = `${DEFAULT_ENTRY_BASE}{${DEFAULT_ENTRY_EXTENSIONS.join(",")}}`;
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
const cwdArg: { cwd: StringArgDef } = {
|
|
10
11
|
cwd: {
|
|
11
12
|
type: "string",
|
|
12
13
|
description: "Project root (default: current working directory)",
|
|
13
14
|
},
|
|
14
15
|
};
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
const entryArg: { entry: PositionalArgDef; cwd: StringArgDef } = {
|
|
17
18
|
entry: {
|
|
18
19
|
type: "positional",
|
|
19
20
|
description: `Entry file (default: ${DEFAULT_ENTRY})`,
|
|
@@ -22,17 +23,24 @@ export const entryArg: { entry: PositionalArgDef; cwd: StringArgDef } = {
|
|
|
22
23
|
...cwdArg,
|
|
23
24
|
};
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
const resolveCwd = (args: { cwd?: string }): string => (args.cwd ? resolve(args.cwd) : process.cwd());
|
|
26
27
|
|
|
27
28
|
const resolveDefaultEntry = (cwd: string): string => {
|
|
28
29
|
for (const extension of DEFAULT_ENTRY_EXTENSIONS) {
|
|
29
30
|
const candidate = resolve(cwd, `${DEFAULT_ENTRY_BASE}${extension}`);
|
|
30
|
-
|
|
31
|
+
|
|
32
|
+
if (existsSync(candidate)) {
|
|
33
|
+
return candidate;
|
|
34
|
+
}
|
|
31
35
|
}
|
|
32
|
-
|
|
36
|
+
|
|
37
|
+
return resolve(cwd, `${DEFAULT_ENTRY_BASE}${DEFAULT_ENTRY_EXTENSION}`);
|
|
33
38
|
};
|
|
34
39
|
|
|
35
|
-
|
|
40
|
+
const resolveEntry = (args: { entry?: string; cwd?: string }): { cwd: string; entry: string } => {
|
|
36
41
|
const cwd = resolveCwd(args);
|
|
42
|
+
|
|
37
43
|
return { cwd, entry: args.entry ? resolve(cwd, args.entry) : resolveDefaultEntry(cwd) };
|
|
38
44
|
};
|
|
45
|
+
|
|
46
|
+
export { cwdArg, entryArg, resolveCwd, resolveEntry };
|
package/src/internal/errors.ts
CHANGED
|
@@ -1,28 +1,36 @@
|
|
|
1
|
-
import { logger } from "@gtkx/utils";
|
|
2
1
|
import type { ArgsDef, CommandDef } from "citty";
|
|
2
|
+
import { logger } from "@gtkx/utils";
|
|
3
3
|
|
|
4
4
|
const ERROR_EXIT_CODE = 1;
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
const printError = (cause: unknown): never => {
|
|
7
7
|
const message = cause instanceof Error ? cause.message : String(cause);
|
|
8
8
|
logger.error(message);
|
|
9
|
+
|
|
9
10
|
if (cause instanceof Error && cause.stack !== undefined) {
|
|
10
11
|
logger.debug(cause.stack);
|
|
11
12
|
}
|
|
13
|
+
|
|
12
14
|
process.exit(ERROR_EXIT_CODE);
|
|
13
15
|
};
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
const withErrorBoundary = <T extends ArgsDef>(command: CommandDef<T>): CommandDef<T> => {
|
|
16
18
|
const run = command.run;
|
|
17
|
-
|
|
19
|
+
|
|
20
|
+
if (run === undefined) {
|
|
21
|
+
return command;
|
|
22
|
+
}
|
|
23
|
+
|
|
18
24
|
return {
|
|
19
25
|
...command,
|
|
20
|
-
run: async (context) => {
|
|
26
|
+
run: async (context): Promise<unknown> => {
|
|
21
27
|
try {
|
|
22
28
|
return await run(context);
|
|
23
|
-
} catch (
|
|
24
|
-
printError(
|
|
29
|
+
} catch (error) {
|
|
30
|
+
return printError(error);
|
|
25
31
|
}
|
|
26
32
|
},
|
|
27
33
|
};
|
|
28
34
|
};
|
|
35
|
+
|
|
36
|
+
export { printError, withErrorBoundary };
|
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
import { existsSync, readdirSync } from "node:fs";
|
|
2
2
|
import { join, relative } from "node:path";
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
type ListedFile = {
|
|
5
5
|
absPath: string;
|
|
6
6
|
rel: string;
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
if (!existsSync(dir))
|
|
9
|
+
const listFilesRecursive = (dir: string, shouldInclude?: (name: string) => boolean): ListedFile[] => {
|
|
10
|
+
if (!existsSync(dir)) {
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
|
|
11
14
|
return readdirSync(dir, { recursive: true, withFileTypes: true })
|
|
12
|
-
.filter((entry) => entry.isFile() && (
|
|
15
|
+
.filter((entry) => entry.isFile() && (shouldInclude === undefined || shouldInclude(entry.name)))
|
|
13
16
|
.map((entry) => {
|
|
14
17
|
const absPath = join(entry.parentPath, entry.name);
|
|
18
|
+
|
|
15
19
|
return { absPath, rel: relative(dir, absPath) };
|
|
16
20
|
});
|
|
17
21
|
};
|
|
22
|
+
|
|
23
|
+
export { listFilesRecursive, type ListedFile };
|
|
@@ -1,21 +1,37 @@
|
|
|
1
1
|
import { existsSync, statSync } from "node:fs";
|
|
2
2
|
import { delimiter, join } from "node:path";
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
const cache
|
|
4
|
+
const resolveCliTool = (() => {
|
|
5
|
+
const cache: Map<string, string> = new Map();
|
|
6
6
|
|
|
7
7
|
return (executable: string): string => {
|
|
8
8
|
const cached = cache.get(executable);
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
for (const dir of dirs) {
|
|
13
|
-
const candidate = join(dir, executable);
|
|
14
|
-
if (existsSync(candidate) && statSync(candidate).isFile()) {
|
|
15
|
-
cache.set(executable, candidate);
|
|
16
|
-
return candidate;
|
|
17
|
-
}
|
|
9
|
+
|
|
10
|
+
if (cached) {
|
|
11
|
+
return cached;
|
|
18
12
|
}
|
|
19
|
-
|
|
13
|
+
|
|
14
|
+
const resolved = findInPath(executable);
|
|
15
|
+
cache.set(executable, resolved);
|
|
16
|
+
|
|
17
|
+
return resolved;
|
|
20
18
|
};
|
|
21
19
|
})();
|
|
20
|
+
|
|
21
|
+
const isExecutableFile = (candidate: string): boolean => existsSync(candidate) && statSync(candidate).isFile();
|
|
22
|
+
|
|
23
|
+
const findInPath = (executable: string): string => {
|
|
24
|
+
const dirs = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
|
|
25
|
+
|
|
26
|
+
for (const dir of dirs) {
|
|
27
|
+
const candidate = join(dir, executable);
|
|
28
|
+
|
|
29
|
+
if (isExecutableFile(candidate)) {
|
|
30
|
+
return candidate;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
throw new Error(`${executable} not found in PATH`);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export { resolveCliTool };
|
|
@@ -2,15 +2,18 @@ import { mkdtempSync, rmSync } from "node:fs";
|
|
|
2
2
|
import { tmpdir } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
const removeTempDir = (dir: string): void => {
|
|
6
6
|
rmSync(dir, { recursive: true, force: true, maxRetries: 5 });
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
const withStagingDir = <T>(prefix: string, produce: (dir: string) => T): T => {
|
|
10
10
|
const dir = mkdtempSync(join(tmpdir(), `gtkx-${prefix}-`));
|
|
11
|
+
|
|
11
12
|
try {
|
|
12
13
|
return produce(dir);
|
|
13
14
|
} finally {
|
|
14
15
|
removeTempDir(dir);
|
|
15
16
|
}
|
|
16
17
|
};
|
|
18
|
+
|
|
19
|
+
export { removeTempDir, withStagingDir };
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
const XDG_DATA_DIRS_DEFAULT = "/usr/local/share:/usr/share";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const prependXdgDataDir = (dir: string, existing: string | undefined): string => {
|
|
4
4
|
const base = existing === undefined || existing.length === 0 ? XDG_DATA_DIRS_DEFAULT : existing;
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
if (base.split(":").includes(dir)) {
|
|
7
|
+
return base;
|
|
8
|
+
}
|
|
9
|
+
|
|
6
10
|
return `${dir}:${base}`;
|
|
7
11
|
};
|
|
12
|
+
|
|
13
|
+
export { prependXdgDataDir };
|
package/src/mcp/client.ts
CHANGED
|
@@ -1,20 +1,58 @@
|
|
|
1
|
-
import * as net from "node:net";
|
|
2
1
|
import * as Gio from "@gtkx/gi/gio";
|
|
3
2
|
import * as Gtk from "@gtkx/gi/gtk";
|
|
4
3
|
import { DEFAULT_SOCKET_PATH, ErrorCode, ProtocolConnection, ProtocolError, type Request } from "@gtkx/mcp/internal";
|
|
5
4
|
import { error, errorMessage, info, normalizeError, warn } from "@gtkx/utils";
|
|
5
|
+
import * as net from "node:net";
|
|
6
6
|
import { dispatch } from "./handlers.js";
|
|
7
7
|
import { WidgetRegistry } from "./widget-registry.js";
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
type McpClientOptions = {
|
|
10
10
|
socketPath?: string;
|
|
11
11
|
applicationId: string;
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
+
type ConnectCallbacks = {
|
|
15
|
+
onSuccess?: (() => void) | undefined;
|
|
16
|
+
onError?: ((error: Error) => void) | undefined;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
type ConnectSettler = {
|
|
20
|
+
succeed: () => void;
|
|
21
|
+
fail: (error: Error) => void;
|
|
22
|
+
};
|
|
23
|
+
|
|
14
24
|
const RECONNECT_DELAY_MS = 2000;
|
|
15
|
-
const REGISTER_TIMEOUT_MS =
|
|
25
|
+
const REGISTER_TIMEOUT_MS = 30_000;
|
|
26
|
+
const DISCONNECT_ERROR_CODES: Set<string> = new Set(["ENOENT", "ECONNREFUSED", "EPIPE", "ECONNRESET"]);
|
|
27
|
+
|
|
28
|
+
const toResponseError = (error: unknown): { code: number; message: string; data?: unknown } =>
|
|
29
|
+
error instanceof ProtocolError
|
|
30
|
+
? error.toErrorObject()
|
|
31
|
+
: { code: ErrorCode.INTERNAL_ERROR, message: errorMessage(error) };
|
|
32
|
+
|
|
33
|
+
const connectSettler = (callbacks: ConnectCallbacks): ConnectSettler => {
|
|
34
|
+
let isSettled = false;
|
|
35
|
+
|
|
36
|
+
const settle = (notify: () => void): void => {
|
|
37
|
+
if (isSettled) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
isSettled = true;
|
|
42
|
+
notify();
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
succeed: () => {
|
|
47
|
+
settle(() => callbacks.onSuccess?.());
|
|
48
|
+
},
|
|
49
|
+
fail: (failure) => {
|
|
50
|
+
settle(() => callbacks.onError?.(failure));
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
};
|
|
16
54
|
|
|
17
|
-
|
|
55
|
+
class McpClient {
|
|
18
56
|
private socket: net.Socket | null = null;
|
|
19
57
|
private connection: ProtocolConnection | null = null;
|
|
20
58
|
private socketPath: string;
|
|
@@ -30,92 +68,61 @@ export class McpClient {
|
|
|
30
68
|
this.applicationId = options.applicationId;
|
|
31
69
|
}
|
|
32
70
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
this.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
this.pendingConnectReject = null;
|
|
43
|
-
reject(error);
|
|
44
|
-
},
|
|
45
|
-
);
|
|
46
|
-
});
|
|
71
|
+
private handleClose(): void {
|
|
72
|
+
if (this.hasConnected) {
|
|
73
|
+
info("Disconnected from MCP server");
|
|
74
|
+
this.hasConnected = false;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
this.socket = null;
|
|
78
|
+
this.connection = null;
|
|
79
|
+
this.scheduleReconnect();
|
|
47
80
|
}
|
|
48
81
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
this.
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
clearTimeout(this.reconnectTimer);
|
|
57
|
-
this.reconnectTimer = null;
|
|
58
|
-
}
|
|
59
|
-
this.connection?.rejectPending(new Error("Client disconnected"));
|
|
60
|
-
if (this.socket) {
|
|
61
|
-
this.connection?.write({ id: crypto.randomUUID(), method: "app.unregister" });
|
|
62
|
-
this.socket.destroy();
|
|
63
|
-
this.socket = null;
|
|
82
|
+
private handleSocketError(socketError: Error): void {
|
|
83
|
+
const code = (socketError as NodeJS.ErrnoException).code;
|
|
84
|
+
|
|
85
|
+
if (code !== undefined && DISCONNECT_ERROR_CODES.has(code)) {
|
|
86
|
+
this.scheduleReconnect();
|
|
87
|
+
} else {
|
|
88
|
+
error("Socket error:", socketError.message);
|
|
64
89
|
}
|
|
65
|
-
this.connection = null;
|
|
66
|
-
this.hasConnected = false;
|
|
67
90
|
}
|
|
68
91
|
|
|
69
|
-
private
|
|
70
|
-
|
|
92
|
+
private async registerWithServer(settle: ConnectSettler): Promise<void> {
|
|
93
|
+
try {
|
|
94
|
+
await this.register();
|
|
95
|
+
info("Registered with MCP server");
|
|
96
|
+
settle.succeed();
|
|
97
|
+
} catch (registerError) {
|
|
98
|
+
error("Failed to register with MCP server:", errorMessage(registerError));
|
|
99
|
+
settle.fail(normalizeError(registerError));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
71
102
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
settled = true;
|
|
75
|
-
callback?.(...args);
|
|
76
|
-
};
|
|
103
|
+
private attemptConnect(callbacks: ConnectCallbacks = {}): void {
|
|
104
|
+
const settle = connectSettler(callbacks);
|
|
77
105
|
|
|
78
106
|
const socket = net.createConnection(this.socketPath, () => {
|
|
79
107
|
info(`Connected to MCP server at ${this.socketPath}`);
|
|
80
108
|
this.hasConnected = true;
|
|
81
|
-
this.
|
|
82
|
-
.then(() => {
|
|
83
|
-
info("Registered with MCP server");
|
|
84
|
-
settle(onSuccess);
|
|
85
|
-
})
|
|
86
|
-
.catch((cause) => {
|
|
87
|
-
error("Failed to register with MCP server:", cause.message);
|
|
88
|
-
settle(onError, normalizeError(cause));
|
|
89
|
-
});
|
|
109
|
+
void this.registerWithServer(settle);
|
|
90
110
|
});
|
|
91
111
|
|
|
92
112
|
const connection = ProtocolConnection.fromSocket(socket, {
|
|
93
113
|
onClose: () => {
|
|
94
|
-
|
|
95
|
-
info("Disconnected from MCP server");
|
|
96
|
-
this.hasConnected = false;
|
|
97
|
-
}
|
|
98
|
-
this.socket = null;
|
|
99
|
-
this.connection = null;
|
|
100
|
-
this.scheduleReconnect();
|
|
114
|
+
this.handleClose();
|
|
101
115
|
},
|
|
102
116
|
onError: (socketError) => {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
code === "ENOENT" || code === "ECONNREFUSED" || code === "EPIPE" || code === "ECONNRESET";
|
|
106
|
-
if (isDisconnectError) {
|
|
107
|
-
this.scheduleReconnect();
|
|
108
|
-
} else {
|
|
109
|
-
error("Socket error:", socketError.message);
|
|
110
|
-
}
|
|
111
|
-
settle(onError, socketError);
|
|
117
|
+
this.handleSocketError(socketError);
|
|
118
|
+
settle.fail(socketError);
|
|
112
119
|
},
|
|
113
120
|
});
|
|
121
|
+
|
|
114
122
|
connection.on("request", (request) => {
|
|
115
|
-
this.handleRequest(request)
|
|
116
|
-
error("Error handling request:", cause);
|
|
117
|
-
});
|
|
123
|
+
void this.handleRequest(request);
|
|
118
124
|
});
|
|
125
|
+
|
|
119
126
|
connection.on("invalid", ({ error: parseError }) => {
|
|
120
127
|
warn(`Received invalid JSON from MCP server: ${parseError.message}`);
|
|
121
128
|
});
|
|
@@ -125,7 +132,10 @@ export class McpClient {
|
|
|
125
132
|
}
|
|
126
133
|
|
|
127
134
|
private scheduleReconnect(): void {
|
|
128
|
-
if (this.reconnectTimer || this.isStopping)
|
|
135
|
+
if (this.reconnectTimer || this.isStopping) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
129
139
|
this.reconnectTimer = setTimeout(() => {
|
|
130
140
|
this.reconnectTimer = null;
|
|
131
141
|
this.attemptConnect();
|
|
@@ -136,6 +146,7 @@ export class McpClient {
|
|
|
136
146
|
if (!this.connection) {
|
|
137
147
|
return Promise.reject(new Error("Connection not initialized"));
|
|
138
148
|
}
|
|
149
|
+
|
|
139
150
|
return this.connection.send(
|
|
140
151
|
"app.register",
|
|
141
152
|
{
|
|
@@ -147,31 +158,78 @@ export class McpClient {
|
|
|
147
158
|
);
|
|
148
159
|
}
|
|
149
160
|
|
|
150
|
-
private async
|
|
161
|
+
private async respondToRequest(request: Request): Promise<void> {
|
|
151
162
|
const { id, method, params } = request;
|
|
152
163
|
const connection = this.connection;
|
|
153
|
-
|
|
164
|
+
|
|
165
|
+
if (!connection) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
154
168
|
|
|
155
169
|
try {
|
|
156
170
|
const defaultApp = Gio.Application.getDefault();
|
|
171
|
+
|
|
157
172
|
if (!(defaultApp instanceof Gtk.Application)) {
|
|
158
173
|
throw new TypeError("Application not initialized");
|
|
159
174
|
}
|
|
175
|
+
|
|
160
176
|
this.registry.refresh();
|
|
161
177
|
const result = await dispatch(method, params, { app: defaultApp, registry: this.registry });
|
|
162
178
|
connection.write({ id, result });
|
|
163
|
-
} catch (
|
|
164
|
-
|
|
165
|
-
connection.write({ id, error: error.toErrorObject() });
|
|
166
|
-
} else {
|
|
167
|
-
connection.write({
|
|
168
|
-
id,
|
|
169
|
-
error: {
|
|
170
|
-
code: ErrorCode.INTERNAL_ERROR,
|
|
171
|
-
message: errorMessage(error),
|
|
172
|
-
},
|
|
173
|
-
});
|
|
174
|
-
}
|
|
179
|
+
} catch (dispatchError) {
|
|
180
|
+
connection.write({ id, error: toResponseError(dispatchError) });
|
|
175
181
|
}
|
|
176
182
|
}
|
|
183
|
+
|
|
184
|
+
private async handleRequest(request: Request): Promise<void> {
|
|
185
|
+
try {
|
|
186
|
+
await this.respondToRequest(request);
|
|
187
|
+
} catch (requestError) {
|
|
188
|
+
error("Error handling request:", requestError);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async connect(): Promise<void> {
|
|
193
|
+
return new Promise<void>((resolve, reject) => {
|
|
194
|
+
this.pendingConnectReject = reject;
|
|
195
|
+
|
|
196
|
+
this.attemptConnect({
|
|
197
|
+
onSuccess: () => {
|
|
198
|
+
this.pendingConnectReject = null;
|
|
199
|
+
resolve();
|
|
200
|
+
},
|
|
201
|
+
onError: (connectError) => {
|
|
202
|
+
this.pendingConnectReject = null;
|
|
203
|
+
reject(connectError);
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
disconnect(): void {
|
|
210
|
+
this.isStopping = true;
|
|
211
|
+
|
|
212
|
+
if (this.pendingConnectReject) {
|
|
213
|
+
this.pendingConnectReject(new Error("Client disconnected before connection registered"));
|
|
214
|
+
this.pendingConnectReject = null;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (this.reconnectTimer) {
|
|
218
|
+
clearTimeout(this.reconnectTimer);
|
|
219
|
+
this.reconnectTimer = null;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
this.connection?.rejectPending(new Error("Client disconnected"));
|
|
223
|
+
|
|
224
|
+
if (this.socket) {
|
|
225
|
+
this.connection?.write({ id: crypto.randomUUID(), method: "app.unregister" });
|
|
226
|
+
this.socket.destroy();
|
|
227
|
+
this.socket = null;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
this.connection = null;
|
|
231
|
+
this.hasConnected = false;
|
|
232
|
+
}
|
|
177
233
|
}
|
|
234
|
+
|
|
235
|
+
export { McpClient, type McpClientOptions };
|