agendex-cli 0.8.4 → 0.8.5
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/cli.js +96 -14
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3104,6 +3104,53 @@ function spawnBrowser(command, args, options = {}) {
|
|
|
3104
3104
|
import { spawn as spawn2 } from "node:child_process";
|
|
3105
3105
|
import { resolve as resolve4 } from "node:path";
|
|
3106
3106
|
import { fileURLToPath } from "node:url";
|
|
3107
|
+
|
|
3108
|
+
// src/sync-cache.ts
|
|
3109
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
3110
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "node:fs";
|
|
3111
|
+
import { homedir as homedir10 } from "node:os";
|
|
3112
|
+
import { join as join11 } from "node:path";
|
|
3113
|
+
var CACHE_PATH = join11(homedir10(), ".agendex", "sync-cache.json");
|
|
3114
|
+
function loadSyncCache() {
|
|
3115
|
+
if (!existsSync7(CACHE_PATH))
|
|
3116
|
+
return {};
|
|
3117
|
+
try {
|
|
3118
|
+
const raw = JSON.parse(readFileSync5(CACHE_PATH, "utf-8"));
|
|
3119
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
3120
|
+
return {};
|
|
3121
|
+
return raw;
|
|
3122
|
+
} catch {
|
|
3123
|
+
return {};
|
|
3124
|
+
}
|
|
3125
|
+
}
|
|
3126
|
+
function saveSyncCache(cache, options) {
|
|
3127
|
+
const dir = join11(homedir10(), ".agendex");
|
|
3128
|
+
if (!existsSync7(dir))
|
|
3129
|
+
mkdirSync3(dir, { recursive: true });
|
|
3130
|
+
if (options?.replace) {
|
|
3131
|
+
writeFileSync3(CACHE_PATH, JSON.stringify(cache));
|
|
3132
|
+
return;
|
|
3133
|
+
}
|
|
3134
|
+
const existing = loadSyncCache();
|
|
3135
|
+
writeFileSync3(CACHE_PATH, JSON.stringify({ ...existing, ...cache }));
|
|
3136
|
+
}
|
|
3137
|
+
function computePayloadHash(payload) {
|
|
3138
|
+
const canonical = JSON.stringify([
|
|
3139
|
+
payload.localPlanId,
|
|
3140
|
+
payload.agent,
|
|
3141
|
+
payload.title,
|
|
3142
|
+
payload.content,
|
|
3143
|
+
payload.format,
|
|
3144
|
+
payload.filePath ?? null,
|
|
3145
|
+
payload.workspace ?? null,
|
|
3146
|
+
payload.metadata ?? null,
|
|
3147
|
+
payload.createdAt ?? null,
|
|
3148
|
+
payload.updatedAt ?? null
|
|
3149
|
+
]);
|
|
3150
|
+
return createHash2("sha256").update(canonical).digest("hex").slice(0, 20);
|
|
3151
|
+
}
|
|
3152
|
+
|
|
3153
|
+
// src/daemon.ts
|
|
3107
3154
|
var MAX_RESTARTS = 5;
|
|
3108
3155
|
var RESTART_WINDOW_MS = 60000;
|
|
3109
3156
|
var RESTART_DELAY_MS = 5000;
|
|
@@ -3127,6 +3174,7 @@ async function runWorker() {
|
|
|
3127
3174
|
setActiveAdapters(adapters);
|
|
3128
3175
|
console.log(`[agendex] daemon starting with ${config.enabledAdapters.length} adapters`);
|
|
3129
3176
|
sendHeartbeat();
|
|
3177
|
+
const syncCache = loadSyncCache();
|
|
3130
3178
|
const syncQueue = [];
|
|
3131
3179
|
let syncing = false;
|
|
3132
3180
|
async function tryRefreshToken() {
|
|
@@ -3167,6 +3215,7 @@ async function runWorker() {
|
|
|
3167
3215
|
console.error(`[agendex] sync failed for "${payload.title}": ${result.error}`);
|
|
3168
3216
|
} else {
|
|
3169
3217
|
syncedCount++;
|
|
3218
|
+
syncCache[payload.localPlanId] = computePayloadHash(payload);
|
|
3170
3219
|
}
|
|
3171
3220
|
}
|
|
3172
3221
|
} catch (err) {
|
|
@@ -3176,6 +3225,7 @@ async function runWorker() {
|
|
|
3176
3225
|
syncing = false;
|
|
3177
3226
|
}
|
|
3178
3227
|
if (syncedCount > 0 || failedCount > 0) {
|
|
3228
|
+
saveSyncCache(syncCache);
|
|
3179
3229
|
console.log(`[agendex] sync complete: ${syncedCount} synced, ${failedCount} failed`);
|
|
3180
3230
|
}
|
|
3181
3231
|
if (syncQueue.length > 0)
|
|
@@ -3185,10 +3235,23 @@ async function runWorker() {
|
|
|
3185
3235
|
console.log(`[agendex] initial scan...`);
|
|
3186
3236
|
await scan();
|
|
3187
3237
|
const plans = getAll();
|
|
3188
|
-
|
|
3238
|
+
let initialSkipped = 0;
|
|
3189
3239
|
for (const plan of plans) {
|
|
3190
|
-
|
|
3240
|
+
const payload = planToPayload(plan);
|
|
3241
|
+
const hash = computePayloadHash(payload);
|
|
3242
|
+
if (syncCache[plan.id] === hash) {
|
|
3243
|
+
initialSkipped++;
|
|
3244
|
+
continue;
|
|
3245
|
+
}
|
|
3246
|
+
syncQueue.push(payload);
|
|
3247
|
+
}
|
|
3248
|
+
const activePlanIds = new Set(plans.map((plan) => plan.id));
|
|
3249
|
+
for (const id of Object.keys(syncCache)) {
|
|
3250
|
+
if (!activePlanIds.has(id))
|
|
3251
|
+
delete syncCache[id];
|
|
3191
3252
|
}
|
|
3253
|
+
saveSyncCache(syncCache, { replace: true });
|
|
3254
|
+
console.log(`[agendex] syncing ${syncQueue.length} plans (${initialSkipped} unchanged)...`);
|
|
3192
3255
|
await processSyncQueue();
|
|
3193
3256
|
setInterval(() => void sendHeartbeat(), CLI_DAEMON_HEARTBEAT_INTERVAL_MS);
|
|
3194
3257
|
startWatching((changedPlans) => {
|
|
@@ -3249,7 +3312,7 @@ async function startSupervisor() {
|
|
|
3249
3312
|
}
|
|
3250
3313
|
|
|
3251
3314
|
// src/sync.ts
|
|
3252
|
-
async function syncAll() {
|
|
3315
|
+
async function syncAll(force = false) {
|
|
3253
3316
|
const config = await loadOrInitConfig();
|
|
3254
3317
|
const adapters = resolveAdapters(config.enabledAdapters);
|
|
3255
3318
|
setActiveAdapters(adapters);
|
|
@@ -3257,9 +3320,13 @@ async function syncAll() {
|
|
|
3257
3320
|
await scan();
|
|
3258
3321
|
const plans = getAll();
|
|
3259
3322
|
console.log(`[agendex] Found ${plans.length} plans. Syncing to cloud...`);
|
|
3323
|
+
const cache = force ? {} : loadSyncCache();
|
|
3324
|
+
const activePlanIds = new Set;
|
|
3260
3325
|
let synced = 0;
|
|
3326
|
+
let skipped = 0;
|
|
3261
3327
|
let failed = 0;
|
|
3262
3328
|
for (const plan of plans) {
|
|
3329
|
+
activePlanIds.add(plan.id);
|
|
3263
3330
|
const payload = {
|
|
3264
3331
|
localPlanId: plan.id,
|
|
3265
3332
|
agent: plan.agent,
|
|
@@ -3268,27 +3335,40 @@ async function syncAll() {
|
|
|
3268
3335
|
format: plan.format,
|
|
3269
3336
|
filePath: plan.filePath,
|
|
3270
3337
|
workspace: plan.workspace,
|
|
3271
|
-
metadata: plan.metadata
|
|
3338
|
+
metadata: plan.metadata,
|
|
3339
|
+
createdAt: plan.createdAt.getTime(),
|
|
3340
|
+
updatedAt: plan.updatedAt.getTime()
|
|
3272
3341
|
};
|
|
3342
|
+
const hash = computePayloadHash(payload);
|
|
3343
|
+
if (!force && cache[plan.id] === hash) {
|
|
3344
|
+
skipped++;
|
|
3345
|
+
continue;
|
|
3346
|
+
}
|
|
3273
3347
|
const result = await syncPlan(payload);
|
|
3274
3348
|
if (result.ok) {
|
|
3275
3349
|
synced++;
|
|
3350
|
+
cache[plan.id] = hash;
|
|
3276
3351
|
} else {
|
|
3277
3352
|
failed++;
|
|
3278
3353
|
console.error(`[agendex] Failed to sync "${plan.title}": ${result.error}`);
|
|
3279
3354
|
}
|
|
3280
3355
|
}
|
|
3281
|
-
|
|
3356
|
+
for (const id of Object.keys(cache)) {
|
|
3357
|
+
if (!activePlanIds.has(id))
|
|
3358
|
+
delete cache[id];
|
|
3359
|
+
}
|
|
3360
|
+
saveSyncCache(cache, { replace: true });
|
|
3361
|
+
console.log(`[agendex] Sync complete: ${synced} synced, ${skipped} unchanged, ${failed} failed`);
|
|
3282
3362
|
}
|
|
3283
3363
|
|
|
3284
3364
|
// src/version.ts
|
|
3285
|
-
import { existsSync as
|
|
3365
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
|
|
3286
3366
|
import { tmpdir } from "node:os";
|
|
3287
|
-
import { join as
|
|
3367
|
+
import { join as join12 } from "node:path";
|
|
3288
3368
|
// package.json
|
|
3289
3369
|
var package_default = {
|
|
3290
3370
|
name: "agendex-cli",
|
|
3291
|
-
version: "0.8.
|
|
3371
|
+
version: "0.8.5",
|
|
3292
3372
|
description: "Agendex CLI for login, sync, and daemon workflows",
|
|
3293
3373
|
homepage: "https://github.com/Tyru5/Agendex#readme",
|
|
3294
3374
|
repository: {
|
|
@@ -3332,14 +3412,14 @@ var package_default = {
|
|
|
3332
3412
|
|
|
3333
3413
|
// src/version.ts
|
|
3334
3414
|
var CLI_VERSION = package_default.version;
|
|
3335
|
-
var CACHE_FILE = process.env.AGENDEX_UPDATE_CACHE_FILE ??
|
|
3415
|
+
var CACHE_FILE = process.env.AGENDEX_UPDATE_CACHE_FILE ?? join12(tmpdir(), ".agendex-update-cache.json");
|
|
3336
3416
|
var CACHE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
3337
3417
|
var UPDATE_URL = process.env.AGENDEX_UPDATE_URL ?? "https://registry.npmjs.org/agendex-cli/latest";
|
|
3338
3418
|
function readCache(current) {
|
|
3339
3419
|
try {
|
|
3340
|
-
if (!
|
|
3420
|
+
if (!existsSync8(CACHE_FILE))
|
|
3341
3421
|
return null;
|
|
3342
|
-
const { result, ts } = JSON.parse(
|
|
3422
|
+
const { result, ts } = JSON.parse(readFileSync6(CACHE_FILE, "utf8"));
|
|
3343
3423
|
if (Date.now() - ts > CACHE_TTL_MS)
|
|
3344
3424
|
return null;
|
|
3345
3425
|
return normalizeResult(result, current);
|
|
@@ -3349,7 +3429,7 @@ function readCache(current) {
|
|
|
3349
3429
|
}
|
|
3350
3430
|
function writeCache(result) {
|
|
3351
3431
|
try {
|
|
3352
|
-
|
|
3432
|
+
writeFileSync4(CACHE_FILE, JSON.stringify({ result, ts: Date.now() }));
|
|
3353
3433
|
} catch {}
|
|
3354
3434
|
}
|
|
3355
3435
|
async function checkForUpdate() {
|
|
@@ -3489,7 +3569,8 @@ async function main() {
|
|
|
3489
3569
|
return 0;
|
|
3490
3570
|
}
|
|
3491
3571
|
case "sync": {
|
|
3492
|
-
|
|
3572
|
+
const force = args.includes("--force");
|
|
3573
|
+
await syncAll(force);
|
|
3493
3574
|
return 0;
|
|
3494
3575
|
}
|
|
3495
3576
|
case "cleanup": {
|
|
@@ -3622,7 +3703,8 @@ Usage:
|
|
|
3622
3703
|
agendex login --url <url> Login to a self-hosted instance
|
|
3623
3704
|
agendex logout Clear stored cloud token
|
|
3624
3705
|
agendex configure Select which agents/adapters to index
|
|
3625
|
-
agendex sync One-shot scan + sync to cloud
|
|
3706
|
+
agendex sync One-shot scan + sync to cloud (skips unchanged plans)
|
|
3707
|
+
agendex sync --force Re-sync all plans, ignoring cache
|
|
3626
3708
|
agendex cleanup Interactively remove cloud daemons
|
|
3627
3709
|
agendex cleanup --stale Auto-remove all stale daemons
|
|
3628
3710
|
agendex status Show current config state + daemon status
|