@nextclaw/server 0.4.11 → 0.4.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +45 -2
- package/dist/index.js +210 -8
- package/package.json +1 -1
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?: {
|
|
@@ -236,9 +268,20 @@ declare function buildConfigMeta(config: Config): ConfigMetaView;
|
|
|
236
268
|
declare function buildConfigSchemaView(_config: Config): ConfigSchemaResponse;
|
|
237
269
|
declare function executeConfigAction(configPath: string, actionId: string, request: ConfigActionExecuteRequest$1): Promise<ExecuteActionResult>;
|
|
238
270
|
declare function loadConfigOrDefault(configPath: string): Config;
|
|
239
|
-
declare function updateModel(configPath: string,
|
|
271
|
+
declare function updateModel(configPath: string, patch: {
|
|
272
|
+
model?: string;
|
|
273
|
+
maxTokens?: number;
|
|
274
|
+
}): ConfigView;
|
|
240
275
|
declare function updateProvider(configPath: string, providerName: string, patch: ProviderConfigUpdate): ProviderConfigView | null;
|
|
241
276
|
declare function updateChannel(configPath: string, channelName: string, patch: Record<string, unknown>): Record<string, unknown> | null;
|
|
277
|
+
declare function listSessions(configPath: string, query?: {
|
|
278
|
+
q?: string;
|
|
279
|
+
limit?: number;
|
|
280
|
+
activeMinutes?: number;
|
|
281
|
+
}): SessionsListView;
|
|
282
|
+
declare function getSessionHistory(configPath: string, key: string, limit?: number): SessionHistoryView | null;
|
|
283
|
+
declare function patchSession(configPath: string, key: string, patch: SessionPatchUpdate): SessionHistoryView | null;
|
|
284
|
+
declare function deleteSession(configPath: string, key: string): boolean;
|
|
242
285
|
declare function updateRuntime(configPath: string, patch: RuntimeConfigUpdate): Pick<ConfigView, "agents" | "bindings" | "session">;
|
|
243
286
|
|
|
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 };
|
|
287
|
+
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];
|
|
@@ -343,9 +345,14 @@ async function executeConfigAction(configPath, actionId, request) {
|
|
|
343
345
|
function loadConfigOrDefault(configPath) {
|
|
344
346
|
return loadConfig(configPath);
|
|
345
347
|
}
|
|
346
|
-
function updateModel(configPath,
|
|
348
|
+
function updateModel(configPath, patch) {
|
|
347
349
|
const config = loadConfigOrDefault(configPath);
|
|
348
|
-
|
|
350
|
+
if (typeof patch.model === "string") {
|
|
351
|
+
config.agents.defaults.model = patch.model;
|
|
352
|
+
}
|
|
353
|
+
if (typeof patch.maxTokens === "number" && Number.isFinite(patch.maxTokens)) {
|
|
354
|
+
config.agents.defaults.maxTokens = Math.max(1, Math.trunc(patch.maxTokens));
|
|
355
|
+
}
|
|
349
356
|
const next = ConfigSchema.parse(config);
|
|
350
357
|
saveConfig(next, configPath);
|
|
351
358
|
return buildConfigView(next);
|
|
@@ -391,6 +398,137 @@ function updateChannel(configPath, channelName, patch) {
|
|
|
391
398
|
uiHints
|
|
392
399
|
);
|
|
393
400
|
}
|
|
401
|
+
function normalizeSessionKey(value) {
|
|
402
|
+
return value.trim();
|
|
403
|
+
}
|
|
404
|
+
function createSessionManager(config) {
|
|
405
|
+
return new SessionManager(getWorkspacePathFromConfig(config));
|
|
406
|
+
}
|
|
407
|
+
function listSessions(configPath, query) {
|
|
408
|
+
const config = loadConfigOrDefault(configPath);
|
|
409
|
+
const sessionManager = createSessionManager(config);
|
|
410
|
+
const now = Date.now();
|
|
411
|
+
const activeMinutes = typeof query?.activeMinutes === "number" ? Math.max(0, Math.trunc(query.activeMinutes)) : 0;
|
|
412
|
+
const q = (query?.q ?? "").trim().toLowerCase();
|
|
413
|
+
const limit = typeof query?.limit === "number" ? Math.max(0, Math.trunc(query.limit)) : 0;
|
|
414
|
+
const entries = sessionManager.listSessions().map((item) => {
|
|
415
|
+
const key = typeof item.key === "string" ? normalizeSessionKey(item.key) : "";
|
|
416
|
+
if (!key) {
|
|
417
|
+
return null;
|
|
418
|
+
}
|
|
419
|
+
const session = sessionManager.getIfExists(key);
|
|
420
|
+
const messages = session?.messages ?? [];
|
|
421
|
+
const lastMessage = messages.length > 0 ? messages[messages.length - 1] : null;
|
|
422
|
+
const metadata = item.metadata && typeof item.metadata === "object" ? item.metadata : {};
|
|
423
|
+
const label = typeof metadata.label === "string" ? metadata.label.trim() : "";
|
|
424
|
+
const preferredModel = typeof metadata.preferred_model === "string" ? metadata.preferred_model.trim() : "";
|
|
425
|
+
const createdAt = typeof item.created_at === "string" ? item.created_at : (/* @__PURE__ */ new Date(0)).toISOString();
|
|
426
|
+
const updatedAt = typeof item.updated_at === "string" ? item.updated_at : createdAt;
|
|
427
|
+
return {
|
|
428
|
+
key,
|
|
429
|
+
createdAt,
|
|
430
|
+
updatedAt,
|
|
431
|
+
label: label || void 0,
|
|
432
|
+
preferredModel: preferredModel || void 0,
|
|
433
|
+
messageCount: messages.length,
|
|
434
|
+
lastRole: typeof lastMessage?.role === "string" ? lastMessage.role : void 0,
|
|
435
|
+
lastTimestamp: typeof lastMessage?.timestamp === "string" ? lastMessage.timestamp : void 0
|
|
436
|
+
};
|
|
437
|
+
}).filter((item) => Boolean(item));
|
|
438
|
+
const filtered = entries.filter((entry) => {
|
|
439
|
+
if (activeMinutes > 0) {
|
|
440
|
+
const ageMs = now - new Date(entry.updatedAt).getTime();
|
|
441
|
+
if (!Number.isFinite(ageMs) || ageMs > activeMinutes * 6e4) {
|
|
442
|
+
return false;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
if (!q) {
|
|
446
|
+
return true;
|
|
447
|
+
}
|
|
448
|
+
return entry.key.toLowerCase().includes(q) || (entry.label ?? "").toLowerCase().includes(q);
|
|
449
|
+
});
|
|
450
|
+
filtered.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
|
|
451
|
+
const total = filtered.length;
|
|
452
|
+
const sessions = limit > 0 ? filtered.slice(0, limit) : filtered;
|
|
453
|
+
return { sessions, total };
|
|
454
|
+
}
|
|
455
|
+
function getSessionHistory(configPath, key, limit) {
|
|
456
|
+
const normalizedKey = normalizeSessionKey(key);
|
|
457
|
+
if (!normalizedKey) {
|
|
458
|
+
return null;
|
|
459
|
+
}
|
|
460
|
+
const config = loadConfigOrDefault(configPath);
|
|
461
|
+
const sessionManager = createSessionManager(config);
|
|
462
|
+
const session = sessionManager.getIfExists(normalizedKey);
|
|
463
|
+
if (!session) {
|
|
464
|
+
return null;
|
|
465
|
+
}
|
|
466
|
+
const safeLimit = typeof limit === "number" ? Math.min(500, Math.max(1, Math.trunc(limit))) : 200;
|
|
467
|
+
const allMessages = session.messages;
|
|
468
|
+
const messages = allMessages.length > safeLimit ? allMessages.slice(-safeLimit) : allMessages;
|
|
469
|
+
return {
|
|
470
|
+
key: normalizedKey,
|
|
471
|
+
totalMessages: allMessages.length,
|
|
472
|
+
metadata: session.metadata,
|
|
473
|
+
messages: messages.map((message) => {
|
|
474
|
+
const entry = {
|
|
475
|
+
role: message.role,
|
|
476
|
+
content: message.content,
|
|
477
|
+
timestamp: message.timestamp
|
|
478
|
+
};
|
|
479
|
+
if (typeof message.name === "string") {
|
|
480
|
+
entry.name = message.name;
|
|
481
|
+
}
|
|
482
|
+
if (typeof message.tool_call_id === "string") {
|
|
483
|
+
entry.tool_call_id = message.tool_call_id;
|
|
484
|
+
}
|
|
485
|
+
return entry;
|
|
486
|
+
})
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
function patchSession(configPath, key, patch) {
|
|
490
|
+
const normalizedKey = normalizeSessionKey(key);
|
|
491
|
+
if (!normalizedKey) {
|
|
492
|
+
return null;
|
|
493
|
+
}
|
|
494
|
+
const config = loadConfigOrDefault(configPath);
|
|
495
|
+
const sessionManager = createSessionManager(config);
|
|
496
|
+
const session = sessionManager.getIfExists(normalizedKey);
|
|
497
|
+
if (!session) {
|
|
498
|
+
return null;
|
|
499
|
+
}
|
|
500
|
+
if (patch.clearHistory) {
|
|
501
|
+
sessionManager.clear(session);
|
|
502
|
+
}
|
|
503
|
+
if (Object.prototype.hasOwnProperty.call(patch, "label")) {
|
|
504
|
+
const label = typeof patch.label === "string" ? patch.label.trim() : "";
|
|
505
|
+
if (label) {
|
|
506
|
+
session.metadata.label = label;
|
|
507
|
+
} else {
|
|
508
|
+
delete session.metadata.label;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
if (Object.prototype.hasOwnProperty.call(patch, "preferredModel")) {
|
|
512
|
+
const preferredModel = typeof patch.preferredModel === "string" ? patch.preferredModel.trim() : "";
|
|
513
|
+
if (preferredModel) {
|
|
514
|
+
session.metadata.preferred_model = preferredModel;
|
|
515
|
+
} else {
|
|
516
|
+
delete session.metadata.preferred_model;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
session.updatedAt = /* @__PURE__ */ new Date();
|
|
520
|
+
sessionManager.save(session);
|
|
521
|
+
return getSessionHistory(configPath, normalizedKey, 200);
|
|
522
|
+
}
|
|
523
|
+
function deleteSession(configPath, key) {
|
|
524
|
+
const normalizedKey = normalizeSessionKey(key);
|
|
525
|
+
if (!normalizedKey) {
|
|
526
|
+
return false;
|
|
527
|
+
}
|
|
528
|
+
const config = loadConfigOrDefault(configPath);
|
|
529
|
+
const sessionManager = createSessionManager(config);
|
|
530
|
+
return sessionManager.delete(normalizedKey);
|
|
531
|
+
}
|
|
394
532
|
function updateRuntime(configPath, patch) {
|
|
395
533
|
const config = loadConfigOrDefault(configPath);
|
|
396
534
|
if (patch.agents?.defaults && Object.prototype.hasOwnProperty.call(patch.agents.defaults, "contextTokens")) {
|
|
@@ -462,12 +600,28 @@ function createUiRouter(options) {
|
|
|
462
600
|
});
|
|
463
601
|
app.put("/api/config/model", async (c) => {
|
|
464
602
|
const body = await readJson(c.req.raw);
|
|
465
|
-
if (!body.ok
|
|
466
|
-
return c.json(err("INVALID_BODY", "
|
|
603
|
+
if (!body.ok) {
|
|
604
|
+
return c.json(err("INVALID_BODY", "invalid json body"), 400);
|
|
605
|
+
}
|
|
606
|
+
const hasModel = typeof body.data.model === "string";
|
|
607
|
+
const hasMaxTokens = typeof body.data.maxTokens === "number";
|
|
608
|
+
if (!hasModel && !hasMaxTokens) {
|
|
609
|
+
return c.json(err("INVALID_BODY", "model or maxTokens is required"), 400);
|
|
610
|
+
}
|
|
611
|
+
const view = updateModel(options.configPath, {
|
|
612
|
+
model: hasModel ? body.data.model : void 0,
|
|
613
|
+
maxTokens: hasMaxTokens ? body.data.maxTokens : void 0
|
|
614
|
+
});
|
|
615
|
+
if (hasModel) {
|
|
616
|
+
options.publish({ type: "config.updated", payload: { path: "agents.defaults.model" } });
|
|
617
|
+
}
|
|
618
|
+
if (hasMaxTokens) {
|
|
619
|
+
options.publish({ type: "config.updated", payload: { path: "agents.defaults.maxTokens" } });
|
|
467
620
|
}
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
621
|
+
return c.json(ok({
|
|
622
|
+
model: view.agents.defaults.model,
|
|
623
|
+
maxTokens: view.agents.defaults.maxTokens
|
|
624
|
+
}));
|
|
471
625
|
});
|
|
472
626
|
app.put("/api/config/providers/:provider", async (c) => {
|
|
473
627
|
const provider = c.req.param("provider");
|
|
@@ -495,6 +649,50 @@ function createUiRouter(options) {
|
|
|
495
649
|
options.publish({ type: "config.updated", payload: { path: `channels.${channel}` } });
|
|
496
650
|
return c.json(ok(result));
|
|
497
651
|
});
|
|
652
|
+
app.get("/api/sessions", (c) => {
|
|
653
|
+
const query = c.req.query();
|
|
654
|
+
const q = typeof query.q === "string" ? query.q : void 0;
|
|
655
|
+
const limit = typeof query.limit === "string" ? Number.parseInt(query.limit, 10) : void 0;
|
|
656
|
+
const activeMinutes = typeof query.activeMinutes === "string" ? Number.parseInt(query.activeMinutes, 10) : void 0;
|
|
657
|
+
const data = listSessions(options.configPath, {
|
|
658
|
+
q,
|
|
659
|
+
limit: Number.isFinite(limit) ? limit : void 0,
|
|
660
|
+
activeMinutes: Number.isFinite(activeMinutes) ? activeMinutes : void 0
|
|
661
|
+
});
|
|
662
|
+
return c.json(ok(data));
|
|
663
|
+
});
|
|
664
|
+
app.get("/api/sessions/:key/history", (c) => {
|
|
665
|
+
const key = decodeURIComponent(c.req.param("key"));
|
|
666
|
+
const query = c.req.query();
|
|
667
|
+
const limit = typeof query.limit === "string" ? Number.parseInt(query.limit, 10) : void 0;
|
|
668
|
+
const data = getSessionHistory(options.configPath, key, Number.isFinite(limit) ? limit : void 0);
|
|
669
|
+
if (!data) {
|
|
670
|
+
return c.json(err("NOT_FOUND", `session not found: ${key}`), 404);
|
|
671
|
+
}
|
|
672
|
+
return c.json(ok(data));
|
|
673
|
+
});
|
|
674
|
+
app.put("/api/sessions/:key", async (c) => {
|
|
675
|
+
const key = decodeURIComponent(c.req.param("key"));
|
|
676
|
+
const body = await readJson(c.req.raw);
|
|
677
|
+
if (!body.ok || !body.data || typeof body.data !== "object") {
|
|
678
|
+
return c.json(err("INVALID_BODY", "invalid json body"), 400);
|
|
679
|
+
}
|
|
680
|
+
const data = patchSession(options.configPath, key, body.data);
|
|
681
|
+
if (!data) {
|
|
682
|
+
return c.json(err("NOT_FOUND", `session not found: ${key}`), 404);
|
|
683
|
+
}
|
|
684
|
+
options.publish({ type: "config.updated", payload: { path: "session" } });
|
|
685
|
+
return c.json(ok(data));
|
|
686
|
+
});
|
|
687
|
+
app.delete("/api/sessions/:key", (c) => {
|
|
688
|
+
const key = decodeURIComponent(c.req.param("key"));
|
|
689
|
+
const deleted = deleteSession(options.configPath, key);
|
|
690
|
+
if (!deleted) {
|
|
691
|
+
return c.json(err("NOT_FOUND", `session not found: ${key}`), 404);
|
|
692
|
+
}
|
|
693
|
+
options.publish({ type: "config.updated", payload: { path: "session" } });
|
|
694
|
+
return c.json(ok({ deleted: true }));
|
|
695
|
+
});
|
|
498
696
|
app.put("/api/config/runtime", async (c) => {
|
|
499
697
|
const body = await readJson(c.req.raw);
|
|
500
698
|
if (!body.ok || !body.data || typeof body.data !== "object") {
|
|
@@ -616,8 +814,12 @@ export {
|
|
|
616
814
|
buildConfigSchemaView,
|
|
617
815
|
buildConfigView,
|
|
618
816
|
createUiRouter,
|
|
817
|
+
deleteSession,
|
|
619
818
|
executeConfigAction,
|
|
819
|
+
getSessionHistory,
|
|
820
|
+
listSessions,
|
|
620
821
|
loadConfigOrDefault,
|
|
822
|
+
patchSession,
|
|
621
823
|
startUiServer,
|
|
622
824
|
updateChannel,
|
|
623
825
|
updateModel,
|