clankie 0.2.11 → 0.2.12

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.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 join33 } from "node:path";
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";
@@ -297275,7 +297278,9 @@ async function createSession(options2 = {}) {
297275
297278
  const extensionFactories = [];
297276
297279
  const restrictToWorkspace = config.agent?.restrictToWorkspace ?? true;
297277
297280
  if (restrictToWorkspace) {
297278
- const allowedPaths = config.agent?.allowedPaths ?? [];
297281
+ const configuredAllowedPaths = config.agent?.allowedPaths ?? [];
297282
+ const attachmentRoot = join28(getAppDir(), "attachments");
297283
+ const allowedPaths = Array.from(new Set([...configuredAllowedPaths, attachmentRoot]));
297279
297284
  extensionFactories.push(createWorkspaceJailExtension(cwd, allowedPaths));
297280
297285
  }
297281
297286
  const loader = new DefaultResourceLoader({
@@ -297367,16 +297372,16 @@ function buildApiKeyProviders(oauthIds, dynamicProviderIds) {
297367
297372
 
297368
297373
  // src/daemon.ts
297369
297374
  import { existsSync as existsSync28, readFileSync as readFileSync24, unlinkSync as unlinkSync3, watch as watch3, writeFileSync as writeFileSync12 } from "node:fs";
297370
- import { join as join31 } from "node:path";
297375
+ import { join as join32 } from "node:path";
297371
297376
 
297372
297377
  // src/channels/slack.ts
297373
297378
  var import_socket_mode = __toESM(require_src6(), 1);
297374
297379
  var import_web_api = __toESM(require_dist18(), 1);
297375
297380
  import { existsSync as existsSync25, mkdirSync as mkdirSync10, readFileSync as readFileSync22, writeFileSync as writeFileSync11 } from "node:fs";
297376
297381
  import { homedir as homedir14 } from "node:os";
297377
- import { join as join28 } from "node:path";
297382
+ import { join as join29 } from "node:path";
297378
297383
  var SLACK_MAX_LENGTH = 4000;
297379
- var ACTIVE_THREADS_FILE = join28(homedir14(), ".clankie", "slack-active-threads.json");
297384
+ var ACTIVE_THREADS_FILE = join29(homedir14(), ".clankie", "slack-active-threads.json");
297380
297385
  var THREAD_TTL_DAYS = 7;
297381
297386
 
297382
297387
  class SlackChannel {
@@ -297464,7 +297469,7 @@ class SlackChannel {
297464
297469
  }
297465
297470
  saveActiveThreads() {
297466
297471
  try {
297467
- const dir = join28(homedir14(), ".clankie");
297472
+ const dir = join29(homedir14(), ".clankie");
297468
297473
  if (!existsSync25(dir)) {
297469
297474
  mkdirSync10(dir, { recursive: true, mode: 448 });
297470
297475
  }
@@ -297607,7 +297612,7 @@ class SlackChannel {
297607
297612
  // src/channels/web.ts
297608
297613
  import * as crypto5 from "node:crypto";
297609
297614
  import { existsSync as existsSync27, readdirSync as readdirSync11, readFileSync as readFileSync23, statSync as statSync12 } from "node:fs";
297610
- import { join as join30, resolve as resolve11 } from "node:path";
297615
+ import { join as join31, resolve as resolve11 } from "node:path";
297611
297616
 
297612
297617
  // node_modules/@hono/node-server/dist/index.mjs
297613
297618
  import { createServer as createServerHTTP } from "http";
@@ -299868,7 +299873,7 @@ var Hono2 = class extends Hono {
299868
299873
 
299869
299874
  // src/sessions.ts
299870
299875
  import { existsSync as existsSync26, mkdirSync as mkdirSync11 } from "node:fs";
299871
- import { join as join29 } from "node:path";
299876
+ import { join as join30 } from "node:path";
299872
299877
  var sessionCache = new Map;
299873
299878
  var activeSessionNames = new Map;
299874
299879
  var chatLocks = new Map;
@@ -299887,7 +299892,9 @@ async function getOrCreateSession(chatKey, config) {
299887
299892
  const extensionFactories = [];
299888
299893
  const restrictToWorkspace = config.agent?.restrictToWorkspace ?? true;
299889
299894
  if (restrictToWorkspace) {
299890
- const allowedPaths = config.agent?.allowedPaths ?? [];
299895
+ const configuredAllowedPaths = config.agent?.allowedPaths ?? [];
299896
+ const attachmentRoot = join30(getAppDir(), "attachments");
299897
+ const allowedPaths = Array.from(new Set([...configuredAllowedPaths, attachmentRoot]));
299891
299898
  extensionFactories.push(createWorkspaceJailExtension(cwd, allowedPaths));
299892
299899
  }
299893
299900
  const loader = new DefaultResourceLoader({
@@ -299896,7 +299903,7 @@ async function getOrCreateSession(chatKey, config) {
299896
299903
  extensionFactories
299897
299904
  });
299898
299905
  await loader.reload();
299899
- const sessionDir = join29(getAppDir(), "sessions", chatKey);
299906
+ const sessionDir = join30(getAppDir(), "sessions", chatKey);
299900
299907
  if (!existsSync26(sessionDir)) {
299901
299908
  mkdirSync11(sessionDir, { recursive: true });
299902
299909
  }
@@ -299968,7 +299975,7 @@ async function getOrCreateSession(chatKey, config) {
299968
299975
  return session;
299969
299976
  }
299970
299977
  function listSessionNames(chatIdentifier) {
299971
- const sessionsDir = join29(getAppDir(), "sessions");
299978
+ const sessionsDir = join30(getAppDir(), "sessions");
299972
299979
  if (!existsSync26(sessionsDir)) {
299973
299980
  return [];
299974
299981
  }
@@ -299978,7 +299985,7 @@ function listSessionNames(chatIdentifier) {
299978
299985
  const sessionNames = new Set;
299979
299986
  for (const entry of entries) {
299980
299987
  if (entry.startsWith(`${chatIdentifier}_`)) {
299981
- const entryPath = join29(sessionsDir, entry);
299988
+ const entryPath = join30(sessionsDir, entry);
299982
299989
  if (statSync12(entryPath).isDirectory()) {
299983
299990
  const sessionName = entry.substring(chatIdentifier.length + 1);
299984
299991
  if (sessionName) {
@@ -300012,13 +300019,13 @@ async function saveNonImageAttachments(attachments, chatKey) {
300012
300019
  if (nonImages.length === 0)
300013
300020
  return [];
300014
300021
  const { mkdirSync: mkdirSync12, writeFileSync: writeFileSync12 } = await import("node:fs");
300015
- const { join: join30 } = await import("node:path");
300016
- const dir = join30(getAppDir(), "attachments", chatKey);
300022
+ const { join: join31 } = await import("node:path");
300023
+ const dir = join31(getAppDir(), "attachments", chatKey);
300017
300024
  mkdirSync12(dir, { recursive: true });
300018
300025
  const results = [];
300019
300026
  for (const att of nonImages) {
300020
300027
  const name = att.fileName || `file_${Date.now()}`;
300021
- const filePath = join30(dir, name);
300028
+ const filePath = join31(dir, name);
300022
300029
  writeFileSync12(filePath, Buffer.from(att.data, "base64"));
300023
300030
  results.push({ fileName: name, path: filePath });
300024
300031
  console.log(`[session] Saved attachment: ${filePath} (${att.mimeType})`);
@@ -300119,7 +300126,7 @@ class WebChannel {
300119
300126
  if (pathname === "" || pathname === "/") {
300120
300127
  pathname = "_shell.html";
300121
300128
  }
300122
- const _filePath = join30(this.options.staticDir, pathname);
300129
+ const _filePath = join31(this.options.staticDir, pathname);
300123
300130
  const resolvedPath = resolve11(this.options.staticDir, pathname);
300124
300131
  if (!resolvedPath.startsWith(resolve11(this.options.staticDir))) {
300125
300132
  return c.text("Forbidden", 403);
@@ -300152,7 +300159,7 @@ class WebChannel {
300152
300159
  }
300153
300160
  });
300154
300161
  }
300155
- const shellPath = join30(this.options.staticDir, "_shell.html");
300162
+ const shellPath = join31(this.options.staticDir, "_shell.html");
300156
300163
  if (existsSync27(shellPath)) {
300157
300164
  const content = readFileSync23(shellPath);
300158
300165
  return new Response(content, {
@@ -300290,12 +300297,34 @@ class WebChannel {
300290
300297
  this.sendError(ws2, inbound.sessionId, command.type, err instanceof Error ? err.message : String(err), commandId);
300291
300298
  }
300292
300299
  }
300300
+ injectAttachmentPaths(sessionId, message) {
300301
+ const attachmentNames = Array.from(message.matchAll(/\[Attached:\s*([^\]]+)\]/g)).map((match3) => match3[1]?.trim()).filter((name) => Boolean(name));
300302
+ if (attachmentNames.length === 0)
300303
+ return message;
300304
+ const attachmentDir = join31(getAppDir(), "attachments", sessionId);
300305
+ const resolved = [];
300306
+ for (const name of attachmentNames) {
300307
+ const path14 = join31(attachmentDir, name);
300308
+ if (existsSync27(path14)) {
300309
+ resolved.push(` - ${name}: ${path14}`);
300310
+ }
300311
+ }
300312
+ if (resolved.length === 0)
300313
+ return message;
300314
+ const suffix = `
300315
+
300316
+ [Attached files saved to disk]
300317
+ ${resolved.join(`
300318
+ `)}`;
300319
+ return `${message}${suffix}`;
300320
+ }
300293
300321
  async executeCommand(sessionId, session, command) {
300294
300322
  const id = command.id;
300295
300323
  switch (command.type) {
300296
300324
  case "prompt": {
300297
300325
  console.log(`[web] Executing prompt - session.sessionId: ${session.sessionId}, sessionFile: ${session.sessionFile}`);
300298
- session.prompt(command.message, {
300326
+ const promptWithAttachmentPaths = this.injectAttachmentPaths(sessionId, command.message);
300327
+ session.prompt(promptWithAttachmentPaths, {
300299
300328
  images: command.images,
300300
300329
  streamingBehavior: command.streamingBehavior,
300301
300330
  source: "rpc"
@@ -300320,13 +300349,13 @@ class WebChannel {
300320
300349
  case "upload_attachment": {
300321
300350
  const { fileName, data, mimeType } = command;
300322
300351
  const { mkdirSync: mkdirSync12, writeFileSync: writeFileSync12 } = await import("node:fs");
300323
- const { join: join31 } = await import("node:path");
300324
- const dir = join31(getAppDir(), "attachments", sessionId);
300352
+ const { join: join32 } = await import("node:path");
300353
+ const dir = join32(getAppDir(), "attachments", sessionId);
300325
300354
  mkdirSync12(dir, { recursive: true });
300326
300355
  const timestamp = Date.now();
300327
300356
  const sanitizedName = fileName.replace(/[^a-zA-Z0-9.-]/g, "_");
300328
300357
  const uniqueFileName = `${timestamp}_${sanitizedName}`;
300329
- const filePath = join31(dir, uniqueFileName);
300358
+ const filePath = join32(dir, uniqueFileName);
300330
300359
  writeFileSync12(filePath, Buffer.from(data, "base64"));
300331
300360
  console.log(`[web] Saved attachment: ${filePath} (${mimeType})`);
300332
300361
  return {
@@ -300860,8 +300889,8 @@ class WebChannel {
300860
300889
  try {
300861
300890
  const files = readdirSync11(sessionPath).filter((f3) => f3.endsWith(".jsonl")).map((f3) => ({
300862
300891
  name: f3,
300863
- path: join30(sessionPath, f3),
300864
- mtime: statSync12(join30(sessionPath, f3)).mtime.getTime()
300892
+ path: join31(sessionPath, f3),
300893
+ mtime: statSync12(join31(sessionPath, f3)).mtime.getTime()
300865
300894
  })).sort((a, b) => b.mtime - a.mtime);
300866
300895
  if (files.length === 0)
300867
300896
  return;
@@ -300888,13 +300917,13 @@ class WebChannel {
300888
300917
  }
300889
300918
  async listAllSessions() {
300890
300919
  const sessions = [];
300891
- const sessionsDir = join30(getAppDir(), "sessions");
300920
+ const sessionsDir = join31(getAppDir(), "sessions");
300892
300921
  if (!existsSync27(sessionsDir)) {
300893
300922
  return sessions;
300894
300923
  }
300895
300924
  try {
300896
300925
  const dirs = readdirSync11(sessionsDir);
300897
- const webSessions = dirs.filter((dir) => dir.startsWith("web_")).map((dir) => ({ sessionId: dir, path: join30(sessionsDir, dir) })).filter(({ path: path14 }) => {
300926
+ const webSessions = dirs.filter((dir) => dir.startsWith("web_")).map((dir) => ({ sessionId: dir, path: join31(sessionsDir, dir) })).filter(({ path: path14 }) => {
300898
300927
  try {
300899
300928
  return statSync12(path14).isDirectory();
300900
300929
  } catch {
@@ -300965,7 +300994,7 @@ class WebChannel {
300965
300994
  }
300966
300995
 
300967
300996
  // src/daemon.ts
300968
- var PID_FILE = join31(getAppDir(), "daemon.pid");
300997
+ var PID_FILE = join32(getAppDir(), "daemon.pid");
300969
300998
  function isRunning() {
300970
300999
  if (!existsSync28(PID_FILE))
300971
301000
  return { running: false };
@@ -301223,23 +301252,23 @@ async function restartDaemon() {
301223
301252
  import { execSync as execSync3 } from "node:child_process";
301224
301253
  import { existsSync as existsSync29, mkdirSync as mkdirSync12, readFileSync as readFileSync25, unlinkSync as unlinkSync4, writeFileSync as writeFileSync13 } from "node:fs";
301225
301254
  import { homedir as homedir15, platform as platform3 } from "node:os";
301226
- import { dirname as dirname16, join as join32 } from "node:path";
301255
+ import { dirname as dirname16, join as join33 } from "node:path";
301227
301256
  var SERVICE_NAME = "clankie";
301228
301257
  var LAUNCHD_LABEL = "ai.clankie.daemon";
301229
301258
  function resolveProgramArguments() {
301230
301259
  const runtime = process.execPath || process.argv[0];
301231
301260
  const thisFile = import.meta.filename;
301232
- const cliEntry = thisFile.endsWith("service.ts") ? join32(dirname16(thisFile), "cli.ts") : join32(dirname16(thisFile), "cli.js");
301261
+ const cliEntry = thisFile.endsWith("service.ts") ? join33(dirname16(thisFile), "cli.ts") : join33(dirname16(thisFile), "cli.js");
301233
301262
  return [runtime, cliEntry, "start", "--foreground"];
301234
301263
  }
301235
301264
  function systemdUnitPath() {
301236
- return join32(homedir15(), ".config", "systemd", "user", `${SERVICE_NAME}.service`);
301265
+ return join33(homedir15(), ".config", "systemd", "user", `${SERVICE_NAME}.service`);
301237
301266
  }
301238
301267
  function buildSystemdUnit() {
301239
301268
  const args = resolveProgramArguments();
301240
301269
  const execStart = args.map(systemdEscapeArg).join(" ");
301241
- const workspace = join32(getAppDir(), "workspace");
301242
- const logDir = join32(getAppDir(), "logs");
301270
+ const workspace = join33(getAppDir(), "workspace");
301271
+ const logDir = join33(getAppDir(), "logs");
301243
301272
  return [
301244
301273
  "[Unit]",
301245
301274
  `Description=clankie — personal AI assistant daemon`,
@@ -301254,8 +301283,8 @@ function buildSystemdUnit() {
301254
301283
  "KillMode=process",
301255
301284
  `Environment=HOME=${homedir15()}`,
301256
301285
  `Environment=PATH=${process.env.PATH}`,
301257
- `StandardOutput=append:${join32(logDir, "daemon.log")}`,
301258
- `StandardError=append:${join32(logDir, "daemon.log")}`,
301286
+ `StandardOutput=append:${join33(logDir, "daemon.log")}`,
301287
+ `StandardError=append:${join33(logDir, "daemon.log")}`,
301259
301288
  "",
301260
301289
  "[Install]",
301261
301290
  "WantedBy=default.target",
@@ -301287,8 +301316,8 @@ async function installSystemd() {
301287
301316
  }
301288
301317
  }
301289
301318
  const unitPath = systemdUnitPath();
301290
- const logDir = join32(getAppDir(), "logs");
301291
- const workspace = join32(getAppDir(), "workspace");
301319
+ const logDir = join33(getAppDir(), "logs");
301320
+ const workspace = join33(getAppDir(), "workspace");
301292
301321
  mkdirSync12(dirname16(unitPath), { recursive: true });
301293
301322
  mkdirSync12(logDir, { recursive: true });
301294
301323
  mkdirSync12(workspace, { recursive: true });
@@ -301317,7 +301346,7 @@ async function installSystemd() {
301317
301346
  console.log(`
301318
301347
  ✓ Installed and started systemd service: ${SERVICE_NAME}.service`);
301319
301348
  console.log(` Logs: journalctl --user -u ${SERVICE_NAME} -f`);
301320
- console.log(` Or: ${join32(logDir, "daemon.log")}`);
301349
+ console.log(` Or: ${join33(logDir, "daemon.log")}`);
301321
301350
  }
301322
301351
  async function uninstallSystemd() {
301323
301352
  const unitPath = systemdUnitPath();
@@ -301332,7 +301361,7 @@ async function uninstallSystemd() {
301332
301361
  console.log(`✓ Uninstalled systemd service.`);
301333
301362
  }
301334
301363
  function logsSystemd() {
301335
- const logFile = join32(getAppDir(), "logs", "daemon.log");
301364
+ const logFile = join33(getAppDir(), "logs", "daemon.log");
301336
301365
  console.log(`Log file: ${logFile}
301337
301366
  `);
301338
301367
  const result = execSafe(`journalctl --user -u ${SERVICE_NAME} --no-pager -n 50`);
@@ -301354,15 +301383,15 @@ function statusSystemd() {
301354
301383
  console.log(result.stdout || result.stderr || "Service not found.");
301355
301384
  }
301356
301385
  function launchdPlistPath() {
301357
- return join32(homedir15(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
301386
+ return join33(homedir15(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
301358
301387
  }
301359
301388
  function plistEscape(value2) {
301360
301389
  return value2.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
301361
301390
  }
301362
301391
  function buildLaunchdPlist() {
301363
301392
  const args = resolveProgramArguments();
301364
- const logDir = join32(getAppDir(), "logs");
301365
- const workspace = join32(getAppDir(), "workspace");
301393
+ const logDir = join33(getAppDir(), "logs");
301394
+ const workspace = join33(getAppDir(), "workspace");
301366
301395
  const argsXml = args.map((a) => ` <string>${plistEscape(a)}</string>`).join(`
301367
301396
  `);
301368
301397
  const envVars = {};
@@ -301390,9 +301419,9 @@ ${argsXml}
301390
301419
  <key>WorkingDirectory</key>
301391
301420
  <string>${plistEscape(workspace)}</string>
301392
301421
  <key>StandardOutPath</key>
301393
- <string>${plistEscape(join32(logDir, "daemon.log"))}</string>
301422
+ <string>${plistEscape(join33(logDir, "daemon.log"))}</string>
301394
301423
  <key>StandardErrorPath</key>
301395
- <string>${plistEscape(join32(logDir, "daemon.log"))}</string>
301424
+ <string>${plistEscape(join33(logDir, "daemon.log"))}</string>
301396
301425
  <key>EnvironmentVariables</key>
301397
301426
  <dict>
301398
301427
  ${envXml}
@@ -301403,8 +301432,8 @@ ${envXml}
301403
301432
  }
301404
301433
  async function installLaunchd() {
301405
301434
  const plistPath = launchdPlistPath();
301406
- const logDir = join32(getAppDir(), "logs");
301407
- const workspace = join32(getAppDir(), "workspace");
301435
+ const logDir = join33(getAppDir(), "logs");
301436
+ const workspace = join33(getAppDir(), "workspace");
301408
301437
  mkdirSync12(dirname16(plistPath), { recursive: true });
301409
301438
  mkdirSync12(logDir, { recursive: true });
301410
301439
  mkdirSync12(workspace, { recursive: true });
@@ -301421,7 +301450,7 @@ async function installLaunchd() {
301421
301450
  }
301422
301451
  console.log(`
301423
301452
  ✓ Installed and started launchd agent: ${LAUNCHD_LABEL}`);
301424
- console.log(` Logs: tail -f ${join32(logDir, "daemon.log")}`);
301453
+ console.log(` Logs: tail -f ${join33(logDir, "daemon.log")}`);
301425
301454
  }
301426
301455
  async function uninstallLaunchd() {
301427
301456
  const plistPath = launchdPlistPath();
@@ -301435,7 +301464,7 @@ async function uninstallLaunchd() {
301435
301464
  console.log(`✓ Uninstalled launchd agent.`);
301436
301465
  }
301437
301466
  function logsLaunchd() {
301438
- const logFile = join32(getAppDir(), "logs", "daemon.log");
301467
+ const logFile = join33(getAppDir(), "logs", "daemon.log");
301439
301468
  console.log(`Log file: ${logFile}
301440
301469
  `);
301441
301470
  if (existsSync29(logFile)) {
@@ -301493,7 +301522,7 @@ function showServiceLogs() {
301493
301522
  } else if (isLinux) {
301494
301523
  logsSystemd();
301495
301524
  } else {
301496
- const logFile = join32(getAppDir(), "logs", "daemon.log");
301525
+ const logFile = join33(getAppDir(), "logs", "daemon.log");
301497
301526
  if (existsSync29(logFile)) {
301498
301527
  console.log(readFileSync25(logFile, "utf-8"));
301499
301528
  } else {
@@ -301583,7 +301612,7 @@ Credentials are stored at ~/.clankie/auth.json (separate from pi's auth).
301583
301612
  `);
301584
301613
  }
301585
301614
  function printVersion() {
301586
- const packagePath = join33(import.meta.dirname, "..", "package.json");
301615
+ const packagePath = join34(import.meta.dirname, "..", "package.json");
301587
301616
  try {
301588
301617
  const pkg3 = JSON.parse(readFileSync26(packagePath, "utf-8"));
301589
301618
  console.log(`clankie ${pkg3.version}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clankie",
3
- "version": "0.2.11",
3
+ "version": "0.2.12",
4
4
  "description": "A minimal personal AI assistant built on pi's SDK",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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 [&amp;&gt;span:last-child]:truncate [&amp;_svg]:size-4 [&amp;_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 [&amp;&gt;span:last-child]:truncate [&amp;_svg]:size-4 [&amp;_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 [&amp;&gt;svg]:size-4 flex shrink-0 items-center outline-hidden [&amp;&gt;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 [&amp;&gt;span:last-child]:truncate [&amp;_svg]:size-4 [&amp;_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 [&amp;&gt;span:last-child]:truncate [&amp;_svg]:size-4 [&amp;_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 [&amp;&gt;span:last-child]:truncate [&amp;_svg]:size-4 [&amp;_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 [&amp;&gt;svg]:size-3! inline-flex items-center whitespace-nowrap shrink-0 [&amp;&gt;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 [&amp;_svg:not([class*=&#x27;size-&#x27;])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none shrink-0 [&amp;_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:1772486127078,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>
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:1772486954401,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>