clankie 0.2.11 → 0.2.13
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 +0 -5
- package/dist/cli.js +93 -67
- package/package.json +1 -1
- package/web-ui-dist/_shell.html +1 -1
package/README.md
CHANGED
|
@@ -199,9 +199,6 @@ Each session maintains its own conversation history. Sessions persist across dae
|
|
|
199
199
|
Even though clankie lives in Slack, you also have CLI access:
|
|
200
200
|
|
|
201
201
|
```bash
|
|
202
|
-
# Interactive chat session (local terminal, uses pi's TUI)
|
|
203
|
-
clankie chat
|
|
204
|
-
|
|
205
202
|
# Send a one-off message (prints response and exits)
|
|
206
203
|
clankie send "What files are in the current directory?"
|
|
207
204
|
|
|
@@ -297,11 +294,9 @@ Logs are stored in `~/.clankie/logs/daemon.log`.
|
|
|
297
294
|
|
|
298
295
|
```bash
|
|
299
296
|
# Run directly with Bun (no build step)
|
|
300
|
-
bun src/cli.ts chat
|
|
301
297
|
bun src/cli.ts send "hello"
|
|
302
298
|
|
|
303
299
|
# Or use bun scripts
|
|
304
|
-
bun run dev chat
|
|
305
300
|
bun run dev send "hello"
|
|
306
301
|
|
|
307
302
|
# Build for production
|
package/dist/cli.js
CHANGED
|
@@ -203502,7 +203502,7 @@ var require_src6 = __commonJS((exports2) => {
|
|
|
203502
203502
|
// src/cli.ts
|
|
203503
203503
|
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
203504
203504
|
import { readFileSync as readFileSync26 } from "node:fs";
|
|
203505
|
-
import { join as
|
|
203505
|
+
import { join as join34 } from "node:path";
|
|
203506
203506
|
import * as readline3 from "node:readline/promises";
|
|
203507
203507
|
|
|
203508
203508
|
// node_modules/@mariozechner/pi-coding-agent/dist/index.js
|
|
@@ -297043,6 +297043,9 @@ class ThinkingSelectorComponent extends Container {
|
|
|
297043
297043
|
// src/cli.ts
|
|
297044
297044
|
var import_json52 = __toESM(require_lib5(), 1);
|
|
297045
297045
|
|
|
297046
|
+
// src/agent.ts
|
|
297047
|
+
import { join as join28 } from "node:path";
|
|
297048
|
+
|
|
297046
297049
|
// src/config.ts
|
|
297047
297050
|
var import_json5 = __toESM(require_lib5(), 1);
|
|
297048
297051
|
import { chmodSync as chmodSync3, existsSync as existsSync23, mkdirSync as mkdirSync9, readFileSync as readFileSync21, writeFileSync as writeFileSync10 } from "node:fs";
|
|
@@ -297213,15 +297216,21 @@ function createWorkspaceJailExtension(workspaceDir, allowedPaths = []) {
|
|
|
297213
297216
|
let match2;
|
|
297214
297217
|
while ((match2 = absolutePathPattern.exec(command)) !== null) {
|
|
297215
297218
|
const pathLike = match2[1];
|
|
297216
|
-
|
|
297219
|
+
const check = isPathAllowed(pathLike);
|
|
297220
|
+
if (!check.allowed) {
|
|
297217
297221
|
return {
|
|
297218
297222
|
allowed: false,
|
|
297219
|
-
reason: `Blocked: command references path outside
|
|
297223
|
+
reason: check.reason ?? `Blocked: command references path outside allowed scope: ${pathLike}`
|
|
297220
297224
|
};
|
|
297221
297225
|
}
|
|
297222
297226
|
}
|
|
297223
|
-
|
|
297224
|
-
|
|
297227
|
+
const cdMatch = command.match(/(?:^|\s)cd\s+([^\s;|&]+)/);
|
|
297228
|
+
const cdTarget = cdMatch?.[1];
|
|
297229
|
+
if (cdTarget && (cdTarget.startsWith("/") || cdTarget.startsWith("~"))) {
|
|
297230
|
+
const check = isPathAllowed(cdTarget);
|
|
297231
|
+
if (!check.allowed) {
|
|
297232
|
+
return { allowed: false, reason: check.reason ?? `Blocked: cd to disallowed path: ${cdTarget}` };
|
|
297233
|
+
}
|
|
297225
297234
|
}
|
|
297226
297235
|
const dotsPattern = /(?:^|\s)cd\s+(?:\.\.\/){3,}/;
|
|
297227
297236
|
if (dotsPattern.test(command)) {
|
|
@@ -297255,11 +297264,13 @@ function createWorkspaceJailExtension(workspaceDir, allowedPaths = []) {
|
|
|
297255
297264
|
return;
|
|
297256
297265
|
});
|
|
297257
297266
|
pi2.on("before_agent_start", async () => {
|
|
297267
|
+
const allowedPathsNote = normalizedAllowedPaths.length ? `
|
|
297268
|
+
Also allowed: ${normalizedAllowedPaths.join(", ")}` : "";
|
|
297258
297269
|
return {
|
|
297259
297270
|
systemPrompt: `
|
|
297260
297271
|
|
|
297261
|
-
IMPORTANT: You are restricted to working within the directory: ${workspaceDir}
|
|
297262
|
-
Do not access files, run commands, or reference paths outside
|
|
297272
|
+
IMPORTANT: You are restricted to working within the directory: ${workspaceDir}${allowedPathsNote}
|
|
297273
|
+
Do not access files, run commands, or reference paths outside the allowed directories.`
|
|
297263
297274
|
};
|
|
297264
297275
|
});
|
|
297265
297276
|
};
|
|
@@ -297275,7 +297286,9 @@ async function createSession(options2 = {}) {
|
|
|
297275
297286
|
const extensionFactories = [];
|
|
297276
297287
|
const restrictToWorkspace = config.agent?.restrictToWorkspace ?? true;
|
|
297277
297288
|
if (restrictToWorkspace) {
|
|
297278
|
-
const
|
|
297289
|
+
const configuredAllowedPaths = config.agent?.allowedPaths ?? [];
|
|
297290
|
+
const attachmentRoot = join28(getAppDir(), "attachments");
|
|
297291
|
+
const allowedPaths = Array.from(new Set([...configuredAllowedPaths, attachmentRoot]));
|
|
297279
297292
|
extensionFactories.push(createWorkspaceJailExtension(cwd, allowedPaths));
|
|
297280
297293
|
}
|
|
297281
297294
|
const loader = new DefaultResourceLoader({
|
|
@@ -297367,16 +297380,16 @@ function buildApiKeyProviders(oauthIds, dynamicProviderIds) {
|
|
|
297367
297380
|
|
|
297368
297381
|
// src/daemon.ts
|
|
297369
297382
|
import { existsSync as existsSync28, readFileSync as readFileSync24, unlinkSync as unlinkSync3, watch as watch3, writeFileSync as writeFileSync12 } from "node:fs";
|
|
297370
|
-
import { join as
|
|
297383
|
+
import { join as join32 } from "node:path";
|
|
297371
297384
|
|
|
297372
297385
|
// src/channels/slack.ts
|
|
297373
297386
|
var import_socket_mode = __toESM(require_src6(), 1);
|
|
297374
297387
|
var import_web_api = __toESM(require_dist18(), 1);
|
|
297375
297388
|
import { existsSync as existsSync25, mkdirSync as mkdirSync10, readFileSync as readFileSync22, writeFileSync as writeFileSync11 } from "node:fs";
|
|
297376
297389
|
import { homedir as homedir14 } from "node:os";
|
|
297377
|
-
import { join as
|
|
297390
|
+
import { join as join29 } from "node:path";
|
|
297378
297391
|
var SLACK_MAX_LENGTH = 4000;
|
|
297379
|
-
var ACTIVE_THREADS_FILE =
|
|
297392
|
+
var ACTIVE_THREADS_FILE = join29(homedir14(), ".clankie", "slack-active-threads.json");
|
|
297380
297393
|
var THREAD_TTL_DAYS = 7;
|
|
297381
297394
|
|
|
297382
297395
|
class SlackChannel {
|
|
@@ -297464,7 +297477,7 @@ class SlackChannel {
|
|
|
297464
297477
|
}
|
|
297465
297478
|
saveActiveThreads() {
|
|
297466
297479
|
try {
|
|
297467
|
-
const dir =
|
|
297480
|
+
const dir = join29(homedir14(), ".clankie");
|
|
297468
297481
|
if (!existsSync25(dir)) {
|
|
297469
297482
|
mkdirSync10(dir, { recursive: true, mode: 448 });
|
|
297470
297483
|
}
|
|
@@ -297607,7 +297620,7 @@ class SlackChannel {
|
|
|
297607
297620
|
// src/channels/web.ts
|
|
297608
297621
|
import * as crypto5 from "node:crypto";
|
|
297609
297622
|
import { existsSync as existsSync27, readdirSync as readdirSync11, readFileSync as readFileSync23, statSync as statSync12 } from "node:fs";
|
|
297610
|
-
import { join as
|
|
297623
|
+
import { join as join31, resolve as resolve11 } from "node:path";
|
|
297611
297624
|
|
|
297612
297625
|
// node_modules/@hono/node-server/dist/index.mjs
|
|
297613
297626
|
import { createServer as createServerHTTP } from "http";
|
|
@@ -299868,7 +299881,7 @@ var Hono2 = class extends Hono {
|
|
|
299868
299881
|
|
|
299869
299882
|
// src/sessions.ts
|
|
299870
299883
|
import { existsSync as existsSync26, mkdirSync as mkdirSync11 } from "node:fs";
|
|
299871
|
-
import { join as
|
|
299884
|
+
import { join as join30 } from "node:path";
|
|
299872
299885
|
var sessionCache = new Map;
|
|
299873
299886
|
var activeSessionNames = new Map;
|
|
299874
299887
|
var chatLocks = new Map;
|
|
@@ -299887,7 +299900,9 @@ async function getOrCreateSession(chatKey, config) {
|
|
|
299887
299900
|
const extensionFactories = [];
|
|
299888
299901
|
const restrictToWorkspace = config.agent?.restrictToWorkspace ?? true;
|
|
299889
299902
|
if (restrictToWorkspace) {
|
|
299890
|
-
const
|
|
299903
|
+
const configuredAllowedPaths = config.agent?.allowedPaths ?? [];
|
|
299904
|
+
const attachmentRoot = join30(getAppDir(), "attachments");
|
|
299905
|
+
const allowedPaths = Array.from(new Set([...configuredAllowedPaths, attachmentRoot]));
|
|
299891
299906
|
extensionFactories.push(createWorkspaceJailExtension(cwd, allowedPaths));
|
|
299892
299907
|
}
|
|
299893
299908
|
const loader = new DefaultResourceLoader({
|
|
@@ -299896,7 +299911,7 @@ async function getOrCreateSession(chatKey, config) {
|
|
|
299896
299911
|
extensionFactories
|
|
299897
299912
|
});
|
|
299898
299913
|
await loader.reload();
|
|
299899
|
-
const sessionDir =
|
|
299914
|
+
const sessionDir = join30(getAppDir(), "sessions", chatKey);
|
|
299900
299915
|
if (!existsSync26(sessionDir)) {
|
|
299901
299916
|
mkdirSync11(sessionDir, { recursive: true });
|
|
299902
299917
|
}
|
|
@@ -299968,7 +299983,7 @@ async function getOrCreateSession(chatKey, config) {
|
|
|
299968
299983
|
return session;
|
|
299969
299984
|
}
|
|
299970
299985
|
function listSessionNames(chatIdentifier) {
|
|
299971
|
-
const sessionsDir =
|
|
299986
|
+
const sessionsDir = join30(getAppDir(), "sessions");
|
|
299972
299987
|
if (!existsSync26(sessionsDir)) {
|
|
299973
299988
|
return [];
|
|
299974
299989
|
}
|
|
@@ -299978,7 +299993,7 @@ function listSessionNames(chatIdentifier) {
|
|
|
299978
299993
|
const sessionNames = new Set;
|
|
299979
299994
|
for (const entry of entries) {
|
|
299980
299995
|
if (entry.startsWith(`${chatIdentifier}_`)) {
|
|
299981
|
-
const entryPath =
|
|
299996
|
+
const entryPath = join30(sessionsDir, entry);
|
|
299982
299997
|
if (statSync12(entryPath).isDirectory()) {
|
|
299983
299998
|
const sessionName = entry.substring(chatIdentifier.length + 1);
|
|
299984
299999
|
if (sessionName) {
|
|
@@ -300012,13 +300027,13 @@ async function saveNonImageAttachments(attachments, chatKey) {
|
|
|
300012
300027
|
if (nonImages.length === 0)
|
|
300013
300028
|
return [];
|
|
300014
300029
|
const { mkdirSync: mkdirSync12, writeFileSync: writeFileSync12 } = await import("node:fs");
|
|
300015
|
-
const { join:
|
|
300016
|
-
const dir =
|
|
300030
|
+
const { join: join31 } = await import("node:path");
|
|
300031
|
+
const dir = join31(getAppDir(), "attachments", chatKey);
|
|
300017
300032
|
mkdirSync12(dir, { recursive: true });
|
|
300018
300033
|
const results = [];
|
|
300019
300034
|
for (const att of nonImages) {
|
|
300020
300035
|
const name = att.fileName || `file_${Date.now()}`;
|
|
300021
|
-
const filePath =
|
|
300036
|
+
const filePath = join31(dir, name);
|
|
300022
300037
|
writeFileSync12(filePath, Buffer.from(att.data, "base64"));
|
|
300023
300038
|
results.push({ fileName: name, path: filePath });
|
|
300024
300039
|
console.log(`[session] Saved attachment: ${filePath} (${att.mimeType})`);
|
|
@@ -300119,7 +300134,7 @@ class WebChannel {
|
|
|
300119
300134
|
if (pathname === "" || pathname === "/") {
|
|
300120
300135
|
pathname = "_shell.html";
|
|
300121
300136
|
}
|
|
300122
|
-
const _filePath =
|
|
300137
|
+
const _filePath = join31(this.options.staticDir, pathname);
|
|
300123
300138
|
const resolvedPath = resolve11(this.options.staticDir, pathname);
|
|
300124
300139
|
if (!resolvedPath.startsWith(resolve11(this.options.staticDir))) {
|
|
300125
300140
|
return c.text("Forbidden", 403);
|
|
@@ -300152,7 +300167,7 @@ class WebChannel {
|
|
|
300152
300167
|
}
|
|
300153
300168
|
});
|
|
300154
300169
|
}
|
|
300155
|
-
const shellPath =
|
|
300170
|
+
const shellPath = join31(this.options.staticDir, "_shell.html");
|
|
300156
300171
|
if (existsSync27(shellPath)) {
|
|
300157
300172
|
const content = readFileSync23(shellPath);
|
|
300158
300173
|
return new Response(content, {
|
|
@@ -300290,12 +300305,34 @@ class WebChannel {
|
|
|
300290
300305
|
this.sendError(ws2, inbound.sessionId, command.type, err instanceof Error ? err.message : String(err), commandId);
|
|
300291
300306
|
}
|
|
300292
300307
|
}
|
|
300308
|
+
injectAttachmentPaths(sessionId, message) {
|
|
300309
|
+
const attachmentNames = Array.from(message.matchAll(/\[Attached:\s*([^\]]+)\]/g)).map((match3) => match3[1]?.trim()).filter((name) => Boolean(name));
|
|
300310
|
+
if (attachmentNames.length === 0)
|
|
300311
|
+
return message;
|
|
300312
|
+
const attachmentDir = join31(getAppDir(), "attachments", sessionId);
|
|
300313
|
+
const resolved = [];
|
|
300314
|
+
for (const name of attachmentNames) {
|
|
300315
|
+
const path14 = join31(attachmentDir, name);
|
|
300316
|
+
if (existsSync27(path14)) {
|
|
300317
|
+
resolved.push(` - ${name}: ${path14}`);
|
|
300318
|
+
}
|
|
300319
|
+
}
|
|
300320
|
+
if (resolved.length === 0)
|
|
300321
|
+
return message;
|
|
300322
|
+
const suffix = `
|
|
300323
|
+
|
|
300324
|
+
[Attached files saved to disk]
|
|
300325
|
+
${resolved.join(`
|
|
300326
|
+
`)}`;
|
|
300327
|
+
return `${message}${suffix}`;
|
|
300328
|
+
}
|
|
300293
300329
|
async executeCommand(sessionId, session, command) {
|
|
300294
300330
|
const id = command.id;
|
|
300295
300331
|
switch (command.type) {
|
|
300296
300332
|
case "prompt": {
|
|
300297
300333
|
console.log(`[web] Executing prompt - session.sessionId: ${session.sessionId}, sessionFile: ${session.sessionFile}`);
|
|
300298
|
-
|
|
300334
|
+
const promptWithAttachmentPaths = this.injectAttachmentPaths(sessionId, command.message);
|
|
300335
|
+
session.prompt(promptWithAttachmentPaths, {
|
|
300299
300336
|
images: command.images,
|
|
300300
300337
|
streamingBehavior: command.streamingBehavior,
|
|
300301
300338
|
source: "rpc"
|
|
@@ -300320,13 +300357,13 @@ class WebChannel {
|
|
|
300320
300357
|
case "upload_attachment": {
|
|
300321
300358
|
const { fileName, data, mimeType } = command;
|
|
300322
300359
|
const { mkdirSync: mkdirSync12, writeFileSync: writeFileSync12 } = await import("node:fs");
|
|
300323
|
-
const { join:
|
|
300324
|
-
const dir =
|
|
300360
|
+
const { join: join32 } = await import("node:path");
|
|
300361
|
+
const dir = join32(getAppDir(), "attachments", sessionId);
|
|
300325
300362
|
mkdirSync12(dir, { recursive: true });
|
|
300326
300363
|
const timestamp = Date.now();
|
|
300327
300364
|
const sanitizedName = fileName.replace(/[^a-zA-Z0-9.-]/g, "_");
|
|
300328
300365
|
const uniqueFileName = `${timestamp}_${sanitizedName}`;
|
|
300329
|
-
const filePath =
|
|
300366
|
+
const filePath = join32(dir, uniqueFileName);
|
|
300330
300367
|
writeFileSync12(filePath, Buffer.from(data, "base64"));
|
|
300331
300368
|
console.log(`[web] Saved attachment: ${filePath} (${mimeType})`);
|
|
300332
300369
|
return {
|
|
@@ -300860,8 +300897,8 @@ class WebChannel {
|
|
|
300860
300897
|
try {
|
|
300861
300898
|
const files = readdirSync11(sessionPath).filter((f3) => f3.endsWith(".jsonl")).map((f3) => ({
|
|
300862
300899
|
name: f3,
|
|
300863
|
-
path:
|
|
300864
|
-
mtime: statSync12(
|
|
300900
|
+
path: join31(sessionPath, f3),
|
|
300901
|
+
mtime: statSync12(join31(sessionPath, f3)).mtime.getTime()
|
|
300865
300902
|
})).sort((a, b) => b.mtime - a.mtime);
|
|
300866
300903
|
if (files.length === 0)
|
|
300867
300904
|
return;
|
|
@@ -300888,13 +300925,13 @@ class WebChannel {
|
|
|
300888
300925
|
}
|
|
300889
300926
|
async listAllSessions() {
|
|
300890
300927
|
const sessions = [];
|
|
300891
|
-
const sessionsDir =
|
|
300928
|
+
const sessionsDir = join31(getAppDir(), "sessions");
|
|
300892
300929
|
if (!existsSync27(sessionsDir)) {
|
|
300893
300930
|
return sessions;
|
|
300894
300931
|
}
|
|
300895
300932
|
try {
|
|
300896
300933
|
const dirs = readdirSync11(sessionsDir);
|
|
300897
|
-
const webSessions = dirs.filter((dir) => dir.startsWith("web_")).map((dir) => ({ sessionId: dir, path:
|
|
300934
|
+
const webSessions = dirs.filter((dir) => dir.startsWith("web_")).map((dir) => ({ sessionId: dir, path: join31(sessionsDir, dir) })).filter(({ path: path14 }) => {
|
|
300898
300935
|
try {
|
|
300899
300936
|
return statSync12(path14).isDirectory();
|
|
300900
300937
|
} catch {
|
|
@@ -300965,7 +301002,7 @@ class WebChannel {
|
|
|
300965
301002
|
}
|
|
300966
301003
|
|
|
300967
301004
|
// src/daemon.ts
|
|
300968
|
-
var PID_FILE =
|
|
301005
|
+
var PID_FILE = join32(getAppDir(), "daemon.pid");
|
|
300969
301006
|
function isRunning() {
|
|
300970
301007
|
if (!existsSync28(PID_FILE))
|
|
300971
301008
|
return { running: false };
|
|
@@ -301223,23 +301260,23 @@ async function restartDaemon() {
|
|
|
301223
301260
|
import { execSync as execSync3 } from "node:child_process";
|
|
301224
301261
|
import { existsSync as existsSync29, mkdirSync as mkdirSync12, readFileSync as readFileSync25, unlinkSync as unlinkSync4, writeFileSync as writeFileSync13 } from "node:fs";
|
|
301225
301262
|
import { homedir as homedir15, platform as platform3 } from "node:os";
|
|
301226
|
-
import { dirname as dirname16, join as
|
|
301263
|
+
import { dirname as dirname16, join as join33 } from "node:path";
|
|
301227
301264
|
var SERVICE_NAME = "clankie";
|
|
301228
301265
|
var LAUNCHD_LABEL = "ai.clankie.daemon";
|
|
301229
301266
|
function resolveProgramArguments() {
|
|
301230
301267
|
const runtime = process.execPath || process.argv[0];
|
|
301231
301268
|
const thisFile = import.meta.filename;
|
|
301232
|
-
const cliEntry = thisFile.endsWith("service.ts") ?
|
|
301269
|
+
const cliEntry = thisFile.endsWith("service.ts") ? join33(dirname16(thisFile), "cli.ts") : join33(dirname16(thisFile), "cli.js");
|
|
301233
301270
|
return [runtime, cliEntry, "start", "--foreground"];
|
|
301234
301271
|
}
|
|
301235
301272
|
function systemdUnitPath() {
|
|
301236
|
-
return
|
|
301273
|
+
return join33(homedir15(), ".config", "systemd", "user", `${SERVICE_NAME}.service`);
|
|
301237
301274
|
}
|
|
301238
301275
|
function buildSystemdUnit() {
|
|
301239
301276
|
const args = resolveProgramArguments();
|
|
301240
301277
|
const execStart = args.map(systemdEscapeArg).join(" ");
|
|
301241
|
-
const workspace =
|
|
301242
|
-
const logDir =
|
|
301278
|
+
const workspace = join33(getAppDir(), "workspace");
|
|
301279
|
+
const logDir = join33(getAppDir(), "logs");
|
|
301243
301280
|
return [
|
|
301244
301281
|
"[Unit]",
|
|
301245
301282
|
`Description=clankie — personal AI assistant daemon`,
|
|
@@ -301254,8 +301291,8 @@ function buildSystemdUnit() {
|
|
|
301254
301291
|
"KillMode=process",
|
|
301255
301292
|
`Environment=HOME=${homedir15()}`,
|
|
301256
301293
|
`Environment=PATH=${process.env.PATH}`,
|
|
301257
|
-
`StandardOutput=append:${
|
|
301258
|
-
`StandardError=append:${
|
|
301294
|
+
`StandardOutput=append:${join33(logDir, "daemon.log")}`,
|
|
301295
|
+
`StandardError=append:${join33(logDir, "daemon.log")}`,
|
|
301259
301296
|
"",
|
|
301260
301297
|
"[Install]",
|
|
301261
301298
|
"WantedBy=default.target",
|
|
@@ -301287,8 +301324,8 @@ async function installSystemd() {
|
|
|
301287
301324
|
}
|
|
301288
301325
|
}
|
|
301289
301326
|
const unitPath = systemdUnitPath();
|
|
301290
|
-
const logDir =
|
|
301291
|
-
const workspace =
|
|
301327
|
+
const logDir = join33(getAppDir(), "logs");
|
|
301328
|
+
const workspace = join33(getAppDir(), "workspace");
|
|
301292
301329
|
mkdirSync12(dirname16(unitPath), { recursive: true });
|
|
301293
301330
|
mkdirSync12(logDir, { recursive: true });
|
|
301294
301331
|
mkdirSync12(workspace, { recursive: true });
|
|
@@ -301317,7 +301354,7 @@ async function installSystemd() {
|
|
|
301317
301354
|
console.log(`
|
|
301318
301355
|
✓ Installed and started systemd service: ${SERVICE_NAME}.service`);
|
|
301319
301356
|
console.log(` Logs: journalctl --user -u ${SERVICE_NAME} -f`);
|
|
301320
|
-
console.log(` Or: ${
|
|
301357
|
+
console.log(` Or: ${join33(logDir, "daemon.log")}`);
|
|
301321
301358
|
}
|
|
301322
301359
|
async function uninstallSystemd() {
|
|
301323
301360
|
const unitPath = systemdUnitPath();
|
|
@@ -301332,7 +301369,7 @@ async function uninstallSystemd() {
|
|
|
301332
301369
|
console.log(`✓ Uninstalled systemd service.`);
|
|
301333
301370
|
}
|
|
301334
301371
|
function logsSystemd() {
|
|
301335
|
-
const logFile =
|
|
301372
|
+
const logFile = join33(getAppDir(), "logs", "daemon.log");
|
|
301336
301373
|
console.log(`Log file: ${logFile}
|
|
301337
301374
|
`);
|
|
301338
301375
|
const result = execSafe(`journalctl --user -u ${SERVICE_NAME} --no-pager -n 50`);
|
|
@@ -301354,15 +301391,15 @@ function statusSystemd() {
|
|
|
301354
301391
|
console.log(result.stdout || result.stderr || "Service not found.");
|
|
301355
301392
|
}
|
|
301356
301393
|
function launchdPlistPath() {
|
|
301357
|
-
return
|
|
301394
|
+
return join33(homedir15(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
|
|
301358
301395
|
}
|
|
301359
301396
|
function plistEscape(value2) {
|
|
301360
301397
|
return value2.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
301361
301398
|
}
|
|
301362
301399
|
function buildLaunchdPlist() {
|
|
301363
301400
|
const args = resolveProgramArguments();
|
|
301364
|
-
const logDir =
|
|
301365
|
-
const workspace =
|
|
301401
|
+
const logDir = join33(getAppDir(), "logs");
|
|
301402
|
+
const workspace = join33(getAppDir(), "workspace");
|
|
301366
301403
|
const argsXml = args.map((a) => ` <string>${plistEscape(a)}</string>`).join(`
|
|
301367
301404
|
`);
|
|
301368
301405
|
const envVars = {};
|
|
@@ -301390,9 +301427,9 @@ ${argsXml}
|
|
|
301390
301427
|
<key>WorkingDirectory</key>
|
|
301391
301428
|
<string>${plistEscape(workspace)}</string>
|
|
301392
301429
|
<key>StandardOutPath</key>
|
|
301393
|
-
<string>${plistEscape(
|
|
301430
|
+
<string>${plistEscape(join33(logDir, "daemon.log"))}</string>
|
|
301394
301431
|
<key>StandardErrorPath</key>
|
|
301395
|
-
<string>${plistEscape(
|
|
301432
|
+
<string>${plistEscape(join33(logDir, "daemon.log"))}</string>
|
|
301396
301433
|
<key>EnvironmentVariables</key>
|
|
301397
301434
|
<dict>
|
|
301398
301435
|
${envXml}
|
|
@@ -301403,8 +301440,8 @@ ${envXml}
|
|
|
301403
301440
|
}
|
|
301404
301441
|
async function installLaunchd() {
|
|
301405
301442
|
const plistPath = launchdPlistPath();
|
|
301406
|
-
const logDir =
|
|
301407
|
-
const workspace =
|
|
301443
|
+
const logDir = join33(getAppDir(), "logs");
|
|
301444
|
+
const workspace = join33(getAppDir(), "workspace");
|
|
301408
301445
|
mkdirSync12(dirname16(plistPath), { recursive: true });
|
|
301409
301446
|
mkdirSync12(logDir, { recursive: true });
|
|
301410
301447
|
mkdirSync12(workspace, { recursive: true });
|
|
@@ -301421,7 +301458,7 @@ async function installLaunchd() {
|
|
|
301421
301458
|
}
|
|
301422
301459
|
console.log(`
|
|
301423
301460
|
✓ Installed and started launchd agent: ${LAUNCHD_LABEL}`);
|
|
301424
|
-
console.log(` Logs: tail -f ${
|
|
301461
|
+
console.log(` Logs: tail -f ${join33(logDir, "daemon.log")}`);
|
|
301425
301462
|
}
|
|
301426
301463
|
async function uninstallLaunchd() {
|
|
301427
301464
|
const plistPath = launchdPlistPath();
|
|
@@ -301435,7 +301472,7 @@ async function uninstallLaunchd() {
|
|
|
301435
301472
|
console.log(`✓ Uninstalled launchd agent.`);
|
|
301436
301473
|
}
|
|
301437
301474
|
function logsLaunchd() {
|
|
301438
|
-
const logFile =
|
|
301475
|
+
const logFile = join33(getAppDir(), "logs", "daemon.log");
|
|
301439
301476
|
console.log(`Log file: ${logFile}
|
|
301440
301477
|
`);
|
|
301441
301478
|
if (existsSync29(logFile)) {
|
|
@@ -301493,7 +301530,7 @@ function showServiceLogs() {
|
|
|
301493
301530
|
} else if (isLinux) {
|
|
301494
301531
|
logsSystemd();
|
|
301495
301532
|
} else {
|
|
301496
|
-
const logFile =
|
|
301533
|
+
const logFile = join33(getAppDir(), "logs", "daemon.log");
|
|
301497
301534
|
if (existsSync29(logFile)) {
|
|
301498
301535
|
console.log(readFileSync25(logFile, "utf-8"));
|
|
301499
301536
|
} else {
|
|
@@ -301518,7 +301555,6 @@ function printHelp2() {
|
|
|
301518
301555
|
|
|
301519
301556
|
Usage:
|
|
301520
301557
|
clankie send "<message>" Send a message, print response, exit
|
|
301521
|
-
clankie chat Start interactive chat session
|
|
301522
301558
|
clankie init Set up clankie (generates auth token, configures web channel)
|
|
301523
301559
|
clankie login Authenticate with your AI provider
|
|
301524
301560
|
clankie start [--foreground] Start the daemon (foreground by default)
|
|
@@ -301583,7 +301619,7 @@ Credentials are stored at ~/.clankie/auth.json (separate from pi's auth).
|
|
|
301583
301619
|
`);
|
|
301584
301620
|
}
|
|
301585
301621
|
function printVersion() {
|
|
301586
|
-
const packagePath =
|
|
301622
|
+
const packagePath = join34(import.meta.dirname, "..", "package.json");
|
|
301587
301623
|
try {
|
|
301588
301624
|
const pkg3 = JSON.parse(readFileSync26(packagePath, "utf-8"));
|
|
301589
301625
|
console.log(`clankie ${pkg3.version}`);
|
|
@@ -301608,17 +301644,6 @@ Usage: clankie send "<message>"`);
|
|
|
301608
301644
|
initialMessage: message
|
|
301609
301645
|
});
|
|
301610
301646
|
}
|
|
301611
|
-
async function cmdChat(args) {
|
|
301612
|
-
const initialMessage = args.join(" ").trim() || undefined;
|
|
301613
|
-
const { session, modelFallbackMessage } = await createSession({
|
|
301614
|
-
continueRecent: true
|
|
301615
|
-
});
|
|
301616
|
-
const mode = new InteractiveMode(session, {
|
|
301617
|
-
modelFallbackMessage,
|
|
301618
|
-
initialMessage
|
|
301619
|
-
});
|
|
301620
|
-
await mode.run();
|
|
301621
|
-
}
|
|
301622
301647
|
async function getApiKeyProvidersForLogin(oauthIds) {
|
|
301623
301648
|
const dynamicProviderIds = new Set;
|
|
301624
301649
|
try {
|
|
@@ -301910,7 +301935,8 @@ async function main2() {
|
|
|
301910
301935
|
await cmdSend(rest2);
|
|
301911
301936
|
break;
|
|
301912
301937
|
case "chat":
|
|
301913
|
-
|
|
301938
|
+
console.error("The 'chat' command has been removed. Use the Web UI or 'clankie send'.");
|
|
301939
|
+
process.exit(1);
|
|
301914
301940
|
break;
|
|
301915
301941
|
case "init":
|
|
301916
301942
|
await cmdInit(rest2);
|
package/package.json
CHANGED
package/web-ui-dist/_shell.html
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
<!DOCTYPE html><html lang="en" class="dark"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><title>clankie — Personal AI Assistant</title><link rel="modulepreload" href="/assets/main-UkFzTTck.js"/><link rel="modulepreload" href="/assets/index-CdxpJyJ4.js"/><link rel="icon" type="image/svg+xml" href="/favicon.svg"/><link rel="stylesheet" href="/assets/styles-DAZ1IN1z.css"/></head><body><div data-slot="sidebar-wrapper" style="--sidebar-width:16rem;--sidebar-width-icon:3rem" class="group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full"><div class="group peer text-sidebar-foreground hidden md:block" data-state="expanded" data-collapsible="" data-variant="inset" data-side="left" data-slot="sidebar"><div data-slot="sidebar-gap" class="transition-[width] duration-200 ease-linear relative w-(--sidebar-width) bg-transparent group-data-[collapsible=offcanvas]:w-0 group-data-[side=right]:rotate-180 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]"></div><div data-slot="sidebar-container" data-side="left" class="fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear data-[side=left]:left-0 data-[side=left]:group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)] data-[side=right]:right-0 data-[side=right]:group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)] md:flex p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]"><div data-sidebar="sidebar" data-slot="sidebar-inner" class="bg-sidebar group-data-[variant=floating]:ring-sidebar-border group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:shadow-sm group-data-[variant=floating]:ring-1 flex size-full flex-col"><div data-slot="sidebar-header" data-sidebar="header" class="gap-2 p-2 flex flex-col"><ul data-slot="sidebar-menu" data-sidebar="menu" class="gap-0 flex w-full min-w-0 flex-col"><li data-slot="sidebar-menu-item" data-sidebar="menu-item" class="group/menu-item relative"><a data-slot="sidebar-menu-button" data-sidebar="menu-button" data-size="lg" class="ring-sidebar-ring active:bg-sidebar-accent active:text-sidebar-accent-foreground data-active:bg-sidebar-accent data-active:text-sidebar-accent-foreground data-open:hover:bg-sidebar-accent data-open:hover:text-sidebar-accent-foreground gap-2 rounded-md p-2 text-left transition-[width,height,padding] group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! focus-visible:ring-2 data-active:font-medium peer/menu-button flex w-full items-center overflow-hidden outline-hidden group/menu-button disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&_svg]:size-4 [&_svg]:shrink-0 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground h-12 text-sm group-data-[collapsible=icon]:p-0! active" href="/" data-status="active" aria-current="page"><div class="flex items-center gap-2"><div class="flex items-center justify-center w-8 h-8 rounded-lg bg-primary/10 border border-primary/20"><span class="text-sm font-mono font-bold text-primary">c/</span></div><span class="text-base font-mono font-semibold tracking-tight">clankie</span></div></a></li></ul></div><div data-slot="sidebar-content" data-sidebar="content" class="no-scrollbar gap-0 flex min-h-0 flex-1 flex-col overflow-auto group-data-[collapsible=icon]:overflow-hidden"><div data-slot="sidebar-group" data-sidebar="group" class="p-2 relative flex w-full min-w-0 flex-col"><div data-slot="sidebar-group-content" data-sidebar="group-content" class="text-sm w-full"><ul data-slot="sidebar-menu" data-sidebar="menu" class="gap-0 flex w-full min-w-0 flex-col"><li data-slot="sidebar-menu-item" data-sidebar="menu-item" class="group/menu-item relative"><button type="button" id="base-ui-_R_6qb6_" data-slot="sidebar-menu-button" data-sidebar="menu-button" data-size="default" class="ring-sidebar-ring data-active:bg-sidebar-accent data-active:text-sidebar-accent-foreground data-open:hover:bg-sidebar-accent data-open:hover:text-sidebar-accent-foreground gap-2 rounded-md p-2 text-left transition-[width,height,padding] group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! focus-visible:ring-2 data-active:font-medium peer/menu-button flex w-full items-center overflow-hidden outline-hidden group/menu-button disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&_svg]:size-4 [&_svg]:shrink-0 h-8 text-sm bg-primary text-primary-foreground hover:bg-primary/90 hover:text-primary-foreground active:bg-primary/90 active:text-primary-foreground duration-200 ease-linear"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-plus" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="M8 12h8"></path><path d="M12 8v8"></path></svg><span>Create Chat</span></button></li></ul></div></div><div data-slot="sidebar-group" data-sidebar="group" class="p-2 relative flex w-full min-w-0 flex-col group-data-[collapsible=icon]:hidden"><div data-slot="sidebar-group-label" data-sidebar="group-label" class="text-sidebar-foreground/70 ring-sidebar-ring h-8 rounded-md px-2 text-xs font-medium transition-[margin,opacity] duration-200 ease-linear group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0 focus-visible:ring-2 [&>svg]:size-4 flex shrink-0 items-center outline-hidden [&>svg]:shrink-0">Recent Sessions</div><ul data-slot="sidebar-menu" data-sidebar="menu" class="gap-0 flex w-full min-w-0 flex-col"><li data-slot="sidebar-menu-item" data-sidebar="menu-item" class="group/menu-item relative"><button type="button" data-slot="sidebar-menu-button" data-sidebar="menu-button" data-size="default" class="ring-sidebar-ring active:bg-sidebar-accent active:text-sidebar-accent-foreground data-active:bg-sidebar-accent data-active:text-sidebar-accent-foreground data-open:hover:bg-sidebar-accent data-open:hover:text-sidebar-accent-foreground gap-2 rounded-md p-2 text-left transition-[width,height,padding] group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! focus-visible:ring-2 data-active:font-medium peer/menu-button flex w-full items-center overflow-hidden outline-hidden group/menu-button disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&_svg]:size-4 [&_svg]:shrink-0 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground h-8 text-sm" disabled=""><span class="text-sidebar-foreground/70">No sessions yet</span></button></li></ul></div><div data-slot="sidebar-group" data-sidebar="group" class="p-2 relative flex w-full min-w-0 flex-col mt-auto"><div data-slot="sidebar-group-content" data-sidebar="group-content" class="text-sm w-full"><ul data-slot="sidebar-menu" data-sidebar="menu" class="gap-0 flex w-full min-w-0 flex-col"><li data-slot="sidebar-menu-item" data-sidebar="menu-item" class="group/menu-item relative"><a data-slot="sidebar-menu-button" data-sidebar="menu-button" data-size="default" href="/settings" class="ring-sidebar-ring active:bg-sidebar-accent active:text-sidebar-accent-foreground data-active:bg-sidebar-accent data-active:text-sidebar-accent-foreground data-open:hover:bg-sidebar-accent data-open:hover:text-sidebar-accent-foreground gap-2 rounded-md p-2 text-left transition-[width,height,padding] group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! focus-visible:ring-2 data-active:font-medium peer/menu-button flex w-full items-center overflow-hidden outline-hidden group/menu-button disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&_svg]:size-4 [&_svg]:shrink-0 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground h-8 text-sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-settings" aria-hidden="true"><path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915"></path><circle cx="12" cy="12" r="3"></circle></svg><span>Settings</span></a></li><li data-slot="sidebar-menu-item" data-sidebar="menu-item" class="group/menu-item relative"><a data-slot="sidebar-menu-button" data-sidebar="menu-button" data-size="default" href="/extensions" class="ring-sidebar-ring active:bg-sidebar-accent active:text-sidebar-accent-foreground data-active:bg-sidebar-accent data-active:text-sidebar-accent-foreground data-open:hover:bg-sidebar-accent data-open:hover:text-sidebar-accent-foreground gap-2 rounded-md p-2 text-left transition-[width,height,padding] group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! focus-visible:ring-2 data-active:font-medium peer/menu-button flex w-full items-center overflow-hidden outline-hidden group/menu-button disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&_svg]:size-4 [&_svg]:shrink-0 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground h-8 text-sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-puzzle" aria-hidden="true"><path d="M15.39 4.39a1 1 0 0 0 1.68-.474 2.5 2.5 0 1 1 3.014 3.015 1 1 0 0 0-.474 1.68l1.683 1.682a2.414 2.414 0 0 1 0 3.414L19.61 15.39a1 1 0 0 1-1.68-.474 2.5 2.5 0 1 0-3.014 3.015 1 1 0 0 1 .474 1.68l-1.683 1.682a2.414 2.414 0 0 1-3.414 0L8.61 19.61a1 1 0 0 0-1.68.474 2.5 2.5 0 1 1-3.014-3.015 1 1 0 0 0 .474-1.68l-1.683-1.682a2.414 2.414 0 0 1 0-3.414L4.39 8.61a1 1 0 0 1 1.68.474 2.5 2.5 0 1 0 3.014-3.015 1 1 0 0 1-.474-1.68l1.683-1.682a2.414 2.414 0 0 1 3.414 0z"></path></svg><span>Extensions</span></a></li><li data-slot="sidebar-menu-item" data-sidebar="menu-item" class="group/menu-item relative"><div class="px-2 py-1.5"><span data-slot="badge" data-variant="secondary" class="h-5 gap-1 rounded-4xl border px-2 py-0.5 text-xs font-medium transition-all has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&>svg]:size-3! inline-flex items-center whitespace-nowrap shrink-0 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive overflow-hidden group/badge [a]:hover:bg-secondary/80 bg-red-500/10 text-red-500 border-red-500/20 w-full justify-start"><div class="size-2 rounded-full mr-2 bg-red-500 "></div>Disconnected</span></div></li></ul></div></div></div></div></div></div><main data-slot="sidebar-inset" class="bg-background md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2 relative flex w-full flex-1 flex-col"><div class="flex flex-1 flex-col overflow-hidden"><header class="flex h-14 shrink-0 items-center gap-2 border-b px-4"><button type="button" tabindex="0" data-slot="sidebar-trigger" data-sidebar="trigger" class="focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-3 aria-invalid:ring-3 [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-panel-left" aria-hidden="true"><rect width="18" height="18" x="3" y="3" rx="2"></rect><path d="M9 3v18"></path></svg><span class="sr-only">Toggle Sidebar</span></button></header><!--$--><!--$--><!--/$--><script></script><!--/$--></div></main></div><script class="$tsr" id="$tsr-stream-barrier">(self.$R=self.$R||{})["tsr"]=[];self.$_TSR={h(){this.hydrated=!0,this.c()},e(){this.streamEnded=!0,this.c()},c(){this.hydrated&&this.streamEnded&&(delete self.$_TSR,delete self.$R.tsr)},p(e){this.initialized?e():this.buffer.push(e)},buffer:[]};
|
|
2
|
-
;$_TSR.router=($R=>$R[0]={manifest:$R[1]={routes:$R[2]={__root__:$R[3]={preloads:$R[4]=["/assets/main-UkFzTTck.js"],assets:$R[5]=[$R[6]={tag:"script",attrs:$R[7]={type:"module",async:!0},children:"import(\"/assets/main-UkFzTTck.js\")"}]}}},matches:$R[8]=[$R[9]={i:"__root__",u:
|
|
2
|
+
;$_TSR.router=($R=>$R[0]={manifest:$R[1]={routes:$R[2]={__root__:$R[3]={preloads:$R[4]=["/assets/main-UkFzTTck.js"],assets:$R[5]=[$R[6]={tag:"script",attrs:$R[7]={type:"module",async:!0},children:"import(\"/assets/main-UkFzTTck.js\")"}]}}},matches:$R[8]=[$R[9]={i:"__root__",u:1772487482351,s:"success",ssr:!0}],lastMatchId:"__root__"})($R["tsr"]);$_TSR.e();document.currentScript.remove()</script><script type="module" async="">import("/assets/main-UkFzTTck.js")</script></body></html>
|