kennel 0.2.36 → 0.2.37
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/cli/usage.js +1 -1
- package/dist/package/preview.d.ts +1 -0
- package/dist/package/preview.js +61 -17
- package/dist/site/terminal.js +10 -6
- package/package.json +1 -1
- package/schemas/config.schema.json +9 -0
package/dist/cli/usage.js
CHANGED
|
@@ -17,7 +17,7 @@ export function usage() {
|
|
|
17
17
|
kennel notes bind <uuid> [folder] [--json] [--remote <name>] [--replace] [--no-alias]
|
|
18
18
|
kennel notes new-identity [folder] [--json] [--remote <name>]
|
|
19
19
|
kennel serve [folder] [--host <host>] [--port <port>] [--lan|--tailscale]
|
|
20
|
-
kennel share|display [folder] [--lan|--tailscale] [--port <port>] [--dry-run]
|
|
20
|
+
kennel share|display [folder] [--lan|--tailscale] [--password] [--port <port>] [--dry-run]
|
|
21
21
|
kennel dev [path] [--config <json-file>] [--port <port>] [--script <script>] [--dry-run] [-- <args...>]
|
|
22
22
|
kennel extension install
|
|
23
23
|
kennel mcp generate [path] [--config <json-file>] [--data-only|--function-only]
|
package/dist/package/preview.js
CHANGED
|
@@ -296,11 +296,11 @@ function previewTerminal(url, target, startShare) {
|
|
|
296
296
|
].join(`
|
|
297
297
|
`));
|
|
298
298
|
};
|
|
299
|
-
const shareFolder = async () => {
|
|
299
|
+
const shareFolder = async (password = false) => {
|
|
300
300
|
status = "Preparing a private Tailscale link…";
|
|
301
301
|
render();
|
|
302
302
|
try {
|
|
303
|
-
share ??= startShare();
|
|
303
|
+
share ??= startShare(password);
|
|
304
304
|
let active;
|
|
305
305
|
try {
|
|
306
306
|
active = await share;
|
|
@@ -310,11 +310,14 @@ function previewTerminal(url, target, startShare) {
|
|
|
310
310
|
}
|
|
311
311
|
if (finished)
|
|
312
312
|
return;
|
|
313
|
-
status = `Tailscale link: ${active.url}
|
|
313
|
+
status = active.password ? `Tailscale link: ${active.url}
|
|
314
314
|
Username: ${active.username} · Password: ${active.password}
|
|
315
315
|
Password required. Send the password separately.
|
|
316
316
|
Connect Tailscale on your phone to the same tailnet.
|
|
317
317
|
Trusted HTTPS; no certificate installation required.
|
|
318
|
+
Keep this command running. Ctrl+C stops sharing.` : `Tailscale link: ${active.url}
|
|
319
|
+
Connect Tailscale on your phone to the same tailnet.
|
|
320
|
+
Trusted HTTPS; no password required on the tailnet.
|
|
318
321
|
Keep this command running. Ctrl+C stops sharing.`;
|
|
319
322
|
render();
|
|
320
323
|
try {
|
|
@@ -357,7 +360,8 @@ function previewTerminal(url, target, startShare) {
|
|
|
357
360
|
openPreview(url);
|
|
358
361
|
status = "Browser open requested.";
|
|
359
362
|
} },
|
|
360
|
-
{ label: "Share to another device", shortcut: "s", run: shareFolder },
|
|
363
|
+
{ label: "Share to another device", shortcut: "s", run: () => shareFolder(false) },
|
|
364
|
+
{ label: "Share link with password", shortcut: "p", run: () => shareFolder(true) },
|
|
361
365
|
{ label: "Exit", shortcut: "q", run: close }
|
|
362
366
|
];
|
|
363
367
|
const activate = async () => {
|
|
@@ -40843,6 +40847,10 @@ function splitViewerArgs(input) {
|
|
|
40843
40847
|
options.open = true;
|
|
40844
40848
|
continue;
|
|
40845
40849
|
}
|
|
40850
|
+
if (arg === "--password") {
|
|
40851
|
+
options.password = true;
|
|
40852
|
+
continue;
|
|
40853
|
+
}
|
|
40846
40854
|
if (arg === "--no-open") {
|
|
40847
40855
|
options.open = false;
|
|
40848
40856
|
continue;
|
|
@@ -41197,7 +41205,9 @@ var init_tailscale_share = __esm(() => {
|
|
|
41197
41205
|
});
|
|
41198
41206
|
|
|
41199
41207
|
// packages/preview/src/share.ts
|
|
41200
|
-
import { dirname as dirname13, resolve as resolve16 } from "node:path";
|
|
41208
|
+
import { dirname as dirname13, resolve as resolve16, join as join6 } from "node:path";
|
|
41209
|
+
import { homedir as homedir5 } from "node:os";
|
|
41210
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
41201
41211
|
import { existsSync as existsSync7, realpathSync as realpathSync4 } from "node:fs";
|
|
41202
41212
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
41203
41213
|
function splitShareArgs(input) {
|
|
@@ -41206,7 +41216,8 @@ function splitShareArgs(input) {
|
|
|
41206
41216
|
lan: false,
|
|
41207
41217
|
tailscale: false,
|
|
41208
41218
|
port: undefined,
|
|
41209
|
-
dryRun: false
|
|
41219
|
+
dryRun: false,
|
|
41220
|
+
password: false
|
|
41210
41221
|
};
|
|
41211
41222
|
let folder;
|
|
41212
41223
|
for (let index = 0;index < input.length; index += 1) {
|
|
@@ -41215,6 +41226,10 @@ function splitShareArgs(input) {
|
|
|
41215
41226
|
options.dryRun = true;
|
|
41216
41227
|
continue;
|
|
41217
41228
|
}
|
|
41229
|
+
if (arg === "--password") {
|
|
41230
|
+
options.password = true;
|
|
41231
|
+
continue;
|
|
41232
|
+
}
|
|
41218
41233
|
if (arg === "--lan") {
|
|
41219
41234
|
options.lan = true;
|
|
41220
41235
|
continue;
|
|
@@ -41270,6 +41285,13 @@ async function runningViewerRoot(port = plainViewerPort) {
|
|
|
41270
41285
|
}
|
|
41271
41286
|
async function startPreviewShare(target) {
|
|
41272
41287
|
const root = realpathSync4(target.root);
|
|
41288
|
+
const configPath = join6(process.env.XDG_CONFIG_HOME?.trim() || join6(homedir5(), ".config"), "kennel", "config.json");
|
|
41289
|
+
let configuredPassword = false;
|
|
41290
|
+
try {
|
|
41291
|
+
const config = JSON.parse(readFileSync2(configPath, "utf8"));
|
|
41292
|
+
configuredPassword = config.linkSharing?.password === true;
|
|
41293
|
+
} catch {}
|
|
41294
|
+
const password = target.password ?? configuredPassword;
|
|
41273
41295
|
const web = resolve16(dirname13(fileURLToPath4(import.meta.url)), "../../web");
|
|
41274
41296
|
if (!existsSync7(resolve16(web, "index.html"))) {
|
|
41275
41297
|
throw new Error("Kennel web app not found. Run the CLI build first.");
|
|
@@ -41287,37 +41309,58 @@ async function startPreviewShare(target) {
|
|
|
41287
41309
|
});
|
|
41288
41310
|
let shared;
|
|
41289
41311
|
let proxy;
|
|
41312
|
+
let localClose;
|
|
41290
41313
|
const close = async () => {
|
|
41291
41314
|
try {
|
|
41292
41315
|
await proxy?.close();
|
|
41293
41316
|
} finally {
|
|
41294
41317
|
try {
|
|
41295
|
-
await
|
|
41318
|
+
await localClose?.();
|
|
41296
41319
|
} finally {
|
|
41297
41320
|
await api.close();
|
|
41298
41321
|
}
|
|
41299
41322
|
}
|
|
41300
41323
|
};
|
|
41301
41324
|
try {
|
|
41302
|
-
|
|
41325
|
+
const handler = (request, response) => {
|
|
41303
41326
|
api.handle(request, response);
|
|
41304
|
-
}
|
|
41305
|
-
|
|
41306
|
-
|
|
41327
|
+
};
|
|
41328
|
+
const local = password ? await startProtectedShare(handler) : await (async () => {
|
|
41329
|
+
const { createServer } = await import("node:http");
|
|
41330
|
+
const server = createServer(handler);
|
|
41331
|
+
await new Promise((resolve, reject) => {
|
|
41332
|
+
server.once("error", reject);
|
|
41333
|
+
server.listen(0, "127.0.0.1", () => resolve());
|
|
41334
|
+
});
|
|
41335
|
+
const address = server.address();
|
|
41336
|
+
if (!address || typeof address === "string")
|
|
41337
|
+
throw new Error("No share server address");
|
|
41338
|
+
return { port: address.port, close: async () => {
|
|
41339
|
+
await new Promise((resolve) => {
|
|
41340
|
+
server.close(() => resolve());
|
|
41341
|
+
server.closeAllConnections();
|
|
41342
|
+
});
|
|
41343
|
+
} };
|
|
41344
|
+
})();
|
|
41345
|
+
localClose = local.close;
|
|
41346
|
+
shared = password ? local : { port: local.port, close: local.close };
|
|
41347
|
+
proxy = await startTailscaleProxy(host, local.port);
|
|
41348
|
+
const path = new URL(popUrl(host, String(local.port), {
|
|
41307
41349
|
dryRun: false,
|
|
41308
41350
|
host: "127.0.0.1",
|
|
41309
41351
|
hot: false,
|
|
41310
41352
|
lan: false,
|
|
41311
41353
|
open: false,
|
|
41312
41354
|
root,
|
|
41313
|
-
port: String(
|
|
41355
|
+
port: String(local.port),
|
|
41314
41356
|
tailscale: false,
|
|
41315
41357
|
watch: false,
|
|
41358
|
+
password: false,
|
|
41316
41359
|
target: target.target
|
|
41317
41360
|
})).pathname;
|
|
41318
41361
|
const url = new URL(path, proxy.origin);
|
|
41319
|
-
const response = await fetch(
|
|
41320
|
-
headers: { Authorization: shared.authorization },
|
|
41362
|
+
const response = await fetch(`http://127.0.0.1:${shared.port}/api/health`, {
|
|
41363
|
+
...shared.authorization ? { headers: { Authorization: shared.authorization } } : {},
|
|
41321
41364
|
signal: AbortSignal.timeout(30000)
|
|
41322
41365
|
});
|
|
41323
41366
|
if (!response.ok)
|
|
@@ -41326,7 +41369,7 @@ async function startPreviewShare(target) {
|
|
|
41326
41369
|
if (health.viewer !== "kennel-core" || health.root !== root) {
|
|
41327
41370
|
throw new Error("The Tailscale share is not serving the selected folder");
|
|
41328
41371
|
}
|
|
41329
|
-
return { url: url.href, username: shared
|
|
41372
|
+
return { url: url.href, username: shared?.username, password: shared?.password, close };
|
|
41330
41373
|
} catch (error) {
|
|
41331
41374
|
await close();
|
|
41332
41375
|
throw error;
|
|
@@ -41341,14 +41384,15 @@ async function share(input) {
|
|
|
41341
41384
|
console.log(`Sharing ${folder}`);
|
|
41342
41385
|
if (options.dryRun) {
|
|
41343
41386
|
const port = options.port ?? "5181";
|
|
41344
|
-
const url = options.tailscale ? `https://<tailnet-name>.ts.net/folder/${Buffer.from(folder).toString("base64url")}/notes` : popUrl(lanHost2(), port, { dryRun: true, host: "0.0.0.0", hot: false, lan: true, open: false, root: folder, port, tailscale: false, watch: false });
|
|
41387
|
+
const url = options.tailscale ? `https://<tailnet-name>.ts.net/folder/${Buffer.from(folder).toString("base64url")}/notes` : popUrl(lanHost2(), port, { dryRun: true, host: "0.0.0.0", hot: false, lan: true, open: false, root: folder, port, tailscale: false, watch: false, password: false });
|
|
41345
41388
|
console.log(url);
|
|
41346
|
-
console.log(`kennel serve --static ${options.tailscale ? "--tailscale" : "--lan"}${options.port ? ` --port ${options.port}` : ""} ${folder}`);
|
|
41389
|
+
console.log(`kennel serve --static ${options.tailscale ? "--tailscale" : "--lan"}${options.password ? " --password" : ""}${options.port ? ` --port ${options.port}` : ""} ${folder}`);
|
|
41347
41390
|
return;
|
|
41348
41391
|
}
|
|
41349
41392
|
await viewer([
|
|
41350
41393
|
"--static",
|
|
41351
41394
|
options.tailscale ? "--tailscale" : "--lan",
|
|
41395
|
+
...options.password ? ["--password"] : [],
|
|
41352
41396
|
...options.port ? ["--port", options.port] : [],
|
|
41353
41397
|
folder
|
|
41354
41398
|
]);
|
package/dist/site/terminal.js
CHANGED
|
@@ -85,11 +85,11 @@ function previewTerminal(url, target, startShare) {
|
|
|
85
85
|
].join(`
|
|
86
86
|
`));
|
|
87
87
|
};
|
|
88
|
-
const shareFolder = async () => {
|
|
88
|
+
const shareFolder = async (password = false) => {
|
|
89
89
|
status = "Preparing a private Tailscale link…";
|
|
90
90
|
render();
|
|
91
91
|
try {
|
|
92
|
-
share ??= startShare();
|
|
92
|
+
share ??= startShare(password);
|
|
93
93
|
let active;
|
|
94
94
|
try {
|
|
95
95
|
active = await share;
|
|
@@ -99,11 +99,14 @@ function previewTerminal(url, target, startShare) {
|
|
|
99
99
|
}
|
|
100
100
|
if (finished)
|
|
101
101
|
return;
|
|
102
|
-
status = `Tailscale link: ${active.url}
|
|
102
|
+
status = active.password ? `Tailscale link: ${active.url}
|
|
103
103
|
Username: ${active.username} · Password: ${active.password}
|
|
104
104
|
Password required. Send the password separately.
|
|
105
105
|
Connect Tailscale on your phone to the same tailnet.
|
|
106
106
|
Trusted HTTPS; no certificate installation required.
|
|
107
|
+
Keep this command running. Ctrl+C stops sharing.` : `Tailscale link: ${active.url}
|
|
108
|
+
Connect Tailscale on your phone to the same tailnet.
|
|
109
|
+
Trusted HTTPS; no password required on the tailnet.
|
|
107
110
|
Keep this command running. Ctrl+C stops sharing.`;
|
|
108
111
|
render();
|
|
109
112
|
try {
|
|
@@ -146,7 +149,8 @@ function previewTerminal(url, target, startShare) {
|
|
|
146
149
|
openPreview(url);
|
|
147
150
|
status = "Browser open requested.";
|
|
148
151
|
} },
|
|
149
|
-
{ label: "Share to another device", shortcut: "s", run: shareFolder },
|
|
152
|
+
{ label: "Share to another device", shortcut: "s", run: () => shareFolder(false) },
|
|
153
|
+
{ label: "Share link with password", shortcut: "p", run: () => shareFolder(true) },
|
|
150
154
|
{ label: "Exit", shortcut: "q", run: close }
|
|
151
155
|
];
|
|
152
156
|
const activate = async () => {
|
|
@@ -207,9 +211,9 @@ function previewTerminal(url, target, startShare) {
|
|
|
207
211
|
|
|
208
212
|
// packages/cli/src/site/terminal.ts
|
|
209
213
|
function previewTerminal2(url, target) {
|
|
210
|
-
return previewTerminal(url, target, async () => {
|
|
214
|
+
return previewTerminal(url, target, async (password = false) => {
|
|
211
215
|
const { startPreviewShare } = await import(new URL("../package/preview.js", import.meta.url).href);
|
|
212
|
-
return startPreviewShare(target);
|
|
216
|
+
return startPreviewShare({ ...target, ...password ? { password: true } : {} });
|
|
213
217
|
});
|
|
214
218
|
}
|
|
215
219
|
export {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kennel",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.37",
|
|
4
4
|
"description": "Open-source pop-up previews for AI output: markdown, HTML artifacts, email, images, code, tables, notes, and calendars, from the terminal or your agent session.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -21,6 +21,15 @@
|
|
|
21
21
|
"type": "array",
|
|
22
22
|
"items": { "$ref": "#/$defs/externalPlugin" }
|
|
23
23
|
},
|
|
24
|
+
"linkSharing": {
|
|
25
|
+
"description": "Preferred settings for sharing a viewer link.",
|
|
26
|
+
"type": "object",
|
|
27
|
+
"additionalProperties": false,
|
|
28
|
+
"properties": {
|
|
29
|
+
"mode": { "type": "string", "enum": ["tailscale", "lan"] },
|
|
30
|
+
"password": { "type": "boolean" }
|
|
31
|
+
}
|
|
32
|
+
},
|
|
24
33
|
"repositoryNotes": {
|
|
25
34
|
"description": "Opt-in Markdown notes per Git repository, stored in $XDG_CONFIG_HOME/kennel/repos/<uuid>/ and shown in that repository's Notes view. `kennel notes path` registers a repository only when enabled is true.",
|
|
26
35
|
"type": "object",
|