@nextclaw/server 0.4.11 → 0.4.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/index.d.ts CHANGED
@@ -53,6 +53,38 @@ type SessionConfigView = {
53
53
  maxPingPongTurns?: number;
54
54
  };
55
55
  };
56
+ type SessionEntryView = {
57
+ key: string;
58
+ createdAt: string;
59
+ updatedAt: string;
60
+ label?: string;
61
+ preferredModel?: string;
62
+ messageCount: number;
63
+ lastRole?: string;
64
+ lastTimestamp?: string;
65
+ };
66
+ type SessionsListView = {
67
+ sessions: SessionEntryView[];
68
+ total: number;
69
+ };
70
+ type SessionMessageView = {
71
+ role: string;
72
+ content: string;
73
+ timestamp: string;
74
+ name?: string;
75
+ tool_call_id?: string;
76
+ };
77
+ type SessionHistoryView = {
78
+ key: string;
79
+ totalMessages: number;
80
+ metadata: Record<string, unknown>;
81
+ messages: SessionMessageView[];
82
+ };
83
+ type SessionPatchUpdate = {
84
+ label?: string | null;
85
+ preferredModel?: string | null;
86
+ clearHistory?: boolean;
87
+ };
56
88
  type RuntimeConfigUpdate = {
57
89
  agents?: {
58
90
  defaults?: {
@@ -239,6 +271,14 @@ declare function loadConfigOrDefault(configPath: string): Config;
239
271
  declare function updateModel(configPath: string, model: string): ConfigView;
240
272
  declare function updateProvider(configPath: string, providerName: string, patch: ProviderConfigUpdate): ProviderConfigView | null;
241
273
  declare function updateChannel(configPath: string, channelName: string, patch: Record<string, unknown>): Record<string, unknown> | null;
274
+ declare function listSessions(configPath: string, query?: {
275
+ q?: string;
276
+ limit?: number;
277
+ activeMinutes?: number;
278
+ }): SessionsListView;
279
+ declare function getSessionHistory(configPath: string, key: string, limit?: number): SessionHistoryView | null;
280
+ declare function patchSession(configPath: string, key: string, patch: SessionPatchUpdate): SessionHistoryView | null;
281
+ declare function deleteSession(configPath: string, key: string): boolean;
242
282
  declare function updateRuntime(configPath: string, patch: RuntimeConfigUpdate): Pick<ConfigView, "agents" | "bindings" | "session">;
243
283
 
244
- export { type AgentBindingView, type AgentProfileView, type ApiError, type ApiResponse, type BindingPeerView, type ChannelSpecView, type ConfigActionExecuteRequest, type ConfigActionExecuteResult, type ConfigActionManifest, type ConfigActionType, type ConfigMetaView, type ConfigSchemaResponse, type ConfigUiHint, type ConfigUiHints, type ConfigView, type ProviderConfigUpdate, type ProviderConfigView, type ProviderSpecView, type RuntimeConfigUpdate, type SessionConfigView, type UiServerEvent, type UiServerHandle, type UiServerOptions, buildConfigMeta, buildConfigSchemaView, buildConfigView, createUiRouter, executeConfigAction, loadConfigOrDefault, startUiServer, updateChannel, updateModel, updateProvider, updateRuntime };
284
+ export { type AgentBindingView, type AgentProfileView, type ApiError, type ApiResponse, type BindingPeerView, type ChannelSpecView, type ConfigActionExecuteRequest, type ConfigActionExecuteResult, type ConfigActionManifest, type ConfigActionType, type ConfigMetaView, type ConfigSchemaResponse, type ConfigUiHint, type ConfigUiHints, type ConfigView, type ProviderConfigUpdate, type ProviderConfigView, type ProviderSpecView, type RuntimeConfigUpdate, type SessionConfigView, type SessionEntryView, type SessionHistoryView, type SessionMessageView, type SessionPatchUpdate, type SessionsListView, type UiServerEvent, type UiServerHandle, type UiServerOptions, buildConfigMeta, buildConfigSchemaView, buildConfigView, createUiRouter, deleteSession, executeConfigAction, getSessionHistory, listSessions, loadConfigOrDefault, patchSession, startUiServer, updateChannel, updateModel, updateProvider, updateRuntime };
package/dist/index.js CHANGED
@@ -20,7 +20,9 @@ import {
20
20
  buildConfigSchema,
21
21
  findProviderByName,
22
22
  getPackageVersion,
23
- isSensitiveConfigPath
23
+ isSensitiveConfigPath,
24
+ SessionManager,
25
+ getWorkspacePathFromConfig
24
26
  } from "@nextclaw/core";
25
27
  var MASK_MIN_LENGTH = 8;
26
28
  var EXTRA_SENSITIVE_PATH_PATTERNS = [/authorization/i, /cookie/i, /session/i, /bearer/i];
@@ -391,6 +393,137 @@ function updateChannel(configPath, channelName, patch) {
391
393
  uiHints
392
394
  );
393
395
  }
396
+ function normalizeSessionKey(value) {
397
+ return value.trim();
398
+ }
399
+ function createSessionManager(config) {
400
+ return new SessionManager(getWorkspacePathFromConfig(config));
401
+ }
402
+ function listSessions(configPath, query) {
403
+ const config = loadConfigOrDefault(configPath);
404
+ const sessionManager = createSessionManager(config);
405
+ const now = Date.now();
406
+ const activeMinutes = typeof query?.activeMinutes === "number" ? Math.max(0, Math.trunc(query.activeMinutes)) : 0;
407
+ const q = (query?.q ?? "").trim().toLowerCase();
408
+ const limit = typeof query?.limit === "number" ? Math.max(0, Math.trunc(query.limit)) : 0;
409
+ const entries = sessionManager.listSessions().map((item) => {
410
+ const key = typeof item.key === "string" ? normalizeSessionKey(item.key) : "";
411
+ if (!key) {
412
+ return null;
413
+ }
414
+ const session = sessionManager.getIfExists(key);
415
+ const messages = session?.messages ?? [];
416
+ const lastMessage = messages.length > 0 ? messages[messages.length - 1] : null;
417
+ const metadata = item.metadata && typeof item.metadata === "object" ? item.metadata : {};
418
+ const label = typeof metadata.label === "string" ? metadata.label.trim() : "";
419
+ const preferredModel = typeof metadata.preferred_model === "string" ? metadata.preferred_model.trim() : "";
420
+ const createdAt = typeof item.created_at === "string" ? item.created_at : (/* @__PURE__ */ new Date(0)).toISOString();
421
+ const updatedAt = typeof item.updated_at === "string" ? item.updated_at : createdAt;
422
+ return {
423
+ key,
424
+ createdAt,
425
+ updatedAt,
426
+ label: label || void 0,
427
+ preferredModel: preferredModel || void 0,
428
+ messageCount: messages.length,
429
+ lastRole: typeof lastMessage?.role === "string" ? lastMessage.role : void 0,
430
+ lastTimestamp: typeof lastMessage?.timestamp === "string" ? lastMessage.timestamp : void 0
431
+ };
432
+ }).filter((item) => Boolean(item));
433
+ const filtered = entries.filter((entry) => {
434
+ if (activeMinutes > 0) {
435
+ const ageMs = now - new Date(entry.updatedAt).getTime();
436
+ if (!Number.isFinite(ageMs) || ageMs > activeMinutes * 6e4) {
437
+ return false;
438
+ }
439
+ }
440
+ if (!q) {
441
+ return true;
442
+ }
443
+ return entry.key.toLowerCase().includes(q) || (entry.label ?? "").toLowerCase().includes(q);
444
+ });
445
+ filtered.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
446
+ const total = filtered.length;
447
+ const sessions = limit > 0 ? filtered.slice(0, limit) : filtered;
448
+ return { sessions, total };
449
+ }
450
+ function getSessionHistory(configPath, key, limit) {
451
+ const normalizedKey = normalizeSessionKey(key);
452
+ if (!normalizedKey) {
453
+ return null;
454
+ }
455
+ const config = loadConfigOrDefault(configPath);
456
+ const sessionManager = createSessionManager(config);
457
+ const session = sessionManager.getIfExists(normalizedKey);
458
+ if (!session) {
459
+ return null;
460
+ }
461
+ const safeLimit = typeof limit === "number" ? Math.min(500, Math.max(1, Math.trunc(limit))) : 200;
462
+ const allMessages = session.messages;
463
+ const messages = allMessages.length > safeLimit ? allMessages.slice(-safeLimit) : allMessages;
464
+ return {
465
+ key: normalizedKey,
466
+ totalMessages: allMessages.length,
467
+ metadata: session.metadata,
468
+ messages: messages.map((message) => {
469
+ const entry = {
470
+ role: message.role,
471
+ content: message.content,
472
+ timestamp: message.timestamp
473
+ };
474
+ if (typeof message.name === "string") {
475
+ entry.name = message.name;
476
+ }
477
+ if (typeof message.tool_call_id === "string") {
478
+ entry.tool_call_id = message.tool_call_id;
479
+ }
480
+ return entry;
481
+ })
482
+ };
483
+ }
484
+ function patchSession(configPath, key, patch) {
485
+ const normalizedKey = normalizeSessionKey(key);
486
+ if (!normalizedKey) {
487
+ return null;
488
+ }
489
+ const config = loadConfigOrDefault(configPath);
490
+ const sessionManager = createSessionManager(config);
491
+ const session = sessionManager.getIfExists(normalizedKey);
492
+ if (!session) {
493
+ return null;
494
+ }
495
+ if (patch.clearHistory) {
496
+ sessionManager.clear(session);
497
+ }
498
+ if (Object.prototype.hasOwnProperty.call(patch, "label")) {
499
+ const label = typeof patch.label === "string" ? patch.label.trim() : "";
500
+ if (label) {
501
+ session.metadata.label = label;
502
+ } else {
503
+ delete session.metadata.label;
504
+ }
505
+ }
506
+ if (Object.prototype.hasOwnProperty.call(patch, "preferredModel")) {
507
+ const preferredModel = typeof patch.preferredModel === "string" ? patch.preferredModel.trim() : "";
508
+ if (preferredModel) {
509
+ session.metadata.preferred_model = preferredModel;
510
+ } else {
511
+ delete session.metadata.preferred_model;
512
+ }
513
+ }
514
+ session.updatedAt = /* @__PURE__ */ new Date();
515
+ sessionManager.save(session);
516
+ return getSessionHistory(configPath, normalizedKey, 200);
517
+ }
518
+ function deleteSession(configPath, key) {
519
+ const normalizedKey = normalizeSessionKey(key);
520
+ if (!normalizedKey) {
521
+ return false;
522
+ }
523
+ const config = loadConfigOrDefault(configPath);
524
+ const sessionManager = createSessionManager(config);
525
+ return sessionManager.delete(normalizedKey);
526
+ }
394
527
  function updateRuntime(configPath, patch) {
395
528
  const config = loadConfigOrDefault(configPath);
396
529
  if (patch.agents?.defaults && Object.prototype.hasOwnProperty.call(patch.agents.defaults, "contextTokens")) {
@@ -495,6 +628,50 @@ function createUiRouter(options) {
495
628
  options.publish({ type: "config.updated", payload: { path: `channels.${channel}` } });
496
629
  return c.json(ok(result));
497
630
  });
631
+ app.get("/api/sessions", (c) => {
632
+ const query = c.req.query();
633
+ const q = typeof query.q === "string" ? query.q : void 0;
634
+ const limit = typeof query.limit === "string" ? Number.parseInt(query.limit, 10) : void 0;
635
+ const activeMinutes = typeof query.activeMinutes === "string" ? Number.parseInt(query.activeMinutes, 10) : void 0;
636
+ const data = listSessions(options.configPath, {
637
+ q,
638
+ limit: Number.isFinite(limit) ? limit : void 0,
639
+ activeMinutes: Number.isFinite(activeMinutes) ? activeMinutes : void 0
640
+ });
641
+ return c.json(ok(data));
642
+ });
643
+ app.get("/api/sessions/:key/history", (c) => {
644
+ const key = decodeURIComponent(c.req.param("key"));
645
+ const query = c.req.query();
646
+ const limit = typeof query.limit === "string" ? Number.parseInt(query.limit, 10) : void 0;
647
+ const data = getSessionHistory(options.configPath, key, Number.isFinite(limit) ? limit : void 0);
648
+ if (!data) {
649
+ return c.json(err("NOT_FOUND", `session not found: ${key}`), 404);
650
+ }
651
+ return c.json(ok(data));
652
+ });
653
+ app.put("/api/sessions/:key", async (c) => {
654
+ const key = decodeURIComponent(c.req.param("key"));
655
+ const body = await readJson(c.req.raw);
656
+ if (!body.ok || !body.data || typeof body.data !== "object") {
657
+ return c.json(err("INVALID_BODY", "invalid json body"), 400);
658
+ }
659
+ const data = patchSession(options.configPath, key, body.data);
660
+ if (!data) {
661
+ return c.json(err("NOT_FOUND", `session not found: ${key}`), 404);
662
+ }
663
+ options.publish({ type: "config.updated", payload: { path: "session" } });
664
+ return c.json(ok(data));
665
+ });
666
+ app.delete("/api/sessions/:key", (c) => {
667
+ const key = decodeURIComponent(c.req.param("key"));
668
+ const deleted = deleteSession(options.configPath, key);
669
+ if (!deleted) {
670
+ return c.json(err("NOT_FOUND", `session not found: ${key}`), 404);
671
+ }
672
+ options.publish({ type: "config.updated", payload: { path: "session" } });
673
+ return c.json(ok({ deleted: true }));
674
+ });
498
675
  app.put("/api/config/runtime", async (c) => {
499
676
  const body = await readJson(c.req.raw);
500
677
  if (!body.ok || !body.data || typeof body.data !== "object") {
@@ -616,8 +793,12 @@ export {
616
793
  buildConfigSchemaView,
617
794
  buildConfigView,
618
795
  createUiRouter,
796
+ deleteSession,
619
797
  executeConfigAction,
798
+ getSessionHistory,
799
+ listSessions,
620
800
  loadConfigOrDefault,
801
+ patchSession,
621
802
  startUiServer,
622
803
  updateChannel,
623
804
  updateModel,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/server",
3
- "version": "0.4.11",
3
+ "version": "0.4.12",
4
4
  "private": false,
5
5
  "description": "Nextclaw UI/API server.",
6
6
  "type": "module",