0nmcp 1.6.0 → 2.0.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.
@@ -0,0 +1,270 @@
1
+ // ============================================================
2
+ // 0nMCP -Engine: Cron Scheduler
3
+ // ============================================================
4
+ // Minimal 5-field cron parser + scheduler.
5
+ // Pure JS, zero external dependencies (Node.js builtins only).
6
+ //
7
+ // Cron format: minute hour day-of-month month day-of-week
8
+ // - Standard 5 fields
9
+ // - Supports: * ranges (1-5) steps (*/15) lists (1,3,5)
10
+ //
11
+ // Patent Pending: US Provisional Patent Application #63/968,814
12
+ // ============================================================
13
+
14
+ /**
15
+ * Parse a single cron field into an array of valid values.
16
+ *
17
+ * @param {string} field - Cron field expression
18
+ * @param {number} min - Minimum value for this field
19
+ * @param {number} max - Maximum value for this field
20
+ * @returns {number[]} Array of matching values
21
+ */
22
+ function parseField(field, min, max) {
23
+ const values = new Set();
24
+
25
+ for (const part of field.split(",")) {
26
+ const trimmed = part.trim();
27
+
28
+ // Step: */n or range/n
29
+ if (trimmed.includes("/")) {
30
+ const [range, stepStr] = trimmed.split("/");
31
+ const step = parseInt(stepStr, 10);
32
+ if (isNaN(step) || step <= 0) continue;
33
+
34
+ let start = min;
35
+ let end = max;
36
+
37
+ if (range !== "*") {
38
+ if (range.includes("-")) {
39
+ [start, end] = range.split("-").map(Number);
40
+ } else {
41
+ start = parseInt(range, 10);
42
+ }
43
+ }
44
+
45
+ for (let i = start; i <= end; i += step) {
46
+ if (i >= min && i <= max) values.add(i);
47
+ }
48
+ continue;
49
+ }
50
+
51
+ // Range: a-b
52
+ if (trimmed.includes("-")) {
53
+ const [a, b] = trimmed.split("-").map(Number);
54
+ for (let i = a; i <= b; i++) {
55
+ if (i >= min && i <= max) values.add(i);
56
+ }
57
+ continue;
58
+ }
59
+
60
+ // Wildcard
61
+ if (trimmed === "*") {
62
+ for (let i = min; i <= max; i++) values.add(i);
63
+ continue;
64
+ }
65
+
66
+ // Single value
67
+ const val = parseInt(trimmed, 10);
68
+ if (!isNaN(val) && val >= min && val <= max) {
69
+ values.add(val);
70
+ }
71
+ }
72
+
73
+ return [...values].sort((a, b) => a - b);
74
+ }
75
+
76
+ /**
77
+ * Parse a 5-field cron expression.
78
+ *
79
+ * @param {string} expr -e.g., "0 9 * * *" (9:00 AM daily)
80
+ * @returns {{ minutes: number[], hours: number[], days: number[], months: number[], weekdays: number[], matches: (date: Date) => boolean, nextRun: (from?: Date) => Date }}
81
+ */
82
+ export function parseCron(expr) {
83
+ const parts = expr.trim().split(/\s+/);
84
+ if (parts.length !== 5) {
85
+ throw new Error(`Invalid cron expression: "${expr}" -must have 5 fields (minute hour day month weekday)`);
86
+ }
87
+
88
+ const [minField, hourField, dayField, monthField, weekdayField] = parts;
89
+
90
+ const minutes = parseField(minField, 0, 59);
91
+ const hours = parseField(hourField, 0, 23);
92
+ const days = parseField(dayField, 1, 31);
93
+ const months = parseField(monthField, 1, 12);
94
+ const weekdays = parseField(weekdayField, 0, 6); // 0 = Sunday
95
+
96
+ if (minutes.length === 0) throw new Error(`Invalid minute field: ${minField}`);
97
+ if (hours.length === 0) throw new Error(`Invalid hour field: ${hourField}`);
98
+ if (days.length === 0) throw new Error(`Invalid day field: ${dayField}`);
99
+ if (months.length === 0) throw new Error(`Invalid month field: ${monthField}`);
100
+ if (weekdays.length === 0) throw new Error(`Invalid weekday field: ${weekdayField}`);
101
+
102
+ const minuteSet = new Set(minutes);
103
+ const hourSet = new Set(hours);
104
+ const daySet = new Set(days);
105
+ const monthSet = new Set(months);
106
+ const weekdaySet = new Set(weekdays);
107
+
108
+ /**
109
+ * Check if a Date matches this cron expression.
110
+ */
111
+ function matches(date) {
112
+ return (
113
+ minuteSet.has(date.getMinutes()) &&
114
+ hourSet.has(date.getHours()) &&
115
+ daySet.has(date.getDate()) &&
116
+ monthSet.has(date.getMonth() + 1) &&
117
+ weekdaySet.has(date.getDay())
118
+ );
119
+ }
120
+
121
+ /**
122
+ * Calculate the next run time from a given date.
123
+ * Searches forward up to 2 years to find a match.
124
+ */
125
+ function nextRun(from) {
126
+ const d = from ? new Date(from) : new Date();
127
+ // Advance to next minute boundary
128
+ d.setSeconds(0, 0);
129
+ d.setMinutes(d.getMinutes() + 1);
130
+
131
+ const maxDate = new Date(d.getTime() + 2 * 365 * 24 * 60 * 60 * 1000);
132
+
133
+ while (d < maxDate) {
134
+ // Check month
135
+ if (!monthSet.has(d.getMonth() + 1)) {
136
+ d.setMonth(d.getMonth() + 1, 1);
137
+ d.setHours(0, 0, 0, 0);
138
+ continue;
139
+ }
140
+
141
+ // Check day and weekday
142
+ if (!daySet.has(d.getDate()) || !weekdaySet.has(d.getDay())) {
143
+ d.setDate(d.getDate() + 1);
144
+ d.setHours(0, 0, 0, 0);
145
+ continue;
146
+ }
147
+
148
+ // Check hour
149
+ if (!hourSet.has(d.getHours())) {
150
+ d.setHours(d.getHours() + 1, 0, 0, 0);
151
+ continue;
152
+ }
153
+
154
+ // Check minute
155
+ if (!minuteSet.has(d.getMinutes())) {
156
+ d.setMinutes(d.getMinutes() + 1, 0, 0);
157
+ continue;
158
+ }
159
+
160
+ return new Date(d);
161
+ }
162
+
163
+ throw new Error(`No matching time found within 2 years for cron: ${expr}`);
164
+ }
165
+
166
+ return { minutes, hours, days, months, weekdays, matches, nextRun };
167
+ }
168
+
169
+ /**
170
+ * Cron-based scheduler. Schedules callbacks to fire on cron expressions.
171
+ */
172
+ export class CronScheduler {
173
+ constructor() {
174
+ /** @type {Map<string, { timer: NodeJS.Timeout, cron: object, expr: string, cb: Function }>} */
175
+ this._jobs = new Map();
176
+ }
177
+
178
+ /**
179
+ * Schedule a callback on a cron expression.
180
+ *
181
+ * @param {string} id -Unique job ID
182
+ * @param {string} expr -Cron expression (5 fields)
183
+ * @param {Function} cb -Callback to invoke (receives { id, scheduledTime, expr })
184
+ * @param {object} [options]
185
+ * @param {string} [options.timezone] -Timezone (informational only -uses system TZ)
186
+ * @returns {{ id: string, nextRun: Date }}
187
+ */
188
+ schedule(id, expr, cb, options = {}) {
189
+ if (this._jobs.has(id)) {
190
+ this.stop(id);
191
+ }
192
+
193
+ const cron = parseCron(expr);
194
+ const job = { cron, expr, cb, timezone: options.timezone };
195
+
196
+ const scheduleNext = () => {
197
+ const now = new Date();
198
+ const next = cron.nextRun(now);
199
+ const delay = next.getTime() - now.getTime();
200
+
201
+ job.timer = setTimeout(() => {
202
+ try {
203
+ cb({ id, scheduledTime: next, expr });
204
+ } catch (err) {
205
+ console.error(`Cron job ${id} error:`, err.message);
206
+ }
207
+ // Schedule the next run
208
+ scheduleNext();
209
+ }, delay);
210
+
211
+ // Prevent timer from keeping process alive if it's the only thing running
212
+ if (job.timer.unref) job.timer.unref();
213
+
214
+ job.nextRun = next;
215
+ };
216
+
217
+ scheduleNext();
218
+ this._jobs.set(id, job);
219
+
220
+ return { id, nextRun: job.nextRun };
221
+ }
222
+
223
+ /**
224
+ * Stop a scheduled job.
225
+ * @param {string} id
226
+ * @returns {boolean} True if job was stopped
227
+ */
228
+ stop(id) {
229
+ const job = this._jobs.get(id);
230
+ if (!job) return false;
231
+
232
+ clearTimeout(job.timer);
233
+ this._jobs.delete(id);
234
+ return true;
235
+ }
236
+
237
+ /**
238
+ * Stop all scheduled jobs.
239
+ * @returns {number} Number of jobs stopped
240
+ */
241
+ stopAll() {
242
+ let count = 0;
243
+ for (const [id] of this._jobs) {
244
+ this.stop(id);
245
+ count++;
246
+ }
247
+ return count;
248
+ }
249
+
250
+ /**
251
+ * List all active jobs.
252
+ * @returns {Array<{ id: string, expr: string, nextRun: Date }>}
253
+ */
254
+ list() {
255
+ return [...this._jobs.entries()].map(([id, job]) => ({
256
+ id,
257
+ expr: job.expr,
258
+ nextRun: job.nextRun,
259
+ timezone: job.timezone,
260
+ }));
261
+ }
262
+
263
+ /**
264
+ * Get the number of active jobs.
265
+ * @returns {number}
266
+ */
267
+ get size() {
268
+ return this._jobs.size;
269
+ }
270
+ }
package/index.js CHANGED
@@ -28,6 +28,7 @@ import { WorkflowRunner } from "./workflow.js";
28
28
  import { registerAllTools } from "./tools.js";
29
29
  import { registerCrmTools } from "./crm/index.js";
30
30
  import { registerVaultTools, autoUnseal } from "./vault/index.js";
31
+ import { registerContainerTools } from "./vault/tools-container.js";
31
32
  import { unsealedCache } from "./vault/cache.js";
32
33
  import { registerEngineTools } from "./engine/index.js";
33
34
 
@@ -39,7 +40,7 @@ const workflowRunner = new WorkflowRunner(connections);
39
40
 
40
41
  const server = new McpServer({
41
42
  name: "0nMCP",
42
- version: "1.6.0",
43
+ version: "2.0.0",
43
44
  });
44
45
 
45
46
  // ============================================================
@@ -73,6 +74,12 @@ if (vaultResult.unsealed.length > 0) {
73
74
 
74
75
  registerEngineTools(server, z);
75
76
 
77
+ // ============================================================
78
+ // VAULT CONTAINER TOOLS (patent-pending 0nVault containers)
79
+ // ============================================================
80
+
81
+ registerContainerTools(server, z);
82
+
76
83
  // ============================================================
77
84
  // START SERVER (stdio transport)
78
85
  // ============================================================
package/lib/badges.json CHANGED
@@ -26,7 +26,7 @@
26
26
  "total": {
27
27
  "schemaVersion": 1,
28
28
  "label": "capabilities",
29
- "message": "703",
29
+ "message": "708",
30
30
  "color": "00ff88"
31
31
  }
32
32
  }
package/lib/stats.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "generated": "2026-02-15T01:13:33.591Z",
2
+ "generated": "2026-02-25T10:54:43.654Z",
3
3
  "catalogVersion": "1.2.2",
4
4
  "services": 26,
5
5
  "tools": 290,
@@ -794,6 +794,7 @@
794
794
  "crmTools": 245,
795
795
  "vaultTools": 4,
796
796
  "engineTools": 6,
797
- "totalTools": 545,
798
- "totalCapabilities": 703
797
+ "appTools": 5,
798
+ "totalTools": 550,
799
+ "totalCapabilities": 708
799
800
  }
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "0nmcp",
3
- "version": "1.6.0",
3
+ "version": "2.0.0",
4
4
  "mcpName": "io.github.0nork/0nMCP",
5
- "description": "Universal AI API Orchestrator — 545 tools, 26 services, portable AI Brain bundles + machine-bound vault encryption. The most comprehensive MCP server available. Free and open source from 0nORK.",
5
+ "description": "Universal AI API Orchestrator — 550 tools, 26 services, portable AI Brain bundles + machine-bound vault encryption + Application Engine. The most comprehensive MCP server available. Free and open source from 0nORK.",
6
6
  "type": "module",
7
7
  "main": "index.js",
8
8
  "types": "types/index.d.ts",
@@ -49,6 +49,30 @@
49
49
  },
50
50
  "./engine": {
51
51
  "import": "./engine/index.js"
52
+ },
53
+ "./engine/application": {
54
+ "import": "./engine/application.js"
55
+ },
56
+ "./engine/app-server": {
57
+ "import": "./engine/app-server.js"
58
+ },
59
+ "./engine/app-builder": {
60
+ "import": "./engine/app-builder.js"
61
+ },
62
+ "./engine/operations": {
63
+ "import": "./engine/operations.js"
64
+ },
65
+ "./engine/scheduler": {
66
+ "import": "./engine/scheduler.js"
67
+ },
68
+ "./vault/container": {
69
+ "import": "./vault/container.js"
70
+ },
71
+ "./vault/escrow": {
72
+ "import": "./vault/escrow.js"
73
+ },
74
+ "./vault/seal": {
75
+ "import": "./vault/seal.js"
52
76
  }
53
77
  },
54
78
  "scripts": {
@@ -121,6 +145,15 @@
121
145
  "ai-brain",
122
146
  "credential-import",
123
147
  "platform-config",
148
+ "0nvault",
149
+ "semantic-layers",
150
+ "escrow",
151
+ "seal-of-truth",
152
+ "ed25519",
153
+ "x25519",
154
+ "sha3",
155
+ "argon2",
156
+ "digital-asset-transfer",
124
157
  "0n",
125
158
  "0nork",
126
159
  "0nmcp"
@@ -144,7 +177,10 @@
144
177
  },
145
178
  "dependencies": {
146
179
  "@modelcontextprotocol/sdk": "^1.26.0",
147
- "express": "^4.21.0"
180
+ "argon2": "^0.44.0",
181
+ "express": "^4.21.0",
182
+ "js-sha3": "^0.9.3",
183
+ "tweetnacl": "^1.0.3"
148
184
  },
149
185
  "optionalDependencies": {
150
186
  "0n-spec": "^1.1.0"
@@ -160,6 +196,8 @@
160
196
  "files": [
161
197
  "index.js",
162
198
  "cli.js",
199
+ "commands.js",
200
+ "command-runner.js",
163
201
  "tools.js",
164
202
  "workflow.js",
165
203
  "server.js",
@@ -182,12 +220,13 @@
182
220
  "crmTools": 245,
183
221
  "vaultTools": 4,
184
222
  "engineTools": 6,
185
- "totalTools": 545,
223
+ "appTools": 5,
224
+ "totalTools": 550,
186
225
  "services": 26,
187
226
  "actions": 65,
188
227
  "triggers": 93,
189
- "totalCapabilities": 703,
228
+ "totalCapabilities": 708,
190
229
  "categories": 13,
191
- "lastUpdated": "2026-02-15T01:13:33.591Z"
230
+ "lastUpdated": "2026-02-25T10:54:43.654Z"
192
231
  }
193
232
  }
package/server.js CHANGED
@@ -92,7 +92,7 @@ export async function createApp() {
92
92
 
93
93
  // New session
94
94
  const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => crypto.randomUUID() });
95
- const server = new McpServer({ name: "0nMCP", version: "1.6.0" });
95
+ const server = new McpServer({ name: "0nMCP", version: "1.7.0" });
96
96
  registerAllTools(server, connections, orchestrator, workflowRunner);
97
97
  registerCrmTools(server, z);
98
98
  registerVaultTools(server, z);
@@ -123,7 +123,7 @@ export async function createApp() {
123
123
  res.json({
124
124
  status: "ok",
125
125
  name: "0nMCP",
126
- version: "1.6.0",
126
+ version: "1.7.0",
127
127
  uptime: process.uptime(),
128
128
  connections: connections.count(),
129
129
  workflows: workflowRunner.listWorkflows().length,