clawkit-ai 1.0.4 → 1.0.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/package.json +1 -1
- package/src/sync.mjs +134 -10
package/package.json
CHANGED
package/src/sync.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync, readdirSync, statSync, writeFileSync } from 'fs';
|
|
2
2
|
import { join } from 'path';
|
|
3
|
+
import { execSync } from 'child_process';
|
|
3
4
|
import { c, log, detectWorkspace } from './utils.mjs';
|
|
4
5
|
|
|
5
6
|
const API_BASE = 'https://clawkit-api.vercel.app';
|
|
@@ -20,13 +21,13 @@ const SERVICE_PATTERNS = [
|
|
|
20
21
|
{ key: 'brave_search', names: ['Brave Search', 'Brave API'], patterns: [/Brave.*(?:Key|API).*?[:`]\s*`?([^\s`]+)/i] },
|
|
21
22
|
{ key: 'telegram', names: ['Telegram'], patterns: [/Telegram.*(?:Token|Bot).*?[:`]\s*`?(\d+:[^\s`]+)/i] },
|
|
22
23
|
{ key: 'discord', names: ['Discord'], patterns: [/Discord.*(?:Token|Bot).*?[:`]\s*`?([^\s`]+)/i] },
|
|
23
|
-
{ key: 'github', names: ['GitHub'], patterns: [/
|
|
24
|
-
{ key: 'email', names: ['Email', 'IMAP', 'SMTP'], patterns: [
|
|
25
|
-
{ key: 'sogni', names: ['Sogni'], patterns: [/SOGNI_USERNAME.*?[
|
|
26
|
-
{ key: 'upload_post', names: ['Upload-Post', 'upload-post'], patterns: [
|
|
27
|
-
{ key: 'vercel', names: ['Vercel'], patterns: [/
|
|
28
|
-
{ key: 'netlify', names: ['Netlify'], patterns: [/
|
|
29
|
-
{ key: 'maton', names: ['Maton', 'Google Workspace'], patterns: [/MATON_API_KEY
|
|
24
|
+
{ key: 'github', names: ['GitHub'], patterns: [/\*\*PAT:\*\*\s*`?(ghp_[^\s`]+)/i, /github.*(?:Token|PAT).*?`?(ghp_[^\s`]+)/i] },
|
|
25
|
+
{ key: 'email', names: ['Email', 'IMAP', 'SMTP'], patterns: [/\*\*Email:\*\*\s*`?([^\s`]+@[^\s`]+)/i, /App Password.*?[:`]\s*`?([a-z ]{10,})/i] },
|
|
26
|
+
{ key: 'sogni', names: ['Sogni'], patterns: [/SOGNI_USERNAME.*?["'`]\s*([^\s"'`]+)/i, /SOGNI_PASSWORD.*?["'`]\s*([^\s"'`]+)/i] },
|
|
27
|
+
{ key: 'upload_post', names: ['Upload-Post', 'upload-post'], patterns: [/\*\*API Key:\*\*\s*`?(ey[^\s`]+)/i, /upload-post.*?Key.*?`?(ey[^\s`]+)/i] },
|
|
28
|
+
{ key: 'vercel', names: ['Vercel'], patterns: [/(?:Token|token).*?`?(vcp_[^\s`]+)/i, /--token\s+(vcp_[^\s`]+)/i] },
|
|
29
|
+
{ key: 'netlify', names: ['Netlify'], patterns: [/(?:Site ID|site).*?`?([0-9a-f]{8}-[0-9a-f-]{27,})/i] },
|
|
30
|
+
{ key: 'maton', names: ['Maton', 'Google Workspace'], patterns: [/MATON_API_KEY.*?`?([A-Za-z0-9_-]{30,})/i] },
|
|
30
31
|
];
|
|
31
32
|
|
|
32
33
|
function parseAccountsFromTools(workspace) {
|
|
@@ -238,7 +239,75 @@ function gatherState(workspace) {
|
|
|
238
239
|
}
|
|
239
240
|
}
|
|
240
241
|
|
|
241
|
-
//
|
|
242
|
+
// --- Cron Jobs (from OpenClaw gateway API) ---
|
|
243
|
+
try {
|
|
244
|
+
// Try the local gateway WebSocket API via HTTP
|
|
245
|
+
const cronOut = execSync('curl -s -m 3 http://127.0.0.1:18789/api/cron/list 2>/dev/null || true', { timeout: 5000, encoding: 'utf8' });
|
|
246
|
+
const cronMatch = cronOut.match(/\{[\s\S]*\}/);
|
|
247
|
+
if (cronMatch) {
|
|
248
|
+
const cronData = JSON.parse(cronMatch[0]);
|
|
249
|
+
const jobs = cronData.jobs || [];
|
|
250
|
+
state.cronJobs = jobs.map(j => ({
|
|
251
|
+
name: j.name || (j.payload?.text || '').slice(0, 40) || 'Unnamed',
|
|
252
|
+
schedule: j.schedule?.kind === 'cron' ? j.schedule.expr :
|
|
253
|
+
j.schedule?.kind === 'every' ? `every ${Math.round((j.schedule.everyMs || 0) / 60000)}m` :
|
|
254
|
+
j.schedule?.kind === 'at' ? `at ${j.schedule.at}` : 'unknown',
|
|
255
|
+
lastRun: j.state?.lastRunAtMs ? new Date(j.state.lastRunAtMs).toISOString() : undefined,
|
|
256
|
+
status: j.state?.lastStatus || (j.enabled ? 'ok' : 'disabled'),
|
|
257
|
+
}));
|
|
258
|
+
}
|
|
259
|
+
} catch {}
|
|
260
|
+
|
|
261
|
+
// Fallback: parse cron info from HEARTBEAT.md
|
|
262
|
+
if (state.cronJobs.length === 0) {
|
|
263
|
+
const hbPath = join(workspace, 'HEARTBEAT.md');
|
|
264
|
+
if (existsSync(hbPath)) {
|
|
265
|
+
const hb = readFileSync(hbPath, 'utf8');
|
|
266
|
+
const tasks = hb.match(/\*\*(?:Task|Check):\*\*\s*(.+)/g) || [];
|
|
267
|
+
for (const t of tasks.slice(0, 8)) {
|
|
268
|
+
state.cronJobs.push({
|
|
269
|
+
name: t.replace(/\*\*(?:Task|Check):\*\*\s*/, '').slice(0, 50),
|
|
270
|
+
schedule: 'heartbeat',
|
|
271
|
+
status: 'ok',
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// --- Recent Activity (real sources: git log, file changes, daily notes) ---
|
|
278
|
+
|
|
279
|
+
// Git log (last 10 commits from any repo in Desktop)
|
|
280
|
+
try {
|
|
281
|
+
const gitDirs = ['clawkit', 'clawkit-api', 'clawkit-package', 'dashboard'].map(d => join('/home', 'semo', 'Desktop', d));
|
|
282
|
+
for (const dir of gitDirs) {
|
|
283
|
+
if (!existsSync(join(dir, '.git'))) continue;
|
|
284
|
+
try {
|
|
285
|
+
const gitLog = execSync(`cd "${dir}" && git log --oneline --format="%ai|%s" -5 2>/dev/null`, { timeout: 3000, encoding: 'utf8' });
|
|
286
|
+
const repoName = dir.split('/').pop();
|
|
287
|
+
for (const line of gitLog.trim().split('\n').filter(Boolean)) {
|
|
288
|
+
const [date, ...msgParts] = line.split('|');
|
|
289
|
+
state.recentActivity.push({
|
|
290
|
+
timestamp: new Date(date.trim()).toISOString(),
|
|
291
|
+
message: `[${repoName}] ${msgParts.join('|').slice(0, 100)}`,
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
} catch {}
|
|
295
|
+
}
|
|
296
|
+
} catch {}
|
|
297
|
+
|
|
298
|
+
// Recent workspace file changes (last 24h)
|
|
299
|
+
try {
|
|
300
|
+
const recentFiles = execSync(`find "${workspace}" -name "*.md" -mmin -1440 -not -path "*/node_modules/*" -not -path "*/.git/*" -printf "%T@|%f\n" 2>/dev/null | sort -rn | head -5`, { timeout: 3000, encoding: 'utf8' });
|
|
301
|
+
for (const line of recentFiles.trim().split('\n').filter(Boolean)) {
|
|
302
|
+
const [ts, file] = line.split('|');
|
|
303
|
+
state.recentActivity.push({
|
|
304
|
+
timestamp: new Date(parseFloat(ts) * 1000).toISOString(),
|
|
305
|
+
message: `Updated ${file}`,
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
} catch {}
|
|
309
|
+
|
|
310
|
+
// Daily notes entries
|
|
242
311
|
const today = new Date().toISOString().slice(0, 10);
|
|
243
312
|
const yesterday = new Date(Date.now() - 86400000).toISOString().slice(0, 10);
|
|
244
313
|
for (const day of [today, yesterday]) {
|
|
@@ -248,7 +317,7 @@ function gatherState(workspace) {
|
|
|
248
317
|
if (existsSync(f)) {
|
|
249
318
|
const lines = readFileSync(f, 'utf8').split('\n')
|
|
250
319
|
.filter(l => l.startsWith('- ') || l.startsWith('* '))
|
|
251
|
-
.slice(0,
|
|
320
|
+
.slice(0, 3);
|
|
252
321
|
for (const line of lines) {
|
|
253
322
|
state.recentActivity.push({
|
|
254
323
|
timestamp: `${day}T12:00:00Z`,
|
|
@@ -258,7 +327,62 @@ function gatherState(workspace) {
|
|
|
258
327
|
}
|
|
259
328
|
}
|
|
260
329
|
}
|
|
261
|
-
|
|
330
|
+
|
|
331
|
+
// Sort by timestamp descending, dedupe, limit
|
|
332
|
+
state.recentActivity.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
|
333
|
+
state.recentActivity = state.recentActivity.slice(0, 20);
|
|
334
|
+
|
|
335
|
+
// --- Token Usage & Cost ---
|
|
336
|
+
state.usage = { inputTokens: 0, outputTokens: 0, contextUsed: 0, contextMax: 0, costEstimate: '', model: '', compactions: 0 };
|
|
337
|
+
|
|
338
|
+
// Try to read from OpenClaw gateway config for model
|
|
339
|
+
try {
|
|
340
|
+
const gwConfig = join(process.env.HOME || '', '.openclaw', 'config.yaml');
|
|
341
|
+
if (existsSync(gwConfig)) {
|
|
342
|
+
const cfg = readFileSync(gwConfig, 'utf8');
|
|
343
|
+
const modelMatch = cfg.match(/model:\s*["']?([^\s"']+)/);
|
|
344
|
+
if (modelMatch) state.usage.model = modelMatch[1];
|
|
345
|
+
}
|
|
346
|
+
} catch {}
|
|
347
|
+
|
|
348
|
+
// Try to get session stats from gateway HTTP API
|
|
349
|
+
try {
|
|
350
|
+
const statusOut = execSync('curl -s -m 3 http://127.0.0.1:18789/api/session/status 2>/dev/null || true', { timeout: 5000, encoding: 'utf8' });
|
|
351
|
+
const statusMatch = statusOut.match(/\{[\s\S]*\}/);
|
|
352
|
+
if (statusMatch) {
|
|
353
|
+
const sd = JSON.parse(statusMatch[0]);
|
|
354
|
+
if (sd.inputTokens || sd.tokensIn) state.usage.inputTokens = sd.inputTokens || sd.tokensIn || 0;
|
|
355
|
+
if (sd.outputTokens || sd.tokensOut) state.usage.outputTokens = sd.outputTokens || sd.tokensOut || 0;
|
|
356
|
+
if (sd.contextUsed) state.usage.contextUsed = sd.contextUsed;
|
|
357
|
+
if (sd.contextMax) state.usage.contextMax = sd.contextMax;
|
|
358
|
+
if (sd.compactions) state.usage.compactions = sd.compactions;
|
|
359
|
+
if (sd.model) state.usage.model = sd.model;
|
|
360
|
+
}
|
|
361
|
+
} catch {}
|
|
362
|
+
|
|
363
|
+
// If we couldn't get from CLI, estimate from daily note sizes
|
|
364
|
+
if (!state.usage.model) {
|
|
365
|
+
state.usage.model = state.preferences.defaultModel || 'unknown';
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Cost estimation based on model
|
|
369
|
+
const MODEL_COSTS = {
|
|
370
|
+
'claude-opus-4-6': { input: 15, output: 75 }, // per 1M tokens
|
|
371
|
+
'claude-opus-4': { input: 15, output: 75 },
|
|
372
|
+
'claude-sonnet-4-5': { input: 3, output: 15 },
|
|
373
|
+
'claude-sonnet-4': { input: 3, output: 15 },
|
|
374
|
+
'claude-haiku-3-5': { input: 0.8, output: 4 },
|
|
375
|
+
'gpt-4o': { input: 2.5, output: 10 },
|
|
376
|
+
'gpt-4o-mini': { input: 0.15, output: 0.6 },
|
|
377
|
+
'gemini-2.0-flash': { input: 0.1, output: 0.4 },
|
|
378
|
+
};
|
|
379
|
+
const modelKey = Object.keys(MODEL_COSTS).find(k => state.usage.model?.includes(k));
|
|
380
|
+
const costs = MODEL_COSTS[modelKey] || { input: 3, output: 15 };
|
|
381
|
+
const inCost = (state.usage.inputTokens / 1_000_000) * costs.input;
|
|
382
|
+
const outCost = (state.usage.outputTokens / 1_000_000) * costs.output;
|
|
383
|
+
state.usage.costEstimate = `$${(inCost + outCost).toFixed(4)}`;
|
|
384
|
+
state.usage.costPerMInput = costs.input;
|
|
385
|
+
state.usage.costPerMOutput = costs.output;
|
|
262
386
|
|
|
263
387
|
return state;
|
|
264
388
|
}
|