@sleep2agi/commhub-server 0.5.0-preview.16 → 0.5.0-preview.18
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/package.json +1 -1
- package/src/db.ts +27 -0
- package/src/index.ts +38 -0
- package/src/tools.ts +13 -0
package/package.json
CHANGED
package/src/db.ts
CHANGED
|
@@ -221,6 +221,33 @@ db.exec(`
|
|
|
221
221
|
CREATE INDEX IF NOT EXISTS idx_audit_network ON audit_log(network_id);
|
|
222
222
|
`);
|
|
223
223
|
|
|
224
|
+
// ── V3: licenses table ──
|
|
225
|
+
db.exec(`
|
|
226
|
+
CREATE TABLE IF NOT EXISTS licenses (
|
|
227
|
+
id TEXT PRIMARY KEY,
|
|
228
|
+
license_key TEXT UNIQUE NOT NULL,
|
|
229
|
+
type TEXT DEFAULT 'trial',
|
|
230
|
+
max_agents INTEGER DEFAULT 5,
|
|
231
|
+
max_networks INTEGER DEFAULT 3,
|
|
232
|
+
max_tasks_day INTEGER DEFAULT 500,
|
|
233
|
+
activated_at TEXT,
|
|
234
|
+
expires_at TEXT,
|
|
235
|
+
owner_id TEXT,
|
|
236
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
237
|
+
);
|
|
238
|
+
`);
|
|
239
|
+
|
|
240
|
+
// Auto-create trial license on first run
|
|
241
|
+
const existingLicense = db.query<any, []>("SELECT id FROM licenses LIMIT 1").get();
|
|
242
|
+
if (!existingLicense) {
|
|
243
|
+
const trialId = crypto.randomUUID().replace(/-/g, "").slice(0, 12);
|
|
244
|
+
db.run(
|
|
245
|
+
"INSERT INTO licenses (id, license_key, type, expires_at) VALUES (?1, ?2, 'trial', datetime('now', '+14 days'))",
|
|
246
|
+
[`lic_${trialId}`, `trial-${trialId}`]
|
|
247
|
+
);
|
|
248
|
+
console.log("[commhub] 🎉 14-day free trial started!");
|
|
249
|
+
}
|
|
250
|
+
|
|
224
251
|
// ── V3: add network_id to existing tables ──
|
|
225
252
|
for (const table of ["sessions", "nodes", "tasks", "inbox", "task_events"]) {
|
|
226
253
|
try { db.exec(`ALTER TABLE ${table} ADD COLUMN network_id TEXT`); } catch {}
|
package/src/index.ts
CHANGED
|
@@ -163,6 +163,44 @@ Bun.serve({
|
|
|
163
163
|
return createSSEStream(sessionName);
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
+
// ── V3: License endpoints ──
|
|
167
|
+
if (url.pathname === "/api/license" && req.method === "GET") {
|
|
168
|
+
const license = db.query<any, []>("SELECT * FROM licenses ORDER BY created_at LIMIT 1").get();
|
|
169
|
+
if (!license) return withCors(req, Response.json({ ok: true, status: "no_license" }));
|
|
170
|
+
const now = new Date().toISOString().replace("T", " ").slice(0, 19);
|
|
171
|
+
const expired = license.expires_at && license.expires_at < now;
|
|
172
|
+
const daysLeft = license.expires_at
|
|
173
|
+
? Math.max(0, Math.ceil((new Date(license.expires_at).getTime() - Date.now()) / 86400000))
|
|
174
|
+
: null;
|
|
175
|
+
return withCors(req, Response.json({
|
|
176
|
+
ok: true,
|
|
177
|
+
license: { type: license.type, expires_at: license.expires_at, days_left: daysLeft, expired },
|
|
178
|
+
limits: { max_agents: license.max_agents, max_networks: license.max_networks, max_tasks_day: license.max_tasks_day },
|
|
179
|
+
}));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (url.pathname === "/api/license/activate" && req.method === "POST") {
|
|
183
|
+
try {
|
|
184
|
+
const body = await req.json() as any;
|
|
185
|
+
const key = body.key;
|
|
186
|
+
if (!key) return withCors(req, Response.json({ ok: false, error: "key required" }, { status: 400 }));
|
|
187
|
+
// For now: accept any key starting with "anet-" as valid pro license
|
|
188
|
+
if (!key.startsWith("anet-") || key.length < 16) {
|
|
189
|
+
return withCors(req, Response.json({ ok: false, error: "invalid license key" }, { status: 400 }));
|
|
190
|
+
}
|
|
191
|
+
// Upgrade existing license or create new
|
|
192
|
+
db.run("DELETE FROM licenses");
|
|
193
|
+
const licId = `lic_${crypto.randomUUID().replace(/-/g, "").slice(0, 12)}`;
|
|
194
|
+
db.run(
|
|
195
|
+
"INSERT INTO licenses (id, license_key, type, max_agents, max_networks, max_tasks_day, activated_at, expires_at) VALUES (?1, ?2, 'pro', 50, 10, 10000, datetime('now'), datetime('now', '+365 days'))",
|
|
196
|
+
[licId, key]
|
|
197
|
+
);
|
|
198
|
+
return withCors(req, Response.json({ ok: true, type: "pro", expires_in_days: 365 }));
|
|
199
|
+
} catch (e: any) {
|
|
200
|
+
return withCors(req, Response.json({ ok: false, error: e.message }, { status: 400 }));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
166
204
|
// ── V3: Auth endpoints (public) ──
|
|
167
205
|
if (url.pathname === "/api/auth/register" && req.method === "POST") {
|
|
168
206
|
try {
|
package/src/tools.ts
CHANGED
|
@@ -351,6 +351,19 @@ export function registerTools(server: McpServer, clientIP?: string, enforceNetwo
|
|
|
351
351
|
},
|
|
352
352
|
async ({ alias, task, priority, context, from_session, ttl_seconds, network_id: netId }) => {
|
|
353
353
|
const effectiveNetId = getNetworkId(netId);
|
|
354
|
+
|
|
355
|
+
// License check
|
|
356
|
+
const license = db.query<any, []>("SELECT type, expires_at FROM licenses ORDER BY created_at LIMIT 1").get();
|
|
357
|
+
if (license?.expires_at) {
|
|
358
|
+
const now = new Date().toISOString().replace("T", " ").slice(0, 19);
|
|
359
|
+
if (license.expires_at < now) {
|
|
360
|
+
return { content: [{ type: "text" as const, text: JSON.stringify({
|
|
361
|
+
ok: false, error: "license_expired",
|
|
362
|
+
message: "Trial expired. Activate a license: anet activate <key>",
|
|
363
|
+
}) }] };
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
354
367
|
console.log(`[${ts()}] ${from_session} → send_task → ${alias}: ${task.slice(0, 60)}${priority === "high" ? " [HIGH]" : ""}`);
|
|
355
368
|
const id = uuidv4();
|
|
356
369
|
// 事务:inbox + tasks 双写
|