@nextclaw/server 0.5.3 → 0.5.6

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
@@ -1,5 +1,6 @@
1
+ import * as NextclawCore from '@nextclaw/core';
2
+ import { CronService, Config, ConfigActionExecuteRequest as ConfigActionExecuteRequest$1, ConfigActionExecuteResult as ConfigActionExecuteResult$1 } from '@nextclaw/core';
1
3
  import { Hono } from 'hono';
2
- import { Config, ConfigActionExecuteRequest as ConfigActionExecuteRequest$1, ConfigActionExecuteResult as ConfigActionExecuteResult$1 } from '@nextclaw/core';
3
4
 
4
5
  type ApiError = {
5
6
  code: string;
@@ -85,6 +86,55 @@ type SessionPatchUpdate = {
85
86
  preferredModel?: string | null;
86
87
  clearHistory?: boolean;
87
88
  };
89
+ type CronScheduleView = {
90
+ kind: "at";
91
+ atMs?: number | null;
92
+ } | {
93
+ kind: "every";
94
+ everyMs?: number | null;
95
+ } | {
96
+ kind: "cron";
97
+ expr?: string | null;
98
+ tz?: string | null;
99
+ };
100
+ type CronPayloadView = {
101
+ kind?: "system_event" | "agent_turn";
102
+ message: string;
103
+ deliver?: boolean;
104
+ channel?: string | null;
105
+ to?: string | null;
106
+ };
107
+ type CronJobStateView = {
108
+ nextRunAt?: string | null;
109
+ lastRunAt?: string | null;
110
+ lastStatus?: "ok" | "error" | "skipped" | null;
111
+ lastError?: string | null;
112
+ };
113
+ type CronJobView = {
114
+ id: string;
115
+ name: string;
116
+ enabled: boolean;
117
+ schedule: CronScheduleView;
118
+ payload: CronPayloadView;
119
+ state: CronJobStateView;
120
+ createdAt: string;
121
+ updatedAt: string;
122
+ deleteAfterRun: boolean;
123
+ };
124
+ type CronListView = {
125
+ jobs: CronJobView[];
126
+ total: number;
127
+ };
128
+ type CronEnableRequest = {
129
+ enabled: boolean;
130
+ };
131
+ type CronRunRequest = {
132
+ force?: boolean;
133
+ };
134
+ type CronActionResult = {
135
+ job: CronJobView | null;
136
+ executed?: boolean;
137
+ };
88
138
  type RuntimeConfigUpdate = {
89
139
  agents?: {
90
140
  defaults?: {
@@ -363,6 +413,7 @@ type UiServerOptions = {
363
413
  corsOrigins?: string[] | "*";
364
414
  staticDir?: string;
365
415
  marketplace?: MarketplaceApiConfig;
416
+ cronService?: CronService;
366
417
  };
367
418
  type UiServerHandle = {
368
419
  host: string;
@@ -377,6 +428,7 @@ type UiRouterOptions = {
377
428
  configPath: string;
378
429
  publish: (event: UiServerEvent) => void;
379
430
  marketplace?: MarketplaceApiConfig;
431
+ cronService?: InstanceType<typeof NextclawCore.CronService>;
380
432
  };
381
433
  declare function createUiRouter(options: UiRouterOptions): Hono;
382
434
 
@@ -410,4 +462,4 @@ declare function patchSession(configPath: string, key: string, patch: SessionPat
410
462
  declare function deleteSession(configPath: string, key: string): boolean;
411
463
  declare function updateRuntime(configPath: string, patch: RuntimeConfigUpdate): Pick<ConfigView, "agents" | "bindings" | "session">;
412
464
 
413
- 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 MarketplaceApiConfig, type MarketplaceInstallKind, type MarketplaceInstallRequest, type MarketplaceInstallResult, type MarketplaceInstallSkillParams, type MarketplaceInstallSpec, type MarketplaceInstalledRecord, type MarketplaceInstalledView, type MarketplaceInstaller, type MarketplaceItemSummary, type MarketplaceItemType, type MarketplaceItemView, type MarketplaceListView, type MarketplaceManageAction, type MarketplaceManageRequest, type MarketplaceManageResult, type MarketplaceRecommendationView, type MarketplaceSort, 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 };
465
+ 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 CronActionResult, type CronEnableRequest, type CronJobStateView, type CronJobView, type CronListView, type CronPayloadView, type CronRunRequest, type CronScheduleView, type MarketplaceApiConfig, type MarketplaceInstallKind, type MarketplaceInstallRequest, type MarketplaceInstallResult, type MarketplaceInstallSkillParams, type MarketplaceInstallSpec, type MarketplaceInstalledRecord, type MarketplaceInstalledView, type MarketplaceInstaller, type MarketplaceItemSummary, type MarketplaceItemType, type MarketplaceItemView, type MarketplaceListView, type MarketplaceManageAction, type MarketplaceManageRequest, type MarketplaceManageResult, type MarketplaceRecommendationView, type MarketplaceSort, 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
@@ -691,6 +691,38 @@ function ok(data) {
691
691
  function err(code, message, details) {
692
692
  return { ok: false, error: { code, message, details } };
693
693
  }
694
+ function toIsoTime(value) {
695
+ if (typeof value !== "number" || !Number.isFinite(value)) {
696
+ return null;
697
+ }
698
+ const date = new Date(value);
699
+ if (Number.isNaN(date.getTime())) {
700
+ return null;
701
+ }
702
+ return date.toISOString();
703
+ }
704
+ function buildCronJobView(job) {
705
+ return {
706
+ id: job.id,
707
+ name: job.name,
708
+ enabled: job.enabled,
709
+ schedule: job.schedule,
710
+ payload: job.payload,
711
+ state: {
712
+ nextRunAt: toIsoTime(job.state.nextRunAtMs),
713
+ lastRunAt: toIsoTime(job.state.lastRunAtMs),
714
+ lastStatus: job.state.lastStatus ?? null,
715
+ lastError: job.state.lastError ?? null
716
+ },
717
+ createdAt: new Date(job.createdAtMs).toISOString(),
718
+ updatedAt: new Date(job.updatedAtMs).toISOString(),
719
+ deleteAfterRun: job.deleteAfterRun
720
+ };
721
+ }
722
+ function findCronJob(service, id) {
723
+ const jobs = service.listJobs(true);
724
+ return jobs.find((job) => job.id === id) ?? null;
725
+ }
694
726
  async function readJson(req) {
695
727
  try {
696
728
  const data = await req.json();
@@ -899,12 +931,13 @@ function collectMarketplaceInstalledView(options) {
899
931
  records
900
932
  };
901
933
  }
902
- function resolvePluginManageTargetId(options, rawTargetId) {
934
+ function resolvePluginManageTargetId(options, rawTargetId, rawSpec) {
903
935
  const targetId = rawTargetId.trim();
904
- if (!targetId) {
936
+ if (!targetId && !rawSpec) {
905
937
  return rawTargetId;
906
938
  }
907
- const normalizedTarget = normalizePluginNpmSpec(targetId).toLowerCase();
939
+ const normalizedTarget = targetId ? normalizePluginNpmSpec(targetId).toLowerCase() : "";
940
+ const normalizedSpec = rawSpec ? normalizePluginNpmSpec(rawSpec).toLowerCase() : "";
908
941
  const installed = collectMarketplaceInstalledView(options);
909
942
  const pluginRecords = installed.records.filter((record) => record.type === "plugin");
910
943
  const lowerTargetId = targetId.toLowerCase();
@@ -914,13 +947,23 @@ function resolvePluginManageTargetId(options, rawTargetId) {
914
947
  return recordId;
915
948
  }
916
949
  }
917
- for (const record of pluginRecords) {
918
- const normalizedSpec = normalizePluginNpmSpec(record.spec).toLowerCase();
919
- if (normalizedSpec === normalizedTarget && record.id && record.id.trim().length > 0) {
920
- return record.id;
950
+ if (normalizedTarget) {
951
+ for (const record of pluginRecords) {
952
+ const normalizedRecordSpec = normalizePluginNpmSpec(record.spec).toLowerCase();
953
+ if (normalizedRecordSpec === normalizedTarget && record.id && record.id.trim().length > 0) {
954
+ return record.id;
955
+ }
921
956
  }
922
957
  }
923
- return targetId;
958
+ if (normalizedSpec && normalizedSpec !== normalizedTarget) {
959
+ for (const record of pluginRecords) {
960
+ const normalizedRecordSpec = normalizePluginNpmSpec(record.spec).toLowerCase();
961
+ if (normalizedRecordSpec === normalizedSpec && record.id && record.id.trim().length > 0) {
962
+ return record.id;
963
+ }
964
+ }
965
+ }
966
+ return targetId || rawSpec || rawTargetId;
924
967
  }
925
968
  function sanitizeMarketplaceItem(item) {
926
969
  const next = { ...item };
@@ -1022,7 +1065,8 @@ async function manageMarketplaceItem(params) {
1022
1065
  const type = params.body.type;
1023
1066
  const action = params.body.action;
1024
1067
  const requestedTargetId = typeof params.body.id === "string" && params.body.id.trim().length > 0 ? params.body.id.trim() : typeof params.body.spec === "string" && params.body.spec.trim().length > 0 ? params.body.spec.trim() : "";
1025
- const targetId = type === "plugin" ? resolvePluginManageTargetId(params.options, requestedTargetId) : requestedTargetId;
1068
+ const rawSpec = typeof params.body.spec === "string" ? params.body.spec.trim() : "";
1069
+ const targetId = type === "plugin" ? resolvePluginManageTargetId(params.options, requestedTargetId, rawSpec) : requestedTargetId;
1026
1070
  if (type !== "plugin" && type !== "skill" || action !== "enable" && action !== "disable" && action !== "uninstall" || !targetId) {
1027
1071
  throw new Error("INVALID_BODY:type, action and non-empty id/spec are required");
1028
1072
  }
@@ -1294,6 +1338,66 @@ function createUiRouter(options) {
1294
1338
  options.publish({ type: "config.updated", payload: { path: "session" } });
1295
1339
  return c.json(ok({ deleted: true }));
1296
1340
  });
1341
+ app.get("/api/cron", (c) => {
1342
+ if (!options.cronService) {
1343
+ return c.json(err("NOT_AVAILABLE", "cron service unavailable"), 503);
1344
+ }
1345
+ const query = c.req.query();
1346
+ const includeDisabled = query.all === "1" || query.all === "true" || query.all === "yes";
1347
+ const jobs = options.cronService.listJobs(includeDisabled).map((job) => buildCronJobView(job));
1348
+ return c.json(ok({ jobs, total: jobs.length }));
1349
+ });
1350
+ app.delete("/api/cron/:id", (c) => {
1351
+ if (!options.cronService) {
1352
+ return c.json(err("NOT_AVAILABLE", "cron service unavailable"), 503);
1353
+ }
1354
+ const id = decodeURIComponent(c.req.param("id"));
1355
+ const deleted = options.cronService.removeJob(id);
1356
+ if (!deleted) {
1357
+ return c.json(err("NOT_FOUND", `cron job not found: ${id}`), 404);
1358
+ }
1359
+ return c.json(ok({ deleted: true }));
1360
+ });
1361
+ app.put("/api/cron/:id/enable", async (c) => {
1362
+ if (!options.cronService) {
1363
+ return c.json(err("NOT_AVAILABLE", "cron service unavailable"), 503);
1364
+ }
1365
+ const id = decodeURIComponent(c.req.param("id"));
1366
+ const body = await readJson(c.req.raw);
1367
+ if (!body.ok) {
1368
+ return c.json(err("INVALID_BODY", "invalid json body"), 400);
1369
+ }
1370
+ if (typeof body.data.enabled !== "boolean") {
1371
+ return c.json(err("INVALID_BODY", "enabled must be boolean"), 400);
1372
+ }
1373
+ const job = options.cronService.enableJob(id, body.data.enabled);
1374
+ if (!job) {
1375
+ return c.json(err("NOT_FOUND", `cron job not found: ${id}`), 404);
1376
+ }
1377
+ const data = { job: buildCronJobView(job) };
1378
+ return c.json(ok(data));
1379
+ });
1380
+ app.post("/api/cron/:id/run", async (c) => {
1381
+ if (!options.cronService) {
1382
+ return c.json(err("NOT_AVAILABLE", "cron service unavailable"), 503);
1383
+ }
1384
+ const id = decodeURIComponent(c.req.param("id"));
1385
+ const body = await readJson(c.req.raw);
1386
+ if (!body.ok) {
1387
+ return c.json(err("INVALID_BODY", "invalid json body"), 400);
1388
+ }
1389
+ const existing = findCronJob(options.cronService, id);
1390
+ if (!existing) {
1391
+ return c.json(err("NOT_FOUND", `cron job not found: ${id}`), 404);
1392
+ }
1393
+ const executed = await options.cronService.runJob(id, Boolean(body.data.force));
1394
+ const after = findCronJob(options.cronService, id);
1395
+ const data = {
1396
+ job: after ? buildCronJobView(after) : null,
1397
+ executed
1398
+ };
1399
+ return c.json(ok(data));
1400
+ });
1297
1401
  app.put("/api/config/runtime", async (c) => {
1298
1402
  const body = await readJson(c.req.raw);
1299
1403
  if (!body.ok || !body.data || typeof body.data !== "object") {
@@ -1353,7 +1457,8 @@ function startUiServer(options) {
1353
1457
  createUiRouter({
1354
1458
  configPath: options.configPath,
1355
1459
  publish,
1356
- marketplace: options.marketplace
1460
+ marketplace: options.marketplace,
1461
+ cronService: options.cronService
1357
1462
  })
1358
1463
  );
1359
1464
  const staticDir = options.staticDir;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/server",
3
- "version": "0.5.3",
3
+ "version": "0.5.6",
4
4
  "private": false,
5
5
  "description": "Nextclaw UI/API server.",
6
6
  "type": "module",
@@ -15,10 +15,10 @@
15
15
  ],
16
16
  "dependencies": {
17
17
  "@hono/node-server": "^1.13.3",
18
- "@nextclaw/openclaw-compat": "^0.1.21",
18
+ "@nextclaw/openclaw-compat": "^0.1.23",
19
19
  "hono": "^4.6.2",
20
20
  "ws": "^8.18.0",
21
- "@nextclaw/core": "^0.6.28"
21
+ "@nextclaw/core": "^0.6.30"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@types/node": "^20.17.6",