clankie 0.10.1 → 0.11.0
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 +393 -3
- package/package.json +1 -1
- package/web-ui-dist/_shell.html +2 -2
- package/web-ui-dist/assets/auth-DxmWVlOj.js +1 -0
- package/web-ui-dist/assets/{badge-3e57zy_2.js → badge-B27MkVic.js} +1 -1
- package/web-ui-dist/assets/check-D2XSq1UZ.js +1 -0
- package/web-ui-dist/assets/circle-x-Bek90N0j.js +1 -0
- package/web-ui-dist/assets/{connection-D4rgB5k2.js → connection-Bh7ctiLV.js} +1 -1
- package/web-ui-dist/assets/{extensions-BxWXmCbJ.js → extensions-S_3zi2ye.js} +1 -1
- package/web-ui-dist/assets/{extensions-Cf8QLmLt.js → extensions-dzmh5XjT.js} +1 -1
- package/web-ui-dist/assets/{field-DfBj0pPw.js → field-Dy_c96vc.js} +1 -1
- package/web-ui-dist/assets/index-DTLVg2XM.js +1 -0
- package/web-ui-dist/assets/{index-Ff5WtXhh.js → index-DuhjveLi.js} +1 -1
- package/web-ui-dist/assets/{json-render-renderer-BrlU1n47.js → json-render-renderer-BEA6E5Sp.js} +1 -1
- package/web-ui-dist/assets/main-CKIteeQH.js +35 -0
- package/web-ui-dist/assets/notifications-CJ14tHG8.js +1 -0
- package/web-ui-dist/assets/{scoped-models-DDH_ssLY.js → scoped-models-D-DbdhBI.js} +1 -1
- package/web-ui-dist/assets/{sessions._sessionId-CWtQlITL.js → sessions._sessionId-DpLOfRZu.js} +1 -1
- package/web-ui-dist/assets/{skills-Clk3tV2m.js → skills-suEsMWsc.js} +1 -1
- package/web-ui-dist/assets/styles-CuRG0ztT.css +1 -0
- package/web-ui-dist/assets/{theme-e79Jvep_.js → theme-DfHdxP7k.js} +2 -2
- package/web-ui-dist/assets/auth-B20mIC_p.js +0 -1
- package/web-ui-dist/assets/check-CePvKusa.js +0 -1
- package/web-ui-dist/assets/circle-x-B1Pwi07a.js +0 -1
- package/web-ui-dist/assets/index-TBNB5eLy.js +0 -1
- package/web-ui-dist/assets/main-CP6prmzV.js +0 -35
- package/web-ui-dist/assets/styles-KEhqa3CU.css +0 -1
package/dist/cli.js
CHANGED
|
@@ -273899,6 +273899,197 @@ var _ = 30 * 1e3, y = [], R = class {
|
|
|
273899
273899
|
}
|
|
273900
273900
|
};
|
|
273901
273901
|
//#endregion
|
|
273902
|
+
//#region src/notifications.ts
|
|
273903
|
+
/**
|
|
273904
|
+
* Notification storage and management system.
|
|
273905
|
+
*
|
|
273906
|
+
* Notifications are persisted to disk (~/.clankie/notifications.json)
|
|
273907
|
+
* and broadcast to connected WebSocket clients in real-time.
|
|
273908
|
+
*
|
|
273909
|
+
* Features:
|
|
273910
|
+
* - Auto-pruning of old notifications (500 entry limit)
|
|
273911
|
+
* - Deduplication (prevents spam from frequent failures)
|
|
273912
|
+
* - Atomic file writes (write to temp, then rename)
|
|
273913
|
+
* - Real-time broadcast callback for WebSocket delivery
|
|
273914
|
+
*/
|
|
273915
|
+
const MAX_NOTIFICATIONS = 500;
|
|
273916
|
+
const PRUNE_TARGET = 400;
|
|
273917
|
+
const NOTIFICATIONS_FILE = "notifications.json";
|
|
273918
|
+
let notifications = [];
|
|
273919
|
+
let writeQueue = Promise.resolve();
|
|
273920
|
+
let broadcastCallback = null;
|
|
273921
|
+
function getNotificationsPath() {
|
|
273922
|
+
return join(getAppDir(), NOTIFICATIONS_FILE);
|
|
273923
|
+
}
|
|
273924
|
+
function loadNotifications() {
|
|
273925
|
+
const filePath = getNotificationsPath();
|
|
273926
|
+
if (!existsSync(filePath)) return [];
|
|
273927
|
+
try {
|
|
273928
|
+
const content = readFileSync(filePath, "utf-8");
|
|
273929
|
+
const parsed = JSON.parse(content);
|
|
273930
|
+
if (Array.isArray(parsed)) return parsed;
|
|
273931
|
+
} catch (err) {
|
|
273932
|
+
console.error("[notifications] Failed to load notifications:", err);
|
|
273933
|
+
}
|
|
273934
|
+
return [];
|
|
273935
|
+
}
|
|
273936
|
+
function atomicWrite(filePath, data) {
|
|
273937
|
+
const tempPath = `${filePath}.tmp`;
|
|
273938
|
+
writeFileSync(tempPath, data, "utf-8");
|
|
273939
|
+
renameSync(tempPath, filePath);
|
|
273940
|
+
}
|
|
273941
|
+
function persistNotifications() {
|
|
273942
|
+
const filePath = getNotificationsPath();
|
|
273943
|
+
const dir = filePath.substring(0, filePath.lastIndexOf("/"));
|
|
273944
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
273945
|
+
atomicWrite(filePath, JSON.stringify(notifications, null, 2));
|
|
273946
|
+
}
|
|
273947
|
+
function queuePersist() {
|
|
273948
|
+
writeQueue = writeQueue.then(() => {
|
|
273949
|
+
try {
|
|
273950
|
+
persistNotifications();
|
|
273951
|
+
} catch (err) {
|
|
273952
|
+
console.error("[notifications] Failed to persist:", err);
|
|
273953
|
+
}
|
|
273954
|
+
});
|
|
273955
|
+
}
|
|
273956
|
+
function pruneNotifications() {
|
|
273957
|
+
if (notifications.length <= MAX_NOTIFICATIONS) return;
|
|
273958
|
+
const sorted = [...notifications].sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
|
273959
|
+
let toKeep = sorted.filter((n) => !n.dismissed);
|
|
273960
|
+
if (toKeep.length > PRUNE_TARGET) {
|
|
273961
|
+
const unread = toKeep.filter((n) => !n.read);
|
|
273962
|
+
const readToKeep = toKeep.filter((n) => n.read).slice(0, Math.max(0, PRUNE_TARGET - unread.length));
|
|
273963
|
+
toKeep = [...unread, ...readToKeep];
|
|
273964
|
+
}
|
|
273965
|
+
if (toKeep.length > PRUNE_TARGET) toKeep = toKeep.slice(0, PRUNE_TARGET);
|
|
273966
|
+
notifications = toKeep.sort((a, b) => {
|
|
273967
|
+
const timeDiff = new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime();
|
|
273968
|
+
if (timeDiff !== 0) return timeDiff;
|
|
273969
|
+
return a.id.localeCompare(b.id);
|
|
273970
|
+
});
|
|
273971
|
+
console.log(`[notifications] Pruned from ${sorted.length} to ${notifications.length} entries`);
|
|
273972
|
+
}
|
|
273973
|
+
function findDuplicate(input) {
|
|
273974
|
+
if (!input.dedupKey) return void 0;
|
|
273975
|
+
return notifications.find((n) => {
|
|
273976
|
+
if (n.read || n.dismissed) return false;
|
|
273977
|
+
if (n.source !== input.source) return false;
|
|
273978
|
+
return n.metadata?.dedupKey === input.dedupKey;
|
|
273979
|
+
});
|
|
273980
|
+
}
|
|
273981
|
+
/**
|
|
273982
|
+
* Initialize the notification store. Call once at startup.
|
|
273983
|
+
*/
|
|
273984
|
+
function initNotifications() {
|
|
273985
|
+
notifications = loadNotifications();
|
|
273986
|
+
console.log(`[notifications] Loaded ${notifications.length} notifications`);
|
|
273987
|
+
}
|
|
273988
|
+
/**
|
|
273989
|
+
* Set a callback to be called when a new notification is created.
|
|
273990
|
+
* The WebChannel uses this to broadcast to all connected clients.
|
|
273991
|
+
*/
|
|
273992
|
+
function setBroadcastCallback(callback) {
|
|
273993
|
+
broadcastCallback = callback;
|
|
273994
|
+
}
|
|
273995
|
+
/**
|
|
273996
|
+
* Create a new notification.
|
|
273997
|
+
* If dedupKey is provided and matches an existing unread notification,
|
|
273998
|
+
* the existing notification is updated instead of creating a new one.
|
|
273999
|
+
*/
|
|
274000
|
+
function createNotification(input) {
|
|
274001
|
+
const duplicate = findDuplicate(input);
|
|
274002
|
+
if (duplicate) {
|
|
274003
|
+
duplicate.message = input.message;
|
|
274004
|
+
duplicate.timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
274005
|
+
duplicate.title = input.title;
|
|
274006
|
+
duplicate.type = input.type;
|
|
274007
|
+
duplicate.metadata = {
|
|
274008
|
+
...duplicate.metadata,
|
|
274009
|
+
...input.metadata
|
|
274010
|
+
};
|
|
274011
|
+
queuePersist();
|
|
274012
|
+
if (broadcastCallback) broadcastCallback(duplicate);
|
|
274013
|
+
return duplicate;
|
|
274014
|
+
}
|
|
274015
|
+
const notification = {
|
|
274016
|
+
id: randomUUID(),
|
|
274017
|
+
type: input.type,
|
|
274018
|
+
source: input.source,
|
|
274019
|
+
title: input.title,
|
|
274020
|
+
message: input.message,
|
|
274021
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
274022
|
+
read: false,
|
|
274023
|
+
dismissed: false,
|
|
274024
|
+
sessionId: input.sessionId,
|
|
274025
|
+
actionUrl: input.actionUrl,
|
|
274026
|
+
metadata: input.metadata
|
|
274027
|
+
};
|
|
274028
|
+
notifications.push(notification);
|
|
274029
|
+
pruneNotifications();
|
|
274030
|
+
queuePersist();
|
|
274031
|
+
if (broadcastCallback) broadcastCallback(notification);
|
|
274032
|
+
return notification;
|
|
274033
|
+
}
|
|
274034
|
+
/**
|
|
274035
|
+
* Get notifications matching the filter criteria.
|
|
274036
|
+
*/
|
|
274037
|
+
function getNotifications(filter = {}) {
|
|
274038
|
+
const { includeRead = true, includeDismissed = false, source, limit = 100 } = filter;
|
|
274039
|
+
let result = notifications;
|
|
274040
|
+
if (!includeDismissed) result = result.filter((n) => !n.dismissed);
|
|
274041
|
+
if (!includeRead) result = result.filter((n) => !n.read);
|
|
274042
|
+
if (source) result = result.filter((n) => n.source === source);
|
|
274043
|
+
result = result.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
|
274044
|
+
return result.slice(0, limit);
|
|
274045
|
+
}
|
|
274046
|
+
/**
|
|
274047
|
+
* Mark a notification as read.
|
|
274048
|
+
*/
|
|
274049
|
+
function markRead(id) {
|
|
274050
|
+
const notification = notifications.find((n) => n.id === id);
|
|
274051
|
+
if (notification && !notification.read) {
|
|
274052
|
+
notification.read = true;
|
|
274053
|
+
queuePersist();
|
|
274054
|
+
}
|
|
274055
|
+
return notification;
|
|
274056
|
+
}
|
|
274057
|
+
/**
|
|
274058
|
+
* Mark all notifications as read.
|
|
274059
|
+
*/
|
|
274060
|
+
function markAllRead() {
|
|
274061
|
+
let count = 0;
|
|
274062
|
+
for (const notification of notifications) if (!notification.read && !notification.dismissed) {
|
|
274063
|
+
notification.read = true;
|
|
274064
|
+
count++;
|
|
274065
|
+
}
|
|
274066
|
+
if (count > 0) queuePersist();
|
|
274067
|
+
return count;
|
|
274068
|
+
}
|
|
274069
|
+
/**
|
|
274070
|
+
* Dismiss a notification (soft delete - keeps in storage but hidden from UI).
|
|
274071
|
+
*/
|
|
274072
|
+
function dismissNotification(id) {
|
|
274073
|
+
const notification = notifications.find((n) => n.id === id);
|
|
274074
|
+
if (notification && !notification.dismissed) {
|
|
274075
|
+
notification.dismissed = true;
|
|
274076
|
+
queuePersist();
|
|
274077
|
+
}
|
|
274078
|
+
return notification;
|
|
274079
|
+
}
|
|
274080
|
+
/**
|
|
274081
|
+
* Dismiss all notifications.
|
|
274082
|
+
*/
|
|
274083
|
+
function dismissAll() {
|
|
274084
|
+
let count = 0;
|
|
274085
|
+
for (const notification of notifications) if (!notification.dismissed) {
|
|
274086
|
+
notification.dismissed = true;
|
|
274087
|
+
count++;
|
|
274088
|
+
}
|
|
274089
|
+
if (count > 0) queuePersist();
|
|
274090
|
+
return count;
|
|
274091
|
+
}
|
|
274092
|
+
//#endregion
|
|
273902
274093
|
//#region src/extensions/cron/lock.ts
|
|
273903
274094
|
const LOCK_PATH$1 = join(getAppDir(), "cron", "cron.lock");
|
|
273904
274095
|
function isProcessAlive$1(pid) {
|
|
@@ -274116,7 +274307,36 @@ var CronScheduler = class {
|
|
|
274116
274307
|
this.persist();
|
|
274117
274308
|
} catch (err) {
|
|
274118
274309
|
job.consecutiveFailures += 1;
|
|
274119
|
-
|
|
274310
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
274311
|
+
createNotification({
|
|
274312
|
+
type: "error",
|
|
274313
|
+
source: "cron",
|
|
274314
|
+
title: `Cron job failed: ${job.name}`,
|
|
274315
|
+
message: errorMessage,
|
|
274316
|
+
dedupKey: `cron-fail-${job.jobId}`,
|
|
274317
|
+
metadata: {
|
|
274318
|
+
dedupKey: `cron-fail-${job.jobId}`,
|
|
274319
|
+
jobId: job.jobId,
|
|
274320
|
+
jobName: job.name,
|
|
274321
|
+
consecutiveFailures: job.consecutiveFailures,
|
|
274322
|
+
error: errorMessage
|
|
274323
|
+
}
|
|
274324
|
+
});
|
|
274325
|
+
if (job.consecutiveFailures >= 5) {
|
|
274326
|
+
job.enabled = false;
|
|
274327
|
+
createNotification({
|
|
274328
|
+
type: "warning",
|
|
274329
|
+
source: "cron",
|
|
274330
|
+
title: `Cron job disabled: ${job.name}`,
|
|
274331
|
+
message: `Job "${job.name}" was automatically disabled after 5 consecutive failures.`,
|
|
274332
|
+
metadata: {
|
|
274333
|
+
jobId: job.jobId,
|
|
274334
|
+
jobName: job.name,
|
|
274335
|
+
consecutiveFailures: job.consecutiveFailures,
|
|
274336
|
+
autoDisabled: true
|
|
274337
|
+
}
|
|
274338
|
+
});
|
|
274339
|
+
}
|
|
274120
274340
|
this.persist();
|
|
274121
274341
|
console.error(`[cron] Job failed (${job.name}):`, err);
|
|
274122
274342
|
} finally {
|
|
@@ -274598,7 +274818,23 @@ var HeartbeatRunner = class {
|
|
|
274598
274818
|
this.stats.lastResult = result;
|
|
274599
274819
|
this.stats.runCount += 1;
|
|
274600
274820
|
if (result.ok) this.stats.okCount += 1;
|
|
274601
|
-
else
|
|
274821
|
+
else {
|
|
274822
|
+
this.stats.alertCount += 1;
|
|
274823
|
+
const message = result.error ? `Error: ${result.error}` : result.normalizedResponse || "Heartbeat check failed";
|
|
274824
|
+
createNotification({
|
|
274825
|
+
type: result.error ? "error" : "warning",
|
|
274826
|
+
source: "heartbeat",
|
|
274827
|
+
title: "Heartbeat Alert",
|
|
274828
|
+
message,
|
|
274829
|
+
dedupKey: `heartbeat-${this.cwd}`,
|
|
274830
|
+
metadata: {
|
|
274831
|
+
dedupKey: `heartbeat-${this.cwd}`,
|
|
274832
|
+
ok: result.ok,
|
|
274833
|
+
error: result.error,
|
|
274834
|
+
timestamp: result.timestamp
|
|
274835
|
+
}
|
|
274836
|
+
});
|
|
274837
|
+
}
|
|
274602
274838
|
}
|
|
274603
274839
|
async runHeartbeatPrompt(prompt) {
|
|
274604
274840
|
const config = loadConfig();
|
|
@@ -274923,6 +275159,75 @@ function createReloadRuntimeExtension(reloadAllSessions) {
|
|
|
274923
275159
|
};
|
|
274924
275160
|
}
|
|
274925
275161
|
//#endregion
|
|
275162
|
+
//#region src/extensions/web-notifications.ts
|
|
275163
|
+
const NotifyWebUIParamsSchema = Type$1.Object({
|
|
275164
|
+
title: Type$1.String({
|
|
275165
|
+
description: "Short notification title (max 100 characters)",
|
|
275166
|
+
maxLength: 100
|
|
275167
|
+
}),
|
|
275168
|
+
message: Type$1.String({ description: "Detailed notification message" }),
|
|
275169
|
+
type: StringEnum([
|
|
275170
|
+
"info",
|
|
275171
|
+
"warning",
|
|
275172
|
+
"error",
|
|
275173
|
+
"success"
|
|
275174
|
+
]),
|
|
275175
|
+
actionUrl: Type$1.Optional(Type$1.String({ description: "Optional URL path (e.g., /sessions/abc123) to link to when user clicks the notification" }))
|
|
275176
|
+
});
|
|
275177
|
+
/**
|
|
275178
|
+
* Create the web notifications extension factory.
|
|
275179
|
+
*
|
|
275180
|
+
* WEB UI CHANNEL ONLY: This extension provides notifications exclusively
|
|
275181
|
+
* for the web UI channel. Do not use for other channels.
|
|
275182
|
+
*/
|
|
275183
|
+
function createWebNotificationsExtension() {
|
|
275184
|
+
return function webNotificationsExtension(pi) {
|
|
275185
|
+
pi.registerTool({
|
|
275186
|
+
name: "notify_web_ui",
|
|
275187
|
+
label: "Notify Web UI",
|
|
275188
|
+
description: "[WEB UI CHANNEL ONLY] Send a notification to the user through the web UI. This creates a persistent notification that appears in the web UI notifications panel and triggers a real-time toast alert. Use this to inform users about important events, task completions, errors, or status updates when they may not be actively viewing the session. IMPORTANT: This tool is exclusive to the web UI channel and has no effect on other channels.",
|
|
275189
|
+
parameters: NotifyWebUIParamsSchema,
|
|
275190
|
+
async execute(_toolCallId, rawParams) {
|
|
275191
|
+
const params = rawParams;
|
|
275192
|
+
try {
|
|
275193
|
+
const notification = createNotification({
|
|
275194
|
+
type: params.type,
|
|
275195
|
+
source: "session",
|
|
275196
|
+
title: params.title,
|
|
275197
|
+
message: params.message,
|
|
275198
|
+
actionUrl: params.actionUrl
|
|
275199
|
+
});
|
|
275200
|
+
return {
|
|
275201
|
+
content: [{
|
|
275202
|
+
type: "text",
|
|
275203
|
+
text: `Web UI notification sent: "${params.title}"`
|
|
275204
|
+
}],
|
|
275205
|
+
details: {
|
|
275206
|
+
success: true,
|
|
275207
|
+
notificationId: notification.id,
|
|
275208
|
+
timestamp: notification.timestamp
|
|
275209
|
+
}
|
|
275210
|
+
};
|
|
275211
|
+
} catch (err) {
|
|
275212
|
+
return {
|
|
275213
|
+
content: [{
|
|
275214
|
+
type: "text",
|
|
275215
|
+
text: `Failed to send web UI notification: ${err instanceof Error ? err.message : String(err)}`
|
|
275216
|
+
}],
|
|
275217
|
+
details: {
|
|
275218
|
+
success: false,
|
|
275219
|
+
error: err instanceof Error ? err.message : String(err)
|
|
275220
|
+
}
|
|
275221
|
+
};
|
|
275222
|
+
}
|
|
275223
|
+
}
|
|
275224
|
+
});
|
|
275225
|
+
pi.on("before_agent_start", async (event) => {
|
|
275226
|
+
return { systemPrompt: event.systemPrompt + "\n\n[Web UI Channel] You can send notifications to the web UI using the notify_web_ui tool. This is useful for: task completions, errors requiring attention, long-running operations finishing, or critical status updates. Notifications appear in the user's web UI notification panel, trigger toast alerts, and persist across page refreshes. Note: This tool is specific to the web UI channel." };
|
|
275227
|
+
});
|
|
275228
|
+
};
|
|
275229
|
+
}
|
|
275230
|
+
//#endregion
|
|
274926
275231
|
//#region src/lib/scoped-model-resolver.ts
|
|
274927
275232
|
const THINKING_LEVEL_SUFFIXES = new Set([
|
|
274928
275233
|
"off",
|
|
@@ -275263,6 +275568,7 @@ function buildExtensionFactories(config, cwd) {
|
|
|
275263
275568
|
const extensionFactories = [];
|
|
275264
275569
|
extensionFactories.push(createCronExtension());
|
|
275265
275570
|
extensionFactories.push(createHeartbeatExtension());
|
|
275571
|
+
extensionFactories.push(createWebNotificationsExtension());
|
|
275266
275572
|
extensionFactories.push(createPackageManagerExtension(reloadAllSessions));
|
|
275267
275573
|
extensionFactories.push(createReloadRuntimeExtension(reloadAllSessions));
|
|
275268
275574
|
if (config.agent?.restrictToWorkspace ?? true) {
|
|
@@ -278107,8 +278413,12 @@ var WebChannel = class {
|
|
|
278107
278413
|
pendingExtensionRequests = /* @__PURE__ */ new Map();
|
|
278108
278414
|
/** Pending auth login flows: Map<loginFlowId, { ws, inputResolver, abortController }> */
|
|
278109
278415
|
pendingLoginFlows = /* @__PURE__ */ new Map();
|
|
278416
|
+
/** All connected WebSocket clients (for broadcasting) */
|
|
278417
|
+
allConnections = /* @__PURE__ */ new Set();
|
|
278110
278418
|
/** Heartbeat interval for keeping WebSocket connections alive */
|
|
278111
278419
|
heartbeatInterval = null;
|
|
278420
|
+
/** Notification broadcast callback cleanup function */
|
|
278421
|
+
notificationCleanup = null;
|
|
278112
278422
|
constructor(options) {
|
|
278113
278423
|
this.options = options;
|
|
278114
278424
|
}
|
|
@@ -278131,8 +278441,9 @@ var WebChannel = class {
|
|
|
278131
278441
|
if (!origin || !this.options.allowedOrigins.includes(origin)) return c.text("Forbidden", 403);
|
|
278132
278442
|
}
|
|
278133
278443
|
return {
|
|
278134
|
-
onOpen: (_evt,
|
|
278444
|
+
onOpen: (_evt, ws) => {
|
|
278135
278445
|
console.log("[web] Client connected");
|
|
278446
|
+
this.allConnections.add(ws);
|
|
278136
278447
|
},
|
|
278137
278448
|
onMessage: async (evt, ws) => {
|
|
278138
278449
|
try {
|
|
@@ -278151,6 +278462,7 @@ var WebChannel = class {
|
|
|
278151
278462
|
},
|
|
278152
278463
|
onClose: (_evt, ws) => {
|
|
278153
278464
|
console.log("[web] Client disconnected");
|
|
278465
|
+
this.allConnections.delete(ws);
|
|
278154
278466
|
for (const [sessionId, subscribers] of this.sessionSubscriptions.entries()) {
|
|
278155
278467
|
subscribers.delete(ws);
|
|
278156
278468
|
if (subscribers.size === 0) this.sessionSubscriptions.delete(sessionId);
|
|
@@ -278272,6 +278584,23 @@ var WebChannel = class {
|
|
|
278272
278584
|
}, HEARTBEAT_INTERVAL);
|
|
278273
278585
|
console.log(`[web] WebSocket server listening on port ${this.options.port}`);
|
|
278274
278586
|
console.log(`[web] Open in browser: http://localhost:${this.options.port}?token=${this.options.authToken}`);
|
|
278587
|
+
const broadcastToAll = (notification) => {
|
|
278588
|
+
const message = {
|
|
278589
|
+
sessionId: "_notifications",
|
|
278590
|
+
event: {
|
|
278591
|
+
type: "notification",
|
|
278592
|
+
notification
|
|
278593
|
+
}
|
|
278594
|
+
};
|
|
278595
|
+
const json = JSON.stringify(message);
|
|
278596
|
+
for (const ws of this.allConnections) try {
|
|
278597
|
+
ws.send(json);
|
|
278598
|
+
} catch (err) {
|
|
278599
|
+
console.error("[web] Failed to broadcast notification:", err);
|
|
278600
|
+
}
|
|
278601
|
+
};
|
|
278602
|
+
setBroadcastCallback(broadcastToAll);
|
|
278603
|
+
this.notificationCleanup = () => setBroadcastCallback(null);
|
|
278275
278604
|
}
|
|
278276
278605
|
async send(_chatId, _text, _options) {}
|
|
278277
278606
|
async stop() {
|
|
@@ -278279,6 +278608,11 @@ var WebChannel = class {
|
|
|
278279
278608
|
clearInterval(this.heartbeatInterval);
|
|
278280
278609
|
this.heartbeatInterval = null;
|
|
278281
278610
|
}
|
|
278611
|
+
if (this.notificationCleanup) {
|
|
278612
|
+
this.notificationCleanup();
|
|
278613
|
+
this.notificationCleanup = null;
|
|
278614
|
+
}
|
|
278615
|
+
this.allConnections.clear();
|
|
278282
278616
|
if (this.wss) {
|
|
278283
278617
|
for (const client of this.wss.clients) try {
|
|
278284
278618
|
client.close(1001, "Server shutting down");
|
|
@@ -278343,6 +278677,61 @@ var WebChannel = class {
|
|
|
278343
278677
|
});
|
|
278344
278678
|
return;
|
|
278345
278679
|
}
|
|
278680
|
+
if (command.type === "get_notifications") {
|
|
278681
|
+
const notifications = getNotifications();
|
|
278682
|
+
this.sendResponse(ws, void 0, {
|
|
278683
|
+
id: commandId,
|
|
278684
|
+
type: "response",
|
|
278685
|
+
command: "get_notifications",
|
|
278686
|
+
success: true,
|
|
278687
|
+
data: { notifications }
|
|
278688
|
+
});
|
|
278689
|
+
return;
|
|
278690
|
+
}
|
|
278691
|
+
if (command.type === "mark_notification_read") {
|
|
278692
|
+
const notification = markRead(command.notificationId);
|
|
278693
|
+
this.sendResponse(ws, void 0, {
|
|
278694
|
+
id: commandId,
|
|
278695
|
+
type: "response",
|
|
278696
|
+
command: "mark_notification_read",
|
|
278697
|
+
success: true,
|
|
278698
|
+
data: { notification }
|
|
278699
|
+
});
|
|
278700
|
+
return;
|
|
278701
|
+
}
|
|
278702
|
+
if (command.type === "mark_all_notifications_read") {
|
|
278703
|
+
const count = markAllRead();
|
|
278704
|
+
this.sendResponse(ws, void 0, {
|
|
278705
|
+
id: commandId,
|
|
278706
|
+
type: "response",
|
|
278707
|
+
command: "mark_all_notifications_read",
|
|
278708
|
+
success: true,
|
|
278709
|
+
data: { count }
|
|
278710
|
+
});
|
|
278711
|
+
return;
|
|
278712
|
+
}
|
|
278713
|
+
if (command.type === "dismiss_notification") {
|
|
278714
|
+
const notification = dismissNotification(command.notificationId);
|
|
278715
|
+
this.sendResponse(ws, void 0, {
|
|
278716
|
+
id: commandId,
|
|
278717
|
+
type: "response",
|
|
278718
|
+
command: "dismiss_notification",
|
|
278719
|
+
success: true,
|
|
278720
|
+
data: { notification }
|
|
278721
|
+
});
|
|
278722
|
+
return;
|
|
278723
|
+
}
|
|
278724
|
+
if (command.type === "dismiss_all_notifications") {
|
|
278725
|
+
const count = dismissAll();
|
|
278726
|
+
this.sendResponse(ws, void 0, {
|
|
278727
|
+
id: commandId,
|
|
278728
|
+
type: "response",
|
|
278729
|
+
command: "dismiss_all_notifications",
|
|
278730
|
+
success: true,
|
|
278731
|
+
data: { count }
|
|
278732
|
+
});
|
|
278733
|
+
return;
|
|
278734
|
+
}
|
|
278346
278735
|
if (!inbound.sessionId) {
|
|
278347
278736
|
this.sendError(ws, void 0, command.type, "sessionId is required", commandId);
|
|
278348
278737
|
return;
|
|
@@ -279490,6 +279879,7 @@ async function restartChannels() {
|
|
|
279490
279879
|
async function startDaemon() {
|
|
279491
279880
|
writePidFile();
|
|
279492
279881
|
console.log(`[daemon] Starting clankie daemon (pid ${process.pid})...`);
|
|
279882
|
+
initNotifications();
|
|
279493
279883
|
await initializeChannels();
|
|
279494
279884
|
const configPath = getConfigPath();
|
|
279495
279885
|
const cronJobsPath = getCronJobsPath();
|
package/package.json
CHANGED
package/web-ui-dist/_shell.html
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
<!DOCTYPE html><html lang="en"><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-CP6prmzV.js"/><link rel="modulepreload" href="/assets/index-TBNB5eLy.js"/><link rel="icon" type="image/svg+xml" href="/favicon.svg"/><link rel="stylesheet" href="/assets/styles-KEhqa3CU.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)] border-r border-border/20"><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="p-2 flex flex-col gap-3 px-4 py-4"><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="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 text-sm group-data-[collapsible=icon]:p-0! h-10"><div class="flex items-center gap-3"><div class="flex items-center justify-center w-8 h-8 rounded-xl bg-primary/15 border border-primary/20"><span class="text-sm font-mono font-bold text-primary">c/</span></div><span class="text-lg font-mono font-semibold tracking-tight text-sidebar-foreground">clankie</span></div></button></li></ul><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_1phb6_" 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 p-2 text-left 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-10 text-sm font-medium bg-primary text-primary-foreground hover:bg-primary/90 hover:text-primary-foreground active:bg-primary/90 active:text-primary-foreground transition-all rounded-xl"><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 h-4 w-4 mr-1" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="M8 12h8"></path><path d="M12 8v8"></path></svg><span>New Chat</span></button></li></ul></div><div data-slot="sidebar-content" data-sidebar="content" class="no-scrollbar flex min-h-0 flex-1 flex-col overflow-auto group-data-[collapsible=icon]:overflow-hidden gap-2 px-2 py-2"><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 py-2"><div data-slot="sidebar-group-label" data-sidebar="group-label" class="ring-sidebar-ring h-8 rounded-md 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 text-[11px] font-medium uppercase tracking-wider text-muted-foreground/40 px-3 py-2">Recent</div><ul data-slot="sidebar-menu" data-sidebar="menu" class="flex w-full min-w-0 flex-col gap-1 px-1"><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 text-sm h-12 opacity-40" disabled=""><span class="text-sm text-sidebar-foreground/40">No sessions yet</span></button></li></ul></div></div><div data-slot="sidebar-footer" data-sidebar="footer" class="gap-2 p-2 flex flex-col px-2 py-2"><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="dropdown-menu-trigger" 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 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 h-10 text-sm text-sidebar-foreground/70 hover:text-sidebar-foreground rounded-xl" tabindex="0" aria-haspopup="menu" id="base-ui-_R_fhb6_"><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 h-4 w-4" 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></button></li></ul></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 flex w-full flex-1 flex-col relative min-h-0"><header class="topbar-glass sticky top-0 z-40 flex h-14 shrink-0 items-center border-b px-4 md:hidden"><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><div class="min-h-0 flex-1"><!--$--><!--$--><!--/$--><script></script><!--/$--></div></main></div><section aria-label="Notifications alt+T" tabindex="-1" aria-live="polite" aria-relevant="additions text" aria-atomic="false"></section><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"><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-CKIteeQH.js"/><link rel="modulepreload" href="/assets/index-DTLVg2XM.js"/><link rel="icon" type="image/svg+xml" href="/favicon.svg"/><link rel="stylesheet" href="/assets/styles-CuRG0ztT.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)] border-r border-border/20"><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="p-2 flex flex-col gap-3 px-4 py-4"><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="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 text-sm group-data-[collapsible=icon]:p-0! h-10"><div class="flex items-center gap-3"><div class="flex items-center justify-center w-8 h-8 rounded-xl bg-primary/15 border border-primary/20"><span class="text-sm font-mono font-bold text-primary">c/</span></div><span class="text-lg font-mono font-semibold tracking-tight text-sidebar-foreground">clankie</span></div></button></li></ul><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_1phb6_" 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 p-2 text-left 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-10 text-sm font-medium bg-primary text-primary-foreground hover:bg-primary/90 hover:text-primary-foreground active:bg-primary/90 active:text-primary-foreground transition-all rounded-xl"><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 h-4 w-4 mr-1" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="M8 12h8"></path><path d="M12 8v8"></path></svg><span>New Chat</span></button></li></ul></div><div data-slot="sidebar-content" data-sidebar="content" class="no-scrollbar flex min-h-0 flex-1 flex-col overflow-auto group-data-[collapsible=icon]:overflow-hidden gap-2 px-2 py-2"><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 py-2"><div data-slot="sidebar-group-label" data-sidebar="group-label" class="ring-sidebar-ring h-8 rounded-md 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 text-[11px] font-medium uppercase tracking-wider text-muted-foreground/40 px-3 py-2">Recent</div><ul data-slot="sidebar-menu" data-sidebar="menu" class="flex w-full min-w-0 flex-col gap-1 px-1"><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 text-sm h-12 opacity-40" disabled=""><span class="text-sm text-sidebar-foreground/40">No sessions yet</span></button></li></ul></div></div><div data-slot="sidebar-footer" data-sidebar="footer" class="gap-2 p-2 flex flex-col px-2 py-2"><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="/notifications" 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 p-2 text-left 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 h-10 text-sm rounded-xl transition-colors text-sidebar-foreground/70 hover:text-sidebar-foreground"><div class="relative flex items-center justify-center w-4"><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-bell h-4 w-4" aria-hidden="true"><path d="M10.268 21a2 2 0 0 0 3.464 0"></path><path d="M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326"></path></svg></div><span>Notifications</span></a></li><li data-slot="sidebar-menu-item" data-sidebar="menu-item" class="group/menu-item relative"><button type="button" data-slot="dropdown-menu-trigger" 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 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 h-10 text-sm text-sidebar-foreground/70 hover:text-sidebar-foreground rounded-xl" tabindex="0" aria-haspopup="menu" id="base-ui-_R_1rhb6_"><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 h-4 w-4" 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></button></li></ul></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 flex w-full flex-1 flex-col relative min-h-0"><header class="topbar-glass sticky top-0 z-40 flex h-14 shrink-0 items-center border-b px-4 md:hidden"><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><div class="min-h-0 flex-1"><!--$--><!--$--><!--/$--><script></script><!--/$--></div></main></div><section aria-label="Notifications alt+T" tabindex="-1" aria-live="polite" aria-relevant="additions text" aria-atomic="false"></section><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-CKIteeQH.js"],assets:$R[5]=[$R[6]={tag:"script",attrs:$R[7]={type:"module",async:!0},children:"import(\"/assets/main-CKIteeQH.js\")"}]}}},matches:$R[8]=[$R[9]={i:"__root__",u:1772820806652,s:"success",ssr:!0}],lastMatchId:"__root__"})($R["tsr"]);$_TSR.e();document.currentScript.remove()</script><script type="module" async="">import("/assets/main-CKIteeQH.js")</script></body></html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{c as b,V as K,W as R,Y as O,Z as E,r as u,j as e,_ as T,$ as B,i as p,a0 as V,a1 as $,B as x,a2 as q,a3 as H,a4 as U,u as A,a5 as S,a6 as Y,d as j,J as y,Q as k,q as W,a7 as J,a8 as w,s as Q,a9 as X,h as D,x as P,y as F,H as Z,k as L,K as G,aa as ee,ab as te}from"./main-CKIteeQH.js";import{F as I,a as z}from"./field-Dy_c96vc.js";import{C as M}from"./circle-x-Bek90N0j.js";import{B as se}from"./badge-B27MkVic.js";const ae=[["path",{d:"M21.801 10A10 10 0 1 1 17 3.335",key:"yps3ct"}],["path",{d:"m9 11 3 3L22 4",key:"1pflzl"}]],ne=b("circle-check-big",ae);const re=[["path",{d:"M15 3h6v6",key:"1q9fwt"}],["path",{d:"M10 14 21 3",key:"gplh6r"}],["path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6",key:"a6xqqp"}]],ie=b("external-link",re);const oe=[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}]],_=b("shield",oe);function le(t){const{children:a,open:s,defaultOpen:l=!1,onOpenChange:r,onOpenChangeComplete:i,actionsRef:m,handle:f,triggerId:g,defaultTriggerId:h=null}=t,v=K(),N=!!v,c=R(()=>f?.store??new O({open:l,openProp:s,activeTriggerId:h,triggerIdProp:g,modal:!0,disablePointerDismissal:!0,nested:N,role:"alertdialog"})).current;c.useControlledProp("openProp",s),c.useControlledProp("triggerIdProp",g),c.useSyncedValue("nested",N),c.useContextCallback("onOpenChange",r),c.useContextCallback("onOpenChangeComplete",i);const C=c.useState("payload");E({store:c,actionsRef:m,parentContext:v?.store.context});const n=u.useMemo(()=>({store:c}),[c]);return e.jsx(T.Provider,{value:n,children:typeof a=="function"?a({payload:C}):a})}function ce({...t}){return e.jsx(le,{"data-slot":"alert-dialog",...t})}function de({...t}){return e.jsx(H,{"data-slot":"alert-dialog-portal",...t})}function ue({className:t,...a}){return e.jsx(U,{"data-slot":"alert-dialog-overlay",className:p("data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs fixed inset-0 isolate z-50",t),...a})}function me({className:t,size:a="default",...s}){return e.jsxs(de,{children:[e.jsx(ue,{}),e.jsx(B,{"data-slot":"alert-dialog-content","data-size":a,className:p("data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 bg-background ring-foreground/10 gap-4 rounded-xl p-4 ring-1 duration-100 data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-sm group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 outline-none",t),...s})]})}function xe({className:t,...a}){return e.jsx("div",{"data-slot":"alert-dialog-header",className:p("grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-4 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]",t),...a})}function ge({className:t,...a}){return e.jsx("div",{"data-slot":"alert-dialog-footer",className:p("bg-muted/50 -mx-4 -mb-4 rounded-b-xl border-t p-4 flex flex-col-reverse gap-2 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end",t),...a})}function he({className:t,...a}){return e.jsx(V,{"data-slot":"alert-dialog-title",className:p("text-base font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2",t),...a})}function pe({className:t,...a}){return e.jsx($,{"data-slot":"alert-dialog-description",className:p("text-muted-foreground *:[a]:hover:text-foreground text-sm text-balance md:text-pretty *:[a]:underline *:[a]:underline-offset-3",t),...a})}function fe({className:t,...a}){return e.jsx(x,{"data-slot":"alert-dialog-action",className:p(t),...a})}function je({className:t,variant:a="outline",size:s="default",...l}){return e.jsx(q,{"data-slot":"alert-dialog-cancel",className:p(t),render:e.jsx(x,{variant:a,size:s}),...l})}function ye({open:t,onOpenChange:a}){const{loginFlow:s}=A(S,i=>({loginFlow:i.loginFlow}));if(u.useEffect(()=>{!t&&s&&Y()},[t,s]),u.useEffect(()=>{if(t&&s?.status==="complete"&&s.success===!0){const i=setTimeout(()=>{a(!1)},1500);return()=>clearTimeout(i)}},[t,s,a]),!s)return null;const l=()=>{s.loginFlowId&&s.status!=="complete"&&s.status!=="error"&&j.getClient()?.authLoginCancel(s.loginFlowId),a(!1)},r=()=>{a(!1)};return e.jsx(ce,{open:t,onOpenChange:a,children:e.jsxs(me,{children:[e.jsxs(xe,{children:[e.jsx(he,{children:s.status==="complete"&&s.success?"Login Successful":s.status==="error"?"Login Failed":`Sign in to ${s.providerId}`}),e.jsx(pe,{children:e.jsx(ve,{flow:s})})]}),e.jsx(ge,{children:s.status==="complete"||s.status==="error"?e.jsx(fe,{onClick:r,children:"Close"}):e.jsx(je,{onClick:l,children:"Cancel"})})]})})}function ve({flow:t}){const a=j.getClient(),s=u.useRef(null),l=i=>{t.loginFlowId&&a&&a.authLoginInput(t.loginFlowId,i)},r=i=>{t.loginFlowId&&a&&a.authLoginInput(t.loginFlowId,i)};return u.useEffect(()=>{t.status==="waiting_url"&&t.url&&s.current!==t.url&&(s.current=t.url,window.open(t.url,"_blank"))},[t.status,t.url]),t.status==="idle"?e.jsxs("div",{className:"flex items-center gap-3 py-4",children:[e.jsx(y,{className:"h-5 w-5 animate-spin text-muted-foreground"}),e.jsx("span",{className:"text-sm",children:"Starting login..."})]}):t.status==="waiting_url"&&t.url?e.jsxs("div",{className:"space-y-4 py-4",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx(y,{className:"h-5 w-5 animate-spin text-muted-foreground"}),e.jsx("span",{className:"text-sm",children:"Complete the authentication in your browser..."})]}),t.instructions&&e.jsx("p",{className:"text-xs text-muted-foreground rounded-md bg-muted p-2",children:t.instructions}),e.jsxs(x,{onClick:()=>window.open(t.url,"_blank"),className:"w-full",variant:"outline",size:"sm",children:[e.jsx(ie,{className:"mr-2 h-4 w-4"}),"Open in Browser"]}),t.showManualInput&&e.jsx(Ne,{onSubmit:l}),t.progressMessage&&e.jsxs("div",{className:"flex items-center gap-2 text-xs text-muted-foreground",children:[e.jsx(y,{className:"h-3 w-3 animate-spin"}),e.jsx("span",{children:t.progressMessage})]})]}):t.status==="waiting_input"&&t.promptMessage?e.jsx("div",{className:"py-4",children:e.jsx(Ce,{message:t.promptMessage,placeholder:t.promptPlaceholder,onSubmit:r})}):t.status==="in_progress"?e.jsxs("div",{className:"flex items-center gap-3 py-4",children:[e.jsx(y,{className:"h-5 w-5 animate-spin text-muted-foreground"}),e.jsx("span",{className:"text-sm",children:t.progressMessage||"Completing authentication..."})]}):t.status==="complete"&&t.success?e.jsxs("div",{className:"flex items-center gap-3 py-4 text-green-600",children:[e.jsx(ne,{className:"h-5 w-5"}),e.jsxs("span",{className:"text-sm",children:["Successfully authenticated with ",t.providerId]})]}):t.status==="error"||t.status==="complete"&&!t.success?e.jsxs("div",{className:"space-y-2 py-4",children:[e.jsxs("div",{className:"flex items-center gap-3 text-destructive",children:[e.jsx(M,{className:"h-5 w-5"}),e.jsx("span",{className:"text-sm font-medium",children:"Authentication failed"})]}),t.error&&e.jsx("p",{className:"text-xs text-muted-foreground rounded-md bg-muted p-2",children:t.error})]}):null}function Ne({onSubmit:t}){const a=s=>{s.preventDefault();const r=new FormData(s.currentTarget).get("code");r?.trim()&&t(r.trim())};return e.jsx("form",{onSubmit:a,className:"space-y-2",children:e.jsxs(I,{children:[e.jsx(z,{htmlFor:"manual-code",children:"Or paste the authorization code/URL here:"}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx(k,{id:"manual-code",name:"code",type:"text",placeholder:"Paste code or redirect URL",className:"flex-1"}),e.jsx(x,{type:"submit",size:"sm",children:"Submit"})]})]})})}function Ce({message:t,placeholder:a,onSubmit:s}){const l=r=>{r.preventDefault();const m=new FormData(r.currentTarget).get("prompt-value");m?.trim()&&s(m.trim())};return e.jsxs("form",{onSubmit:l,className:"space-y-3",children:[e.jsx("p",{className:"text-sm",children:t}),e.jsx(I,{children:e.jsxs("div",{className:"flex gap-2",children:[e.jsx(k,{id:"prompt-value",name:"prompt-value",type:"text",placeholder:a||"Enter value",className:"flex-1",autoFocus:!0}),e.jsx(x,{type:"submit",size:"sm",children:"Submit"})]})})]})}function Pe(){const{status:t}=A(W,s=>({status:s.status}));return t==="connected"?e.jsx(be,{}):e.jsx("div",{className:"h-full flex items-center justify-center chat-background",children:e.jsxs("div",{className:"text-center space-y-4 max-w-md p-8",children:[e.jsx("div",{className:"inline-flex items-center justify-center w-16 h-16 rounded-2xl bg-destructive/10 border border-destructive/20 mb-2",children:e.jsx(_,{className:"h-8 w-8 text-destructive"})}),e.jsxs("div",{className:"space-y-2",children:[e.jsx("h2",{className:"text-2xl font-semibold",children:"Not Connected"}),e.jsx("p",{className:"text-muted-foreground",children:"Connect to clankie to manage AI provider authentication"})]})]})})}function be(){const{providers:t,isLoadingProviders:a,loginFlow:s}=A(S,n=>({providers:n.providers,isLoadingProviders:n.isLoadingProviders,loginFlow:n.loginFlow})),[l,r]=u.useState(!1),[i,m]=u.useState(null),[f,g]=u.useState(""),h=u.useCallback(async()=>{const n=j.getClient();if(n){J(!0);try{const{providers:o}=await n.getAuthProviders();w(o)}catch(o){console.error("Failed to load auth providers:",o),w([])}}},[]);u.useEffect(()=>{h()},[h]),u.useEffect(()=>{if(s?.status==="complete"&&s.success===!0){h();const{activeSessionId:n}=Q.state;if(n){const o=j.getClient();o&&o.getAvailableModels(n).then(({models:d})=>{X(d),console.log("[settings/auth] Refreshed available models after OAuth login")}).catch(d=>{console.error("[settings/auth] Failed to refresh available models:",d)})}}},[s?.status,s?.success,h]);const v=async n=>{const o=j.getClient();if(o)try{const{loginFlowId:d}=await o.authLogin(n);te(d,n),r(!0)}catch(d){console.error("Failed to start login:",d)}},N=n=>{m(n),g("")},c=async n=>{const o=j.getClient();if(!(!o||!f.trim()))try{await o.authSetApiKey(n,f.trim()),m(null),g(""),await h()}catch(d){console.error("Failed to save API key:",d)}},C=async n=>{const o=j.getClient();if(o)try{await o.authLogout(n),await h()}catch(d){console.error("Failed to logout:",d)}};return e.jsxs("div",{className:"h-full overflow-y-auto chat-background",children:[e.jsxs("div",{className:"container max-w-2xl py-8 px-4",children:[e.jsxs(D,{className:"card-depth",children:[e.jsxs(P,{children:[e.jsx(F,{children:"AI Provider Authentication"}),e.jsx(Z,{children:"Configure authentication for AI providers (OpenAI, Anthropic, etc.)"})]}),e.jsx(L,{children:a?e.jsx("div",{className:"flex items-center justify-center py-8",children:e.jsx(y,{className:"h-6 w-6 animate-spin text-muted-foreground"})}):t.length===0?e.jsx("p",{className:"text-sm text-muted-foreground py-4",children:"No providers available. Make sure clankie is configured with at least one AI provider."}):e.jsx("div",{className:"space-y-3",children:t.map(n=>e.jsx(Ae,{provider:n,isEditing:i===n.id,apiKeyValue:f,onApiKeyChange:g,onLogin:()=>n.type==="oauth"?v(n.id):N(n.id),onSaveApiKey:()=>c(n.id),onCancelApiKey:()=>m(null),onLogout:()=>C(n.id)},n.id))})})]}),e.jsxs(D,{className:"mt-4 card-depth",children:[e.jsx(P,{children:e.jsx(F,{children:"About Provider Authentication"})}),e.jsxs(L,{className:"space-y-3 text-sm text-muted-foreground",children:[e.jsx("p",{children:"AI providers require authentication to access their APIs. You can authenticate using:"}),e.jsxs("ul",{className:"list-disc list-inside space-y-1",children:[e.jsxs("li",{children:[e.jsx("strong",{children:"OAuth"})," - Browser-based authentication flow for supported providers"]}),e.jsxs("li",{children:[e.jsx("strong",{children:"API Key"})," - Direct API key entry for providers that support it"]})]}),e.jsx("p",{className:"text-xs",children:"Your credentials are stored securely by clankie and are never shared with the web UI."})]})]})]}),e.jsx(ye,{open:l,onOpenChange:r})]})}function Ae({provider:t,isEditing:a,apiKeyValue:s,onApiKeyChange:l,onLogin:r,onSaveApiKey:i,onCancelApiKey:m,onLogout:f}){return e.jsx("div",{className:"rounded-lg border p-4",children:e.jsxs("div",{className:"flex items-start justify-between gap-4",children:[e.jsxs("div",{className:"flex-1",children:[e.jsxs("div",{className:"flex items-center gap-2 mb-1",children:[e.jsx("h4",{className:"font-medium",children:t.name}),e.jsx(se,{variant:t.type==="oauth"?"default":"secondary",className:"text-xs",children:t.type==="oauth"?e.jsxs(e.Fragment,{children:[e.jsx(_,{className:"h-3 w-3 mr-1"}),"OAuth"]}):e.jsxs(e.Fragment,{children:[e.jsx(G,{className:"h-3 w-3 mr-1"}),"API Key"]})}),t.hasAuth?e.jsx(ee,{className:"h-4 w-4 text-green-600"}):e.jsx(M,{className:"h-4 w-4 text-muted-foreground"})]}),e.jsx("p",{className:"text-xs text-muted-foreground",children:t.hasAuth?"Authenticated":"Not configured"}),a&&t.type==="apikey"&&e.jsxs("div",{className:"mt-3 space-y-2",children:[e.jsxs(I,{children:[e.jsx(z,{htmlFor:`api-key-${t.id}`,children:"API Key"}),e.jsx(k,{id:`api-key-${t.id}`,type:"password",placeholder:"Enter API key",value:s,onChange:g=>l(g.target.value),autoFocus:!0})]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx(x,{size:"sm",onClick:i,disabled:!s.trim(),children:"Save"}),e.jsx(x,{size:"sm",variant:"outline",onClick:m,children:"Cancel"})]})]})]}),!a&&e.jsx("div",{className:"flex gap-2",children:t.hasAuth?e.jsx(x,{size:"sm",variant:"outline",onClick:f,children:"Logout"}):e.jsx(x,{size:"sm",onClick:r,children:"Login"})})]})})}export{Pe as component};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{ac as i,ad as n,i as d,ae as s}from"./main-CKIteeQH.js";const o=s("h-5 gap-1 rounded-4xl border border-transparent 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 justify-center w-fit 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",{variants:{variant:{default:"bg-primary text-primary-foreground [a]:hover:bg-primary/80",secondary:"bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",destructive:"bg-destructive/10 [a]:hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 text-destructive dark:bg-destructive/20",outline:"border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",ghost:"hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",link:"text-primary underline-offset-4 hover:underline"}},defaultVariants:{variant:"default"}});function g({className:r,variant:e="default",render:t,...a}){return i({defaultTagName:"span",props:n({className:d(o({variant:e}),r)},a),render:t,state:{slot:"badge",variant:e}})}export{g as B};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{c}from"./main-CKIteeQH.js";const e=[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]],t=c("check",e);export{t as C};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{c}from"./main-CKIteeQH.js";const e=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m15 9-6 6",key:"1uzhvr"}],["path",{d:"m9 9 6 6",key:"z0biqf"}]],o=c("circle-x",e);export{o as C};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{u as S,q as w,r,j as e,h as d,x as h,y as x,H as y,k as u,Q as m,B as c,L as T,S as F,U as j,d as p}from"./main-CKIteeQH.js";import{F as k,a as g}from"./field-Dy_c96vc.js";function A(){const{settings:o,status:l}=S(w,t=>({settings:t.settings,status:t.status})),[a,b]=r.useState(o.url),[n,v]=r.useState(o.authToken),s=l==="connected",i=l==="connecting",C=()=>{j({url:a,authToken:n})},N=()=>{j({url:a,authToken:n}),p.connect()},f=()=>{p.disconnect()};return e.jsx("div",{className:"h-full overflow-y-auto chat-background",children:e.jsxs("div",{className:"container max-w-2xl py-8 px-4",children:[e.jsxs(d,{className:"card-depth",children:[e.jsxs(h,{children:[e.jsx(x,{children:"Connection Settings"}),e.jsx(y,{children:"Configure the WebSocket connection to your clankie instance"})]}),e.jsxs(u,{className:"space-y-4",children:[e.jsxs(k,{children:[e.jsx(g,{htmlFor:"ws-url",children:"WebSocket URL"}),e.jsx(m,{id:"ws-url",type:"text",placeholder:"ws://localhost:3100",value:a,onChange:t=>b(t.target.value),disabled:s})]}),e.jsxs(k,{children:[e.jsx(g,{htmlFor:"auth-token",children:"Auth Token"}),e.jsx(m,{id:"auth-token",type:"password",placeholder:"Enter your authentication token",value:n,onChange:t=>v(t.target.value),disabled:s}),e.jsxs("p",{className:"text-xs text-muted-foreground mt-1",children:["Set with:"," ",e.jsx("code",{className:"rounded bg-muted px-1 py-0.5",children:'clankie config set channels.web.authToken "your-token"'})]})]}),e.jsx("div",{className:"flex gap-2 pt-2",children:s?e.jsx(c,{variant:"destructive",onClick:f,children:"Disconnect"}):e.jsxs(e.Fragment,{children:[e.jsx(c,{onClick:N,disabled:i||!n,children:i?"Connecting...":"Connect"}),e.jsx(c,{variant:"outline",onClick:C,disabled:i,children:"Save"})]})}),!n&&e.jsxs("div",{className:"rounded-md border border-destructive/50 bg-destructive/10 p-3 text-sm text-destructive",children:[e.jsx("p",{className:"font-medium",children:"Auth token required"}),e.jsx("p",{className:"text-xs mt-1",children:"Configure the token in clankie and enter it above to connect."})]})]})]}),e.jsxs(d,{className:"mt-4 card-depth",children:[e.jsx(h,{children:e.jsx(x,{children:"Setup Instructions"})}),e.jsxs(u,{className:"space-y-3 text-sm",children:[e.jsxs("div",{children:[e.jsx("p",{className:"font-medium",children:"1. Enable the web channel in clankie"}),e.jsxs("code",{className:"block mt-1 rounded bg-muted p-2 text-xs",children:['clankie config set channels.web.authToken "your-secret-token"',e.jsx("br",{}),"clankie config set channels.web.port 3100"]})]}),e.jsxs("div",{children:[e.jsx("p",{className:"font-medium",children:"2. Start the clankie daemon"}),e.jsx("code",{className:"block mt-1 rounded bg-muted p-2 text-xs",children:"clankie start"})]}),e.jsxs("div",{children:[e.jsx("p",{className:"font-medium",children:"3. Enter the token above and connect"}),e.jsx("p",{className:"text-xs text-muted-foreground mt-1",children:"The web-ui will connect to ws://localhost:3100 by default"})]})]})]}),!s&&e.jsx("div",{className:"mt-4 text-center",children:e.jsx(T,{to:"/settings",children:e.jsxs(c,{variant:"outline",children:[e.jsx(F,{className:"mr-2 h-4 w-4"}),"Back to Settings"]})})})]})})}export{A as component};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{c as a,R as i}from"./main-CKIteeQH.js";const o=[["path",{d:"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5",key:"1gvzjb"}],["path",{d:"M9 18h6",key:"x1upvd"}],["path",{d:"M10 22h4",key:"ceow96"}]],c=a("lightbulb",o),l={extensions:[],extensionErrors:[],skills:[],skillDiagnostics:[],isLoading:!1,installStatus:{isInstalling:!1,output:"",exitCode:null}},e=new i(l);function r(t){e.setState(s=>({...s,isLoading:t}))}function S(t,s){e.setState(n=>({...n,extensions:t,extensionErrors:s,isLoading:!1}))}function d(t,s){e.setState(n=>({...n,skills:t,skillDiagnostics:s,isLoading:!1}))}function g(t){e.setState(s=>({...s,installStatus:{...s.installStatus,...t}}))}function f(){e.setState(t=>({...t,installStatus:{isInstalling:!1,output:"",exitCode:null,error:void 0}}))}export{c as L,d as a,S as b,g as c,e,f as r,r as s};
|