claudekit-cli 3.37.0-dev.1 → 3.37.0-dev.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/README.md +20 -0
- package/dist/index.js +158 -30
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -91,6 +91,9 @@ ck --help
|
|
|
91
91
|
# Open config dashboard immediately
|
|
92
92
|
ck config
|
|
93
93
|
|
|
94
|
+
# Expose the dashboard intentionally to your LAN/Tailscale
|
|
95
|
+
ck config --host 0.0.0.0 --no-open
|
|
96
|
+
|
|
94
97
|
# Command-level help (recommended)
|
|
95
98
|
ck config --help
|
|
96
99
|
ck skills --help
|
|
@@ -99,6 +102,23 @@ ck commands --help
|
|
|
99
102
|
ck migrate --help
|
|
100
103
|
```
|
|
101
104
|
|
|
105
|
+
### Config Dashboard Access
|
|
106
|
+
|
|
107
|
+
By default, `ck config` binds the dashboard to `127.0.0.1` for local-only access.
|
|
108
|
+
|
|
109
|
+
Use `--host` when you intentionally want remote access from another device on the same trusted network:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
# Bind to all interfaces
|
|
113
|
+
ck config --host 0.0.0.0 --no-open
|
|
114
|
+
|
|
115
|
+
# Bind to a specific interface or hostname
|
|
116
|
+
ck config --host 100.88.12.4 --no-open
|
|
117
|
+
ck config --host dashboard.local --no-open
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The dashboard still enforces same-origin browser access. Remote access works when you open the UI from the same host/origin that reaches the server, instead of relying on a hardcoded IP allowlist.
|
|
121
|
+
|
|
102
122
|
### Create New Project
|
|
103
123
|
|
|
104
124
|
```bash
|
package/dist/index.js
CHANGED
|
@@ -46709,21 +46709,15 @@ var init_file_watcher = __esm(() => {
|
|
|
46709
46709
|
|
|
46710
46710
|
// src/domains/web-server/middleware/cors.ts
|
|
46711
46711
|
function corsMiddleware(req, res, next) {
|
|
46712
|
-
const origin = req.headers.origin;
|
|
46713
|
-
|
|
46712
|
+
const origin = getHeaderValue(req.headers.origin);
|
|
46713
|
+
const normalizedOrigin = origin ? normalizeOrigin(origin) : null;
|
|
46714
|
+
if (origin && (!normalizedOrigin || !isAllowedOrigin(normalizedOrigin, req))) {
|
|
46714
46715
|
res.status(403).json({ error: "Forbidden: invalid origin" });
|
|
46715
46716
|
return;
|
|
46716
46717
|
}
|
|
46717
|
-
|
|
46718
|
-
"
|
|
46719
|
-
"
|
|
46720
|
-
"http://localhost:5173",
|
|
46721
|
-
"http://127.0.0.1:3000",
|
|
46722
|
-
"http://127.0.0.1:3456",
|
|
46723
|
-
"http://127.0.0.1:5173"
|
|
46724
|
-
];
|
|
46725
|
-
if (origin && allowedOrigins.includes(origin)) {
|
|
46726
|
-
res.setHeader("Access-Control-Allow-Origin", origin);
|
|
46718
|
+
if (normalizedOrigin) {
|
|
46719
|
+
appendVaryHeader(res, "Origin");
|
|
46720
|
+
res.setHeader("Access-Control-Allow-Origin", normalizedOrigin);
|
|
46727
46721
|
}
|
|
46728
46722
|
res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
|
46729
46723
|
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
|
@@ -46734,6 +46728,68 @@ function corsMiddleware(req, res, next) {
|
|
|
46734
46728
|
}
|
|
46735
46729
|
next();
|
|
46736
46730
|
}
|
|
46731
|
+
function isAllowedOrigin(origin, req) {
|
|
46732
|
+
if (LOCAL_DEV_ORIGINS.has(origin)) {
|
|
46733
|
+
return true;
|
|
46734
|
+
}
|
|
46735
|
+
return getRequestOrigins(req).has(origin);
|
|
46736
|
+
}
|
|
46737
|
+
function getRequestOrigins(req) {
|
|
46738
|
+
const origins = new Set;
|
|
46739
|
+
const hosts = getHeaderValues(req.headers["x-forwarded-host"]).concat(getHeaderValues(req.headers.host));
|
|
46740
|
+
const protocols = getForwardedProtocols(req);
|
|
46741
|
+
for (const host of hosts) {
|
|
46742
|
+
for (const protocol of protocols) {
|
|
46743
|
+
origins.add(`${protocol}://${host}`);
|
|
46744
|
+
}
|
|
46745
|
+
}
|
|
46746
|
+
return origins;
|
|
46747
|
+
}
|
|
46748
|
+
function getForwardedProtocols(req) {
|
|
46749
|
+
const forwarded = getHeaderValues(req.headers["x-forwarded-proto"]).map((value) => value.toLowerCase());
|
|
46750
|
+
const current = typeof req.protocol === "string" && req.protocol ? [req.protocol.toLowerCase()] : ["http"];
|
|
46751
|
+
return Array.from(new Set([...forwarded, ...current].filter((value) => value === "http" || value === "https")));
|
|
46752
|
+
}
|
|
46753
|
+
function normalizeOrigin(origin) {
|
|
46754
|
+
try {
|
|
46755
|
+
return new URL(origin).origin;
|
|
46756
|
+
} catch {
|
|
46757
|
+
return null;
|
|
46758
|
+
}
|
|
46759
|
+
}
|
|
46760
|
+
function getHeaderValue(value) {
|
|
46761
|
+
if (Array.isArray(value)) {
|
|
46762
|
+
return value[0];
|
|
46763
|
+
}
|
|
46764
|
+
return value;
|
|
46765
|
+
}
|
|
46766
|
+
function getHeaderValues(value) {
|
|
46767
|
+
const raw = Array.isArray(value) ? value.join(",") : value;
|
|
46768
|
+
if (!raw) {
|
|
46769
|
+
return [];
|
|
46770
|
+
}
|
|
46771
|
+
return raw.split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
46772
|
+
}
|
|
46773
|
+
function appendVaryHeader(res, value) {
|
|
46774
|
+
const current = res.getHeader("Vary");
|
|
46775
|
+
if (!current) {
|
|
46776
|
+
res.setHeader("Vary", value);
|
|
46777
|
+
return;
|
|
46778
|
+
}
|
|
46779
|
+
const entries = String(current).split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
46780
|
+
if (!entries.includes(value)) {
|
|
46781
|
+
entries.push(value);
|
|
46782
|
+
res.setHeader("Vary", entries.join(", "));
|
|
46783
|
+
}
|
|
46784
|
+
}
|
|
46785
|
+
var LOCAL_DEV_ORIGINS;
|
|
46786
|
+
var init_cors = __esm(() => {
|
|
46787
|
+
LOCAL_DEV_ORIGINS = new Set(["localhost", "127.0.0.1", "[::1]"].flatMap((host) => [
|
|
46788
|
+
`http://${host}:3000`,
|
|
46789
|
+
`http://${host}:3456`,
|
|
46790
|
+
`http://${host}:5173`
|
|
46791
|
+
]));
|
|
46792
|
+
});
|
|
46737
46793
|
|
|
46738
46794
|
// src/domains/web-server/middleware/error-handler.ts
|
|
46739
46795
|
function errorHandler(err, _req, res, _next) {
|
|
@@ -56724,7 +56780,7 @@ var package_default;
|
|
|
56724
56780
|
var init_package = __esm(() => {
|
|
56725
56781
|
package_default = {
|
|
56726
56782
|
name: "claudekit-cli",
|
|
56727
|
-
version: "3.37.0-dev.
|
|
56783
|
+
version: "3.37.0-dev.2",
|
|
56728
56784
|
description: "CLI tool for bootstrapping and updating ClaudeKit projects",
|
|
56729
56785
|
type: "module",
|
|
56730
56786
|
repository: {
|
|
@@ -62150,7 +62206,12 @@ var init_websocket_manager = __esm(() => {
|
|
|
62150
62206
|
import { createServer } from "node:http";
|
|
62151
62207
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
62152
62208
|
async function createAppServer(options2 = {}) {
|
|
62153
|
-
const {
|
|
62209
|
+
const {
|
|
62210
|
+
port: preferredPort,
|
|
62211
|
+
openBrowser = true,
|
|
62212
|
+
devMode = false,
|
|
62213
|
+
host = DEFAULT_DASHBOARD_HOST
|
|
62214
|
+
} = options2;
|
|
62154
62215
|
const port = await getPorts({ port: preferredPort || [3456, 3457, 3458, 3459, 3460] });
|
|
62155
62216
|
const app = import_express2.default();
|
|
62156
62217
|
app.use(import_express2.default.json({ limit: "10mb" }));
|
|
@@ -62183,15 +62244,15 @@ async function createAppServer(options2 = {}) {
|
|
|
62183
62244
|
};
|
|
62184
62245
|
server.once("listening", onListening);
|
|
62185
62246
|
server.once("error", onError);
|
|
62186
|
-
server.listen(port);
|
|
62247
|
+
server.listen(port, host);
|
|
62187
62248
|
});
|
|
62188
|
-
logger.debug(`Server listening on
|
|
62249
|
+
logger.debug(`Server listening on ${host}:${port}`);
|
|
62189
62250
|
if (openBrowser) {
|
|
62190
62251
|
try {
|
|
62191
|
-
await open_default(
|
|
62252
|
+
await open_default(getBrowserUrl(host, port));
|
|
62192
62253
|
} catch (err) {
|
|
62193
62254
|
logger.warning(`Failed to open browser: ${err instanceof Error ? err.message : err}`);
|
|
62194
|
-
logger.info(`Open
|
|
62255
|
+
logger.info(`Open ${getBrowserUrl(host, port)} manually`);
|
|
62195
62256
|
}
|
|
62196
62257
|
}
|
|
62197
62258
|
} catch (error) {
|
|
@@ -62202,6 +62263,7 @@ async function createAppServer(options2 = {}) {
|
|
|
62202
62263
|
}
|
|
62203
62264
|
return {
|
|
62204
62265
|
port,
|
|
62266
|
+
host,
|
|
62205
62267
|
server,
|
|
62206
62268
|
close: async () => {
|
|
62207
62269
|
fileWatcher?.stop();
|
|
@@ -62252,17 +62314,29 @@ async function setupViteDevServer(app, httpServer, options2) {
|
|
|
62252
62314
|
serveStatic(app);
|
|
62253
62315
|
}
|
|
62254
62316
|
}
|
|
62255
|
-
|
|
62317
|
+
function getBrowserUrl(host, port) {
|
|
62318
|
+
if (WILDCARD_HOSTS.has(host) || LOOPBACK_OPEN_HOSTS.has(host)) {
|
|
62319
|
+
return `http://localhost:${port}`;
|
|
62320
|
+
}
|
|
62321
|
+
return `http://${formatHostForUrl(host)}:${port}`;
|
|
62322
|
+
}
|
|
62323
|
+
function formatHostForUrl(host) {
|
|
62324
|
+
return host.includes(":") && !host.startsWith("[") ? `[${host}]` : host;
|
|
62325
|
+
}
|
|
62326
|
+
var import_express2, DEFAULT_DASHBOARD_HOST = "127.0.0.1", LOOPBACK_OPEN_HOSTS, WILDCARD_HOSTS;
|
|
62256
62327
|
var init_server = __esm(() => {
|
|
62257
62328
|
init_logger();
|
|
62258
62329
|
init_get_port();
|
|
62259
62330
|
init_open();
|
|
62260
62331
|
init_file_watcher();
|
|
62332
|
+
init_cors();
|
|
62261
62333
|
init_error_handler();
|
|
62262
62334
|
init_routes();
|
|
62263
62335
|
init_static_server();
|
|
62264
62336
|
init_websocket_manager();
|
|
62265
62337
|
import_express2 = __toESM(require_express2(), 1);
|
|
62338
|
+
LOOPBACK_OPEN_HOSTS = new Set(["127.0.0.1", "localhost", "::1"]);
|
|
62339
|
+
WILDCARD_HOSTS = new Set(["0.0.0.0", "::"]);
|
|
62266
62340
|
});
|
|
62267
62341
|
|
|
62268
62342
|
// src/domains/web-server/index.ts
|
|
@@ -70604,6 +70678,10 @@ var init_config_command_help = __esm(() => {
|
|
|
70604
70678
|
command: "ck config",
|
|
70605
70679
|
description: "Launch the web dashboard (same as 'ck config ui')"
|
|
70606
70680
|
},
|
|
70681
|
+
{
|
|
70682
|
+
command: "ck config --host 0.0.0.0 --no-open",
|
|
70683
|
+
description: "Expose the dashboard to your network intentionally"
|
|
70684
|
+
},
|
|
70607
70685
|
{
|
|
70608
70686
|
command: "ck config set defaults.kit engineer",
|
|
70609
70687
|
description: "Set a config value from the CLI"
|
|
@@ -70651,6 +70729,10 @@ var init_config_command_help = __esm(() => {
|
|
|
70651
70729
|
flags: "--port <port>",
|
|
70652
70730
|
description: "Port for dashboard server"
|
|
70653
70731
|
},
|
|
70732
|
+
{
|
|
70733
|
+
flags: "--host <host>",
|
|
70734
|
+
description: "Bind dashboard host (default: 127.0.0.1)"
|
|
70735
|
+
},
|
|
70654
70736
|
{
|
|
70655
70737
|
flags: "--no-open",
|
|
70656
70738
|
description: "Do not auto-open browser when launching dashboard"
|
|
@@ -70674,7 +70756,7 @@ var init_config_command_help = __esm(() => {
|
|
|
70674
70756
|
sections: [
|
|
70675
70757
|
{
|
|
70676
70758
|
title: "Notes",
|
|
70677
|
-
content: "Run 'ck config --help' to see both CLI actions and dashboard flags. Running bare 'ck config' opens the dashboard directly."
|
|
70759
|
+
content: "Run 'ck config --help' to see both CLI actions and dashboard flags. Running bare 'ck config' opens the dashboard directly. Use '--host' to expose the dashboard intentionally beyond localhost."
|
|
70678
70760
|
}
|
|
70679
70761
|
]
|
|
70680
70762
|
};
|
|
@@ -74708,12 +74790,16 @@ init_commands_discovery();
|
|
|
74708
74790
|
// src/commands/config/config-ui-command.ts
|
|
74709
74791
|
init_logger();
|
|
74710
74792
|
var import_picocolors14 = __toESM(require_picocolors(), 1);
|
|
74793
|
+
import { networkInterfaces } from "node:os";
|
|
74794
|
+
var LOOPBACK_HOSTS = new Set(["127.0.0.1", "localhost", "::1"]);
|
|
74795
|
+
var WILDCARD_HOSTS2 = new Set(["0.0.0.0", "::"]);
|
|
74711
74796
|
async function configUICommand(options2 = {}) {
|
|
74712
74797
|
const { port, dev = false } = options2;
|
|
74798
|
+
const host = options2.host?.trim() || undefined;
|
|
74713
74799
|
const noOpen = options2.open === false || options2.noOpen === true;
|
|
74714
74800
|
try {
|
|
74715
74801
|
if (port) {
|
|
74716
|
-
const isAvailable = await checkPort(port);
|
|
74802
|
+
const isAvailable = await checkPort(port, host);
|
|
74717
74803
|
if (!isAvailable) {
|
|
74718
74804
|
logger.error(`Port ${port} is already in use`);
|
|
74719
74805
|
logger.info("Try: ck config (auto-selects available port)");
|
|
@@ -74726,13 +74812,20 @@ async function configUICommand(options2 = {}) {
|
|
|
74726
74812
|
const server = await startServer({
|
|
74727
74813
|
port,
|
|
74728
74814
|
openBrowser: !noOpen,
|
|
74729
|
-
devMode: dev
|
|
74815
|
+
devMode: dev,
|
|
74816
|
+
host
|
|
74730
74817
|
});
|
|
74731
|
-
const
|
|
74818
|
+
const urls = getDashboardUrls(server.host, server.port);
|
|
74732
74819
|
console.log();
|
|
74733
74820
|
console.log(import_picocolors14.default.bold(" ClaudeKit Dashboard"));
|
|
74734
74821
|
console.log(import_picocolors14.default.dim(" ─────────────────────"));
|
|
74735
|
-
|
|
74822
|
+
if (urls.local) {
|
|
74823
|
+
console.log(` ${import_picocolors14.default.green("➜")} Local: ${import_picocolors14.default.cyan(urls.local)}`);
|
|
74824
|
+
}
|
|
74825
|
+
for (const url of urls.network) {
|
|
74826
|
+
console.log(` ${import_picocolors14.default.green(urls.local ? "•" : "➜")} Network: ${import_picocolors14.default.cyan(url)}`);
|
|
74827
|
+
}
|
|
74828
|
+
console.log(` ${import_picocolors14.default.green("•")} Bind: ${import_picocolors14.default.cyan(server.host)}`);
|
|
74736
74829
|
console.log();
|
|
74737
74830
|
console.log(import_picocolors14.default.dim(" Press Ctrl+C to stop"));
|
|
74738
74831
|
console.log();
|
|
@@ -74760,7 +74853,7 @@ async function configUICommand(options2 = {}) {
|
|
|
74760
74853
|
process.exitCode = 1;
|
|
74761
74854
|
}
|
|
74762
74855
|
}
|
|
74763
|
-
async function checkPort(port) {
|
|
74856
|
+
async function checkPort(port, host) {
|
|
74764
74857
|
const { createServer: createServer2 } = await import("node:net");
|
|
74765
74858
|
return new Promise((resolve13) => {
|
|
74766
74859
|
const server = createServer2();
|
|
@@ -74769,9 +74862,43 @@ async function checkPort(port) {
|
|
|
74769
74862
|
server.close();
|
|
74770
74863
|
resolve13(true);
|
|
74771
74864
|
});
|
|
74772
|
-
server.listen(port);
|
|
74865
|
+
server.listen(port, host);
|
|
74773
74866
|
});
|
|
74774
74867
|
}
|
|
74868
|
+
function getDashboardUrls(host, port) {
|
|
74869
|
+
if (WILDCARD_HOSTS2.has(host)) {
|
|
74870
|
+
return {
|
|
74871
|
+
local: `http://localhost:${port}`,
|
|
74872
|
+
network: getDetectedNetworkUrls(port)
|
|
74873
|
+
};
|
|
74874
|
+
}
|
|
74875
|
+
if (LOOPBACK_HOSTS.has(host)) {
|
|
74876
|
+
return {
|
|
74877
|
+
local: `http://localhost:${port}`,
|
|
74878
|
+
network: []
|
|
74879
|
+
};
|
|
74880
|
+
}
|
|
74881
|
+
return {
|
|
74882
|
+
local: null,
|
|
74883
|
+
network: [buildDashboardUrl(host, port)]
|
|
74884
|
+
};
|
|
74885
|
+
}
|
|
74886
|
+
function getDetectedNetworkUrls(port) {
|
|
74887
|
+
const urls = new Set;
|
|
74888
|
+
for (const addresses of Object.values(networkInterfaces())) {
|
|
74889
|
+
for (const address of addresses ?? []) {
|
|
74890
|
+
if (address.internal) {
|
|
74891
|
+
continue;
|
|
74892
|
+
}
|
|
74893
|
+
urls.add(buildDashboardUrl(address.address, port));
|
|
74894
|
+
}
|
|
74895
|
+
}
|
|
74896
|
+
return Array.from(urls).sort();
|
|
74897
|
+
}
|
|
74898
|
+
function buildDashboardUrl(host, port) {
|
|
74899
|
+
const formattedHost = host.includes(":") && !host.startsWith("[") ? `[${host}]` : host;
|
|
74900
|
+
return `http://${formattedHost}:${port}`;
|
|
74901
|
+
}
|
|
74775
74902
|
|
|
74776
74903
|
// src/commands/config/phases/get-handler.ts
|
|
74777
74904
|
init_config();
|
|
@@ -74935,9 +75062,10 @@ async function configCommand(action, keyOrOptions, valueOrOptions, options2) {
|
|
|
74935
75062
|
}
|
|
74936
75063
|
const rawOpts = options2 || (typeof keyOrOptions === "object" ? keyOrOptions : {});
|
|
74937
75064
|
const uiOpts = {
|
|
74938
|
-
port: rawOpts
|
|
74939
|
-
noOpen: rawOpts
|
|
74940
|
-
dev: rawOpts
|
|
75065
|
+
port: rawOpts.port,
|
|
75066
|
+
noOpen: rawOpts.noOpen,
|
|
75067
|
+
dev: rawOpts.dev,
|
|
75068
|
+
host: rawOpts.host
|
|
74941
75069
|
};
|
|
74942
75070
|
return configUICommand(uiOpts);
|
|
74943
75071
|
}
|
|
@@ -102663,7 +102791,7 @@ function registerCommands(cli) {
|
|
|
102663
102791
|
console.error(`Unknown action: ${action}. Available: start, stop, status, logs, setup, queue, approve, reject`);
|
|
102664
102792
|
}
|
|
102665
102793
|
});
|
|
102666
|
-
cli.command("config [action] [key] [value]", "Manage ClaudeKit configuration").option("-g, --global", "Use global config (~/.claudekit/config.json)").option("-l, --local", "Use local config (.claude/.ck.json)").option("--json", "Output in JSON format").option("--port <port>", "Port for UI server (default: auto)").option("--no-open", "Don't auto-open browser").option("--dev", "Run UI in development mode with HMR").action(async (action, key, value, options2) => {
|
|
102794
|
+
cli.command("config [action] [key] [value]", "Manage ClaudeKit configuration").option("-g, --global", "Use global config (~/.claudekit/config.json)").option("-l, --local", "Use local config (.claude/.ck.json)").option("--json", "Output in JSON format").option("--port <port>", "Port for UI server (default: auto)").option("--host <host>", "Bind dashboard host (default: 127.0.0.1)").option("--no-open", "Don't auto-open browser").option("--dev", "Run UI in development mode with HMR").action(async (action, key, value, options2) => {
|
|
102667
102795
|
await configCommand(action, key, value, options2);
|
|
102668
102796
|
});
|
|
102669
102797
|
registerProjectsCommand(cli);
|