clankie 0.2.10 → 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 +153 -66
- package/package.json +1 -1
- package/web-ui-dist/_shell.html +2 -2
- package/web-ui-dist/assets/{extensions-I1gtx6lT.js → extensions-Ce8R4mF3.js} +1 -1
- package/web-ui-dist/assets/{field-DndVA7U4.js → field-omFPry7c.js} +1 -1
- package/web-ui-dist/assets/{index-QDWEB_Lf.js → index-CdxpJyJ4.js} +1 -1
- package/web-ui-dist/assets/{loader-circle-CO83p2nq.js → loader-circle-DJwIWnWV.js} +1 -1
- package/web-ui-dist/assets/{main-DUT2mAkp.js → main-UkFzTTck.js} +9 -9
- package/web-ui-dist/assets/{sessions._sessionId-D6j5CxBe.js → sessions._sessionId-DA8C51jO.js} +2 -2
- package/web-ui-dist/assets/{settings-EP4s8NoB.js → settings-Db2CTnq7.js} +1 -1
- package/web-ui-dist/assets/styles-DAZ1IN1z.css +1 -0
- package/web-ui-dist/assets/styles-CdE1WgCk.css +0 -1
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";
|
|
@@ -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
|
|
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({
|
|
@@ -297320,18 +297325,63 @@ async function createSession(options2 = {}) {
|
|
|
297320
297325
|
});
|
|
297321
297326
|
}
|
|
297322
297327
|
|
|
297328
|
+
// src/auth/providers.ts
|
|
297329
|
+
var CURATED_API_KEY_PROVIDERS = [
|
|
297330
|
+
{ id: "anthropic", name: "Anthropic" },
|
|
297331
|
+
{ id: "openai", name: "OpenAI" },
|
|
297332
|
+
{ id: "google", name: "Google (Gemini)" },
|
|
297333
|
+
{ id: "xai", name: "xAI (Grok)" },
|
|
297334
|
+
{ id: "groq", name: "Groq" },
|
|
297335
|
+
{ id: "openrouter", name: "OpenRouter" },
|
|
297336
|
+
{ id: "mistral", name: "Mistral" }
|
|
297337
|
+
];
|
|
297338
|
+
function formatProviderName(providerId) {
|
|
297339
|
+
const lower2 = providerId.toLowerCase();
|
|
297340
|
+
if (lower2 === "xai")
|
|
297341
|
+
return "xAI (Grok)";
|
|
297342
|
+
if (lower2 === "openai")
|
|
297343
|
+
return "OpenAI";
|
|
297344
|
+
if (lower2 === "anthropic")
|
|
297345
|
+
return "Anthropic";
|
|
297346
|
+
if (lower2 === "openrouter")
|
|
297347
|
+
return "OpenRouter";
|
|
297348
|
+
if (lower2 === "mistral")
|
|
297349
|
+
return "Mistral";
|
|
297350
|
+
if (lower2 === "groq")
|
|
297351
|
+
return "Groq";
|
|
297352
|
+
if (lower2 === "google")
|
|
297353
|
+
return "Google (Gemini)";
|
|
297354
|
+
return providerId.split(/[-_\s]+/).filter(Boolean).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)).join(" ");
|
|
297355
|
+
}
|
|
297356
|
+
function buildApiKeyProviders(oauthIds, dynamicProviderIds) {
|
|
297357
|
+
const byId = new Map;
|
|
297358
|
+
for (const provider of CURATED_API_KEY_PROVIDERS) {
|
|
297359
|
+
if (!oauthIds.has(provider.id)) {
|
|
297360
|
+
byId.set(provider.id, provider.name);
|
|
297361
|
+
}
|
|
297362
|
+
}
|
|
297363
|
+
for (const providerId of dynamicProviderIds) {
|
|
297364
|
+
if (!providerId || oauthIds.has(providerId))
|
|
297365
|
+
continue;
|
|
297366
|
+
if (!byId.has(providerId)) {
|
|
297367
|
+
byId.set(providerId, formatProviderName(providerId));
|
|
297368
|
+
}
|
|
297369
|
+
}
|
|
297370
|
+
return Array.from(byId.entries()).map(([id, name]) => ({ id, name })).sort((a, b) => a.name.localeCompare(b.name));
|
|
297371
|
+
}
|
|
297372
|
+
|
|
297323
297373
|
// src/daemon.ts
|
|
297324
297374
|
import { existsSync as existsSync28, readFileSync as readFileSync24, unlinkSync as unlinkSync3, watch as watch3, writeFileSync as writeFileSync12 } from "node:fs";
|
|
297325
|
-
import { join as
|
|
297375
|
+
import { join as join32 } from "node:path";
|
|
297326
297376
|
|
|
297327
297377
|
// src/channels/slack.ts
|
|
297328
297378
|
var import_socket_mode = __toESM(require_src6(), 1);
|
|
297329
297379
|
var import_web_api = __toESM(require_dist18(), 1);
|
|
297330
297380
|
import { existsSync as existsSync25, mkdirSync as mkdirSync10, readFileSync as readFileSync22, writeFileSync as writeFileSync11 } from "node:fs";
|
|
297331
297381
|
import { homedir as homedir14 } from "node:os";
|
|
297332
|
-
import { join as
|
|
297382
|
+
import { join as join29 } from "node:path";
|
|
297333
297383
|
var SLACK_MAX_LENGTH = 4000;
|
|
297334
|
-
var ACTIVE_THREADS_FILE =
|
|
297384
|
+
var ACTIVE_THREADS_FILE = join29(homedir14(), ".clankie", "slack-active-threads.json");
|
|
297335
297385
|
var THREAD_TTL_DAYS = 7;
|
|
297336
297386
|
|
|
297337
297387
|
class SlackChannel {
|
|
@@ -297419,7 +297469,7 @@ class SlackChannel {
|
|
|
297419
297469
|
}
|
|
297420
297470
|
saveActiveThreads() {
|
|
297421
297471
|
try {
|
|
297422
|
-
const dir =
|
|
297472
|
+
const dir = join29(homedir14(), ".clankie");
|
|
297423
297473
|
if (!existsSync25(dir)) {
|
|
297424
297474
|
mkdirSync10(dir, { recursive: true, mode: 448 });
|
|
297425
297475
|
}
|
|
@@ -297562,7 +297612,7 @@ class SlackChannel {
|
|
|
297562
297612
|
// src/channels/web.ts
|
|
297563
297613
|
import * as crypto5 from "node:crypto";
|
|
297564
297614
|
import { existsSync as existsSync27, readdirSync as readdirSync11, readFileSync as readFileSync23, statSync as statSync12 } from "node:fs";
|
|
297565
|
-
import { join as
|
|
297615
|
+
import { join as join31, resolve as resolve11 } from "node:path";
|
|
297566
297616
|
|
|
297567
297617
|
// node_modules/@hono/node-server/dist/index.mjs
|
|
297568
297618
|
import { createServer as createServerHTTP } from "http";
|
|
@@ -299823,7 +299873,7 @@ var Hono2 = class extends Hono {
|
|
|
299823
299873
|
|
|
299824
299874
|
// src/sessions.ts
|
|
299825
299875
|
import { existsSync as existsSync26, mkdirSync as mkdirSync11 } from "node:fs";
|
|
299826
|
-
import { join as
|
|
299876
|
+
import { join as join30 } from "node:path";
|
|
299827
299877
|
var sessionCache = new Map;
|
|
299828
299878
|
var activeSessionNames = new Map;
|
|
299829
299879
|
var chatLocks = new Map;
|
|
@@ -299842,7 +299892,9 @@ async function getOrCreateSession(chatKey, config) {
|
|
|
299842
299892
|
const extensionFactories = [];
|
|
299843
299893
|
const restrictToWorkspace = config.agent?.restrictToWorkspace ?? true;
|
|
299844
299894
|
if (restrictToWorkspace) {
|
|
299845
|
-
const
|
|
299895
|
+
const configuredAllowedPaths = config.agent?.allowedPaths ?? [];
|
|
299896
|
+
const attachmentRoot = join30(getAppDir(), "attachments");
|
|
299897
|
+
const allowedPaths = Array.from(new Set([...configuredAllowedPaths, attachmentRoot]));
|
|
299846
299898
|
extensionFactories.push(createWorkspaceJailExtension(cwd, allowedPaths));
|
|
299847
299899
|
}
|
|
299848
299900
|
const loader = new DefaultResourceLoader({
|
|
@@ -299851,7 +299903,7 @@ async function getOrCreateSession(chatKey, config) {
|
|
|
299851
299903
|
extensionFactories
|
|
299852
299904
|
});
|
|
299853
299905
|
await loader.reload();
|
|
299854
|
-
const sessionDir =
|
|
299906
|
+
const sessionDir = join30(getAppDir(), "sessions", chatKey);
|
|
299855
299907
|
if (!existsSync26(sessionDir)) {
|
|
299856
299908
|
mkdirSync11(sessionDir, { recursive: true });
|
|
299857
299909
|
}
|
|
@@ -299923,7 +299975,7 @@ async function getOrCreateSession(chatKey, config) {
|
|
|
299923
299975
|
return session;
|
|
299924
299976
|
}
|
|
299925
299977
|
function listSessionNames(chatIdentifier) {
|
|
299926
|
-
const sessionsDir =
|
|
299978
|
+
const sessionsDir = join30(getAppDir(), "sessions");
|
|
299927
299979
|
if (!existsSync26(sessionsDir)) {
|
|
299928
299980
|
return [];
|
|
299929
299981
|
}
|
|
@@ -299933,7 +299985,7 @@ function listSessionNames(chatIdentifier) {
|
|
|
299933
299985
|
const sessionNames = new Set;
|
|
299934
299986
|
for (const entry of entries) {
|
|
299935
299987
|
if (entry.startsWith(`${chatIdentifier}_`)) {
|
|
299936
|
-
const entryPath =
|
|
299988
|
+
const entryPath = join30(sessionsDir, entry);
|
|
299937
299989
|
if (statSync12(entryPath).isDirectory()) {
|
|
299938
299990
|
const sessionName = entry.substring(chatIdentifier.length + 1);
|
|
299939
299991
|
if (sessionName) {
|
|
@@ -299967,13 +300019,13 @@ async function saveNonImageAttachments(attachments, chatKey) {
|
|
|
299967
300019
|
if (nonImages.length === 0)
|
|
299968
300020
|
return [];
|
|
299969
300021
|
const { mkdirSync: mkdirSync12, writeFileSync: writeFileSync12 } = await import("node:fs");
|
|
299970
|
-
const { join:
|
|
299971
|
-
const dir =
|
|
300022
|
+
const { join: join31 } = await import("node:path");
|
|
300023
|
+
const dir = join31(getAppDir(), "attachments", chatKey);
|
|
299972
300024
|
mkdirSync12(dir, { recursive: true });
|
|
299973
300025
|
const results = [];
|
|
299974
300026
|
for (const att of nonImages) {
|
|
299975
300027
|
const name = att.fileName || `file_${Date.now()}`;
|
|
299976
|
-
const filePath =
|
|
300028
|
+
const filePath = join31(dir, name);
|
|
299977
300029
|
writeFileSync12(filePath, Buffer.from(att.data, "base64"));
|
|
299978
300030
|
results.push({ fileName: name, path: filePath });
|
|
299979
300031
|
console.log(`[session] Saved attachment: ${filePath} (${att.mimeType})`);
|
|
@@ -300074,7 +300126,7 @@ class WebChannel {
|
|
|
300074
300126
|
if (pathname === "" || pathname === "/") {
|
|
300075
300127
|
pathname = "_shell.html";
|
|
300076
300128
|
}
|
|
300077
|
-
const _filePath =
|
|
300129
|
+
const _filePath = join31(this.options.staticDir, pathname);
|
|
300078
300130
|
const resolvedPath = resolve11(this.options.staticDir, pathname);
|
|
300079
300131
|
if (!resolvedPath.startsWith(resolve11(this.options.staticDir))) {
|
|
300080
300132
|
return c.text("Forbidden", 403);
|
|
@@ -300107,7 +300159,7 @@ class WebChannel {
|
|
|
300107
300159
|
}
|
|
300108
300160
|
});
|
|
300109
300161
|
}
|
|
300110
|
-
const shellPath =
|
|
300162
|
+
const shellPath = join31(this.options.staticDir, "_shell.html");
|
|
300111
300163
|
if (existsSync27(shellPath)) {
|
|
300112
300164
|
const content = readFileSync23(shellPath);
|
|
300113
300165
|
return new Response(content, {
|
|
@@ -300245,12 +300297,34 @@ class WebChannel {
|
|
|
300245
300297
|
this.sendError(ws2, inbound.sessionId, command.type, err instanceof Error ? err.message : String(err), commandId);
|
|
300246
300298
|
}
|
|
300247
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
|
+
}
|
|
300248
300321
|
async executeCommand(sessionId, session, command) {
|
|
300249
300322
|
const id = command.id;
|
|
300250
300323
|
switch (command.type) {
|
|
300251
300324
|
case "prompt": {
|
|
300252
300325
|
console.log(`[web] Executing prompt - session.sessionId: ${session.sessionId}, sessionFile: ${session.sessionFile}`);
|
|
300253
|
-
|
|
300326
|
+
const promptWithAttachmentPaths = this.injectAttachmentPaths(sessionId, command.message);
|
|
300327
|
+
session.prompt(promptWithAttachmentPaths, {
|
|
300254
300328
|
images: command.images,
|
|
300255
300329
|
streamingBehavior: command.streamingBehavior,
|
|
300256
300330
|
source: "rpc"
|
|
@@ -300275,13 +300349,13 @@ class WebChannel {
|
|
|
300275
300349
|
case "upload_attachment": {
|
|
300276
300350
|
const { fileName, data, mimeType } = command;
|
|
300277
300351
|
const { mkdirSync: mkdirSync12, writeFileSync: writeFileSync12 } = await import("node:fs");
|
|
300278
|
-
const { join:
|
|
300279
|
-
const dir =
|
|
300352
|
+
const { join: join32 } = await import("node:path");
|
|
300353
|
+
const dir = join32(getAppDir(), "attachments", sessionId);
|
|
300280
300354
|
mkdirSync12(dir, { recursive: true });
|
|
300281
300355
|
const timestamp = Date.now();
|
|
300282
300356
|
const sanitizedName = fileName.replace(/[^a-zA-Z0-9.-]/g, "_");
|
|
300283
300357
|
const uniqueFileName = `${timestamp}_${sanitizedName}`;
|
|
300284
|
-
const filePath =
|
|
300358
|
+
const filePath = join32(dir, uniqueFileName);
|
|
300285
300359
|
writeFileSync12(filePath, Buffer.from(data, "base64"));
|
|
300286
300360
|
console.log(`[web] Saved attachment: ${filePath} (${mimeType})`);
|
|
300287
300361
|
return {
|
|
@@ -300562,15 +300636,7 @@ class WebChannel {
|
|
|
300562
300636
|
case "get_auth_providers": {
|
|
300563
300637
|
const oauthProviders = authStorage.getOAuthProviders();
|
|
300564
300638
|
const oauthIds = new Set(oauthProviders.map((p) => p.id));
|
|
300565
|
-
const apiKeyProviders =
|
|
300566
|
-
{ id: "anthropic", name: "Anthropic" },
|
|
300567
|
-
{ id: "openai", name: "OpenAI" },
|
|
300568
|
-
{ id: "google", name: "Google (Gemini)" },
|
|
300569
|
-
{ id: "xai", name: "xAI (Grok)" },
|
|
300570
|
-
{ id: "groq", name: "Groq" },
|
|
300571
|
-
{ id: "openrouter", name: "OpenRouter" },
|
|
300572
|
-
{ id: "mistral", name: "Mistral" }
|
|
300573
|
-
].filter((p) => !oauthIds.has(p.id));
|
|
300639
|
+
const apiKeyProviders = await this.getApiKeyProviders(oauthIds);
|
|
300574
300640
|
const providers = [
|
|
300575
300641
|
...oauthProviders.map((p) => ({
|
|
300576
300642
|
id: p.id,
|
|
@@ -300749,6 +300815,21 @@ class WebChannel {
|
|
|
300749
300815
|
});
|
|
300750
300816
|
}
|
|
300751
300817
|
}
|
|
300818
|
+
async getApiKeyProviders(oauthIds) {
|
|
300819
|
+
const dynamicProviderIds = new Set;
|
|
300820
|
+
try {
|
|
300821
|
+
const config = loadConfig();
|
|
300822
|
+
const discoverySession = await getOrCreateSession("web_auth_providers", config);
|
|
300823
|
+
const allModels = discoverySession.modelRegistry.getAll();
|
|
300824
|
+
for (const model of allModels) {
|
|
300825
|
+
if (model.provider)
|
|
300826
|
+
dynamicProviderIds.add(model.provider);
|
|
300827
|
+
}
|
|
300828
|
+
} catch (err) {
|
|
300829
|
+
console.warn(`[web] Failed to discover dynamic API key providers: ${err instanceof Error ? err.message : String(err)}`);
|
|
300830
|
+
}
|
|
300831
|
+
return buildApiKeyProviders(oauthIds, dynamicProviderIds);
|
|
300832
|
+
}
|
|
300752
300833
|
sendAuthEvent(ws2, _loginFlowId, event) {
|
|
300753
300834
|
const message = {
|
|
300754
300835
|
sessionId: "_auth",
|
|
@@ -300808,8 +300889,8 @@ class WebChannel {
|
|
|
300808
300889
|
try {
|
|
300809
300890
|
const files = readdirSync11(sessionPath).filter((f3) => f3.endsWith(".jsonl")).map((f3) => ({
|
|
300810
300891
|
name: f3,
|
|
300811
|
-
path:
|
|
300812
|
-
mtime: statSync12(
|
|
300892
|
+
path: join31(sessionPath, f3),
|
|
300893
|
+
mtime: statSync12(join31(sessionPath, f3)).mtime.getTime()
|
|
300813
300894
|
})).sort((a, b) => b.mtime - a.mtime);
|
|
300814
300895
|
if (files.length === 0)
|
|
300815
300896
|
return;
|
|
@@ -300836,13 +300917,13 @@ class WebChannel {
|
|
|
300836
300917
|
}
|
|
300837
300918
|
async listAllSessions() {
|
|
300838
300919
|
const sessions = [];
|
|
300839
|
-
const sessionsDir =
|
|
300920
|
+
const sessionsDir = join31(getAppDir(), "sessions");
|
|
300840
300921
|
if (!existsSync27(sessionsDir)) {
|
|
300841
300922
|
return sessions;
|
|
300842
300923
|
}
|
|
300843
300924
|
try {
|
|
300844
300925
|
const dirs = readdirSync11(sessionsDir);
|
|
300845
|
-
const webSessions = dirs.filter((dir) => dir.startsWith("web_")).map((dir) => ({ sessionId: dir, path:
|
|
300926
|
+
const webSessions = dirs.filter((dir) => dir.startsWith("web_")).map((dir) => ({ sessionId: dir, path: join31(sessionsDir, dir) })).filter(({ path: path14 }) => {
|
|
300846
300927
|
try {
|
|
300847
300928
|
return statSync12(path14).isDirectory();
|
|
300848
300929
|
} catch {
|
|
@@ -300913,7 +300994,7 @@ class WebChannel {
|
|
|
300913
300994
|
}
|
|
300914
300995
|
|
|
300915
300996
|
// src/daemon.ts
|
|
300916
|
-
var PID_FILE =
|
|
300997
|
+
var PID_FILE = join32(getAppDir(), "daemon.pid");
|
|
300917
300998
|
function isRunning() {
|
|
300918
300999
|
if (!existsSync28(PID_FILE))
|
|
300919
301000
|
return { running: false };
|
|
@@ -301171,23 +301252,23 @@ async function restartDaemon() {
|
|
|
301171
301252
|
import { execSync as execSync3 } from "node:child_process";
|
|
301172
301253
|
import { existsSync as existsSync29, mkdirSync as mkdirSync12, readFileSync as readFileSync25, unlinkSync as unlinkSync4, writeFileSync as writeFileSync13 } from "node:fs";
|
|
301173
301254
|
import { homedir as homedir15, platform as platform3 } from "node:os";
|
|
301174
|
-
import { dirname as dirname16, join as
|
|
301255
|
+
import { dirname as dirname16, join as join33 } from "node:path";
|
|
301175
301256
|
var SERVICE_NAME = "clankie";
|
|
301176
301257
|
var LAUNCHD_LABEL = "ai.clankie.daemon";
|
|
301177
301258
|
function resolveProgramArguments() {
|
|
301178
301259
|
const runtime = process.execPath || process.argv[0];
|
|
301179
301260
|
const thisFile = import.meta.filename;
|
|
301180
|
-
const cliEntry = thisFile.endsWith("service.ts") ?
|
|
301261
|
+
const cliEntry = thisFile.endsWith("service.ts") ? join33(dirname16(thisFile), "cli.ts") : join33(dirname16(thisFile), "cli.js");
|
|
301181
301262
|
return [runtime, cliEntry, "start", "--foreground"];
|
|
301182
301263
|
}
|
|
301183
301264
|
function systemdUnitPath() {
|
|
301184
|
-
return
|
|
301265
|
+
return join33(homedir15(), ".config", "systemd", "user", `${SERVICE_NAME}.service`);
|
|
301185
301266
|
}
|
|
301186
301267
|
function buildSystemdUnit() {
|
|
301187
301268
|
const args = resolveProgramArguments();
|
|
301188
301269
|
const execStart = args.map(systemdEscapeArg).join(" ");
|
|
301189
|
-
const workspace =
|
|
301190
|
-
const logDir =
|
|
301270
|
+
const workspace = join33(getAppDir(), "workspace");
|
|
301271
|
+
const logDir = join33(getAppDir(), "logs");
|
|
301191
301272
|
return [
|
|
301192
301273
|
"[Unit]",
|
|
301193
301274
|
`Description=clankie — personal AI assistant daemon`,
|
|
@@ -301202,8 +301283,8 @@ function buildSystemdUnit() {
|
|
|
301202
301283
|
"KillMode=process",
|
|
301203
301284
|
`Environment=HOME=${homedir15()}`,
|
|
301204
301285
|
`Environment=PATH=${process.env.PATH}`,
|
|
301205
|
-
`StandardOutput=append:${
|
|
301206
|
-
`StandardError=append:${
|
|
301286
|
+
`StandardOutput=append:${join33(logDir, "daemon.log")}`,
|
|
301287
|
+
`StandardError=append:${join33(logDir, "daemon.log")}`,
|
|
301207
301288
|
"",
|
|
301208
301289
|
"[Install]",
|
|
301209
301290
|
"WantedBy=default.target",
|
|
@@ -301235,8 +301316,8 @@ async function installSystemd() {
|
|
|
301235
301316
|
}
|
|
301236
301317
|
}
|
|
301237
301318
|
const unitPath = systemdUnitPath();
|
|
301238
|
-
const logDir =
|
|
301239
|
-
const workspace =
|
|
301319
|
+
const logDir = join33(getAppDir(), "logs");
|
|
301320
|
+
const workspace = join33(getAppDir(), "workspace");
|
|
301240
301321
|
mkdirSync12(dirname16(unitPath), { recursive: true });
|
|
301241
301322
|
mkdirSync12(logDir, { recursive: true });
|
|
301242
301323
|
mkdirSync12(workspace, { recursive: true });
|
|
@@ -301265,7 +301346,7 @@ async function installSystemd() {
|
|
|
301265
301346
|
console.log(`
|
|
301266
301347
|
✓ Installed and started systemd service: ${SERVICE_NAME}.service`);
|
|
301267
301348
|
console.log(` Logs: journalctl --user -u ${SERVICE_NAME} -f`);
|
|
301268
|
-
console.log(` Or: ${
|
|
301349
|
+
console.log(` Or: ${join33(logDir, "daemon.log")}`);
|
|
301269
301350
|
}
|
|
301270
301351
|
async function uninstallSystemd() {
|
|
301271
301352
|
const unitPath = systemdUnitPath();
|
|
@@ -301280,7 +301361,7 @@ async function uninstallSystemd() {
|
|
|
301280
301361
|
console.log(`✓ Uninstalled systemd service.`);
|
|
301281
301362
|
}
|
|
301282
301363
|
function logsSystemd() {
|
|
301283
|
-
const logFile =
|
|
301364
|
+
const logFile = join33(getAppDir(), "logs", "daemon.log");
|
|
301284
301365
|
console.log(`Log file: ${logFile}
|
|
301285
301366
|
`);
|
|
301286
301367
|
const result = execSafe(`journalctl --user -u ${SERVICE_NAME} --no-pager -n 50`);
|
|
@@ -301302,15 +301383,15 @@ function statusSystemd() {
|
|
|
301302
301383
|
console.log(result.stdout || result.stderr || "Service not found.");
|
|
301303
301384
|
}
|
|
301304
301385
|
function launchdPlistPath() {
|
|
301305
|
-
return
|
|
301386
|
+
return join33(homedir15(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
|
|
301306
301387
|
}
|
|
301307
301388
|
function plistEscape(value2) {
|
|
301308
301389
|
return value2.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
301309
301390
|
}
|
|
301310
301391
|
function buildLaunchdPlist() {
|
|
301311
301392
|
const args = resolveProgramArguments();
|
|
301312
|
-
const logDir =
|
|
301313
|
-
const workspace =
|
|
301393
|
+
const logDir = join33(getAppDir(), "logs");
|
|
301394
|
+
const workspace = join33(getAppDir(), "workspace");
|
|
301314
301395
|
const argsXml = args.map((a) => ` <string>${plistEscape(a)}</string>`).join(`
|
|
301315
301396
|
`);
|
|
301316
301397
|
const envVars = {};
|
|
@@ -301338,9 +301419,9 @@ ${argsXml}
|
|
|
301338
301419
|
<key>WorkingDirectory</key>
|
|
301339
301420
|
<string>${plistEscape(workspace)}</string>
|
|
301340
301421
|
<key>StandardOutPath</key>
|
|
301341
|
-
<string>${plistEscape(
|
|
301422
|
+
<string>${plistEscape(join33(logDir, "daemon.log"))}</string>
|
|
301342
301423
|
<key>StandardErrorPath</key>
|
|
301343
|
-
<string>${plistEscape(
|
|
301424
|
+
<string>${plistEscape(join33(logDir, "daemon.log"))}</string>
|
|
301344
301425
|
<key>EnvironmentVariables</key>
|
|
301345
301426
|
<dict>
|
|
301346
301427
|
${envXml}
|
|
@@ -301351,8 +301432,8 @@ ${envXml}
|
|
|
301351
301432
|
}
|
|
301352
301433
|
async function installLaunchd() {
|
|
301353
301434
|
const plistPath = launchdPlistPath();
|
|
301354
|
-
const logDir =
|
|
301355
|
-
const workspace =
|
|
301435
|
+
const logDir = join33(getAppDir(), "logs");
|
|
301436
|
+
const workspace = join33(getAppDir(), "workspace");
|
|
301356
301437
|
mkdirSync12(dirname16(plistPath), { recursive: true });
|
|
301357
301438
|
mkdirSync12(logDir, { recursive: true });
|
|
301358
301439
|
mkdirSync12(workspace, { recursive: true });
|
|
@@ -301369,7 +301450,7 @@ async function installLaunchd() {
|
|
|
301369
301450
|
}
|
|
301370
301451
|
console.log(`
|
|
301371
301452
|
✓ Installed and started launchd agent: ${LAUNCHD_LABEL}`);
|
|
301372
|
-
console.log(` Logs: tail -f ${
|
|
301453
|
+
console.log(` Logs: tail -f ${join33(logDir, "daemon.log")}`);
|
|
301373
301454
|
}
|
|
301374
301455
|
async function uninstallLaunchd() {
|
|
301375
301456
|
const plistPath = launchdPlistPath();
|
|
@@ -301383,7 +301464,7 @@ async function uninstallLaunchd() {
|
|
|
301383
301464
|
console.log(`✓ Uninstalled launchd agent.`);
|
|
301384
301465
|
}
|
|
301385
301466
|
function logsLaunchd() {
|
|
301386
|
-
const logFile =
|
|
301467
|
+
const logFile = join33(getAppDir(), "logs", "daemon.log");
|
|
301387
301468
|
console.log(`Log file: ${logFile}
|
|
301388
301469
|
`);
|
|
301389
301470
|
if (existsSync29(logFile)) {
|
|
@@ -301441,7 +301522,7 @@ function showServiceLogs() {
|
|
|
301441
301522
|
} else if (isLinux) {
|
|
301442
301523
|
logsSystemd();
|
|
301443
301524
|
} else {
|
|
301444
|
-
const logFile =
|
|
301525
|
+
const logFile = join33(getAppDir(), "logs", "daemon.log");
|
|
301445
301526
|
if (existsSync29(logFile)) {
|
|
301446
301527
|
console.log(readFileSync25(logFile, "utf-8"));
|
|
301447
301528
|
} else {
|
|
@@ -301531,7 +301612,7 @@ Credentials are stored at ~/.clankie/auth.json (separate from pi's auth).
|
|
|
301531
301612
|
`);
|
|
301532
301613
|
}
|
|
301533
301614
|
function printVersion() {
|
|
301534
|
-
const packagePath =
|
|
301615
|
+
const packagePath = join34(import.meta.dirname, "..", "package.json");
|
|
301535
301616
|
try {
|
|
301536
301617
|
const pkg3 = JSON.parse(readFileSync26(packagePath, "utf-8"));
|
|
301537
301618
|
console.log(`clankie ${pkg3.version}`);
|
|
@@ -301567,20 +301648,26 @@ async function cmdChat(args) {
|
|
|
301567
301648
|
});
|
|
301568
301649
|
await mode.run();
|
|
301569
301650
|
}
|
|
301651
|
+
async function getApiKeyProvidersForLogin(oauthIds) {
|
|
301652
|
+
const dynamicProviderIds = new Set;
|
|
301653
|
+
try {
|
|
301654
|
+
const { session } = await createSession({ ephemeral: true });
|
|
301655
|
+
const allModels = session.modelRegistry.getAll();
|
|
301656
|
+
for (const model of allModels) {
|
|
301657
|
+
if (model.provider)
|
|
301658
|
+
dynamicProviderIds.add(model.provider);
|
|
301659
|
+
}
|
|
301660
|
+
} catch (err) {
|
|
301661
|
+
console.warn(`Warning: failed to discover dynamic providers: ${err instanceof Error ? err.message : String(err)}`);
|
|
301662
|
+
}
|
|
301663
|
+
return buildApiKeyProviders(oauthIds, dynamicProviderIds);
|
|
301664
|
+
}
|
|
301570
301665
|
async function cmdLogin(_args) {
|
|
301571
301666
|
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
301572
301667
|
const authStorage = AuthStorage.create(getAuthPath2());
|
|
301573
301668
|
const oauthProviders = authStorage.getOAuthProviders();
|
|
301574
301669
|
const oauthIds = new Set(oauthProviders.map((p) => p.id));
|
|
301575
|
-
const apiKeyProviders =
|
|
301576
|
-
{ id: "anthropic", name: "Anthropic" },
|
|
301577
|
-
{ id: "openai", name: "OpenAI" },
|
|
301578
|
-
{ id: "google", name: "Google (Gemini)" },
|
|
301579
|
-
{ id: "xai", name: "xAI (Grok)" },
|
|
301580
|
-
{ id: "groq", name: "Groq" },
|
|
301581
|
-
{ id: "openrouter", name: "OpenRouter" },
|
|
301582
|
-
{ id: "mistral", name: "Mistral" }
|
|
301583
|
-
].filter((p) => !oauthIds.has(p.id));
|
|
301670
|
+
const apiKeyProviders = await getApiKeyProvidersForLogin(oauthIds);
|
|
301584
301671
|
const entries = [];
|
|
301585
301672
|
for (const p of oauthProviders) {
|
|
301586
301673
|
entries.push({ id: p.id, name: p.name, type: "oauth", hasAuth: authStorage.hasAuth(p.id) });
|
package/package.json
CHANGED
package/web-ui-dist/_shell.html
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
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-DUT2mAkp.js"/><link rel="modulepreload" href="/assets/index-QDWEB_Lf.js"/><link rel="icon" type="image/svg+xml" href="/favicon.svg"/><link rel="stylesheet" href="/assets/styles-CdE1WgCk.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-
|
|
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: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>
|