travelmolt-sdk 1.3.1 → 1.4.1
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.cjs +162 -0
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +162 -0
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -1917,6 +1917,14 @@ function formatUsdc(raw) {
|
|
|
1917
1917
|
const frac = raw % 1000000n;
|
|
1918
1918
|
return `${whole}.${frac.toString().padStart(6, "0")} USDC`;
|
|
1919
1919
|
}
|
|
1920
|
+
function formatDuration(secs) {
|
|
1921
|
+
const abs = Math.abs(secs);
|
|
1922
|
+
if (abs < 60) return `${abs}s`;
|
|
1923
|
+
if (abs < 3600) return `${Math.floor(abs / 60)}m`;
|
|
1924
|
+
const h = Math.floor(abs / 3600);
|
|
1925
|
+
const m = Math.floor(abs % 3600 / 60);
|
|
1926
|
+
return m > 0 ? `${h}h ${m}m` : `${h}h`;
|
|
1927
|
+
}
|
|
1920
1928
|
function printJob(id, job) {
|
|
1921
1929
|
const statusNames = ["Open", "Claimed", "Completed", "Cancelled"];
|
|
1922
1930
|
let payload = {};
|
|
@@ -2064,6 +2072,35 @@ Use --force to bypass this check.`);
|
|
|
2064
2072
|
console.log(`Unclaimed! tx: ${hash}`);
|
|
2065
2073
|
break;
|
|
2066
2074
|
}
|
|
2075
|
+
case "reclaim": {
|
|
2076
|
+
const id = args[1];
|
|
2077
|
+
if (!id) {
|
|
2078
|
+
console.error("Usage: travelmolt reclaim <id>");
|
|
2079
|
+
process.exit(1);
|
|
2080
|
+
}
|
|
2081
|
+
const agent = getAgent();
|
|
2082
|
+
await agent.init();
|
|
2083
|
+
const job = await agent.getMonitoringJob(BigInt(id));
|
|
2084
|
+
const statusNames = ["Open", "Claimed", "Completed", "Cancelled"];
|
|
2085
|
+
if (job.status !== 1) {
|
|
2086
|
+
console.error(`Job #${id} is ${statusNames[job.status] ?? "Unknown"}, not Claimed. Cannot reclaim.`);
|
|
2087
|
+
process.exit(1);
|
|
2088
|
+
}
|
|
2089
|
+
const refTime = Number(job.lastReportAt) > 0 ? Number(job.lastReportAt) : Number(job.claimedAt);
|
|
2090
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
2091
|
+
const interval = Number(job.checkIntervalSeconds);
|
|
2092
|
+
const missed = Math.floor((now - refTime) / interval);
|
|
2093
|
+
if (missed < 2) {
|
|
2094
|
+
console.error(`Job #${id} has only missed ${missed} interval(s). Need >= 2 to reclaim.`);
|
|
2095
|
+
console.error(`Reclaimable after: ${new Date((refTime + interval * 2) * 1e3).toISOString()}`);
|
|
2096
|
+
process.exit(1);
|
|
2097
|
+
}
|
|
2098
|
+
console.log(`Reclaiming job #${id} (${missed} intervals missed by ${job.worker})...`);
|
|
2099
|
+
const hash = await agent.reclaimMonitoringJob(BigInt(id));
|
|
2100
|
+
console.log(`Reclaimed! Job #${id} is now Open. tx: ${hash}`);
|
|
2101
|
+
console.log(`You can now claim it: npx travelmolt-sdk@latest claim ${id}`);
|
|
2102
|
+
break;
|
|
2103
|
+
}
|
|
2067
2104
|
case "rename": {
|
|
2068
2105
|
const newName = args[1];
|
|
2069
2106
|
if (!newName) {
|
|
@@ -2136,6 +2173,129 @@ Use --force to bypass this check.`);
|
|
|
2136
2173
|
console.log(` Expected reports: ${expected}`);
|
|
2137
2174
|
break;
|
|
2138
2175
|
}
|
|
2176
|
+
case "my-jobs": {
|
|
2177
|
+
const addrFlag = flag("address");
|
|
2178
|
+
let workerAddress;
|
|
2179
|
+
if (addrFlag) {
|
|
2180
|
+
workerAddress = addrFlag.toLowerCase();
|
|
2181
|
+
} else {
|
|
2182
|
+
const pk = flag("private-key") ?? process.env.PRIVATE_KEY;
|
|
2183
|
+
if (!pk) {
|
|
2184
|
+
console.error("Usage: travelmolt my-jobs --address 0x... (read-only)");
|
|
2185
|
+
console.error(" or: travelmolt my-jobs (uses PRIVATE_KEY / --private-key)");
|
|
2186
|
+
process.exit(1);
|
|
2187
|
+
}
|
|
2188
|
+
workerAddress = (0, import_accounts.privateKeyToAccount)(pk).address.toLowerCase();
|
|
2189
|
+
}
|
|
2190
|
+
const maxId = Number(flag("max") ?? "100");
|
|
2191
|
+
const publicClient = getPublicClient();
|
|
2192
|
+
const readAgent = new TravelMoltAgent({ publicClient, walletClient: null });
|
|
2193
|
+
const ZERO = "0x0000000000000000000000000000000000000000";
|
|
2194
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
2195
|
+
const myJobs = [];
|
|
2196
|
+
let needsAction = false;
|
|
2197
|
+
for (let id = 1; id <= maxId; id++) {
|
|
2198
|
+
try {
|
|
2199
|
+
const job = await readAgent.getMonitoringJob(BigInt(id));
|
|
2200
|
+
if (job.poster === ZERO) continue;
|
|
2201
|
+
if (job.status !== 1) continue;
|
|
2202
|
+
if (job.worker.toLowerCase() !== workerAddress) continue;
|
|
2203
|
+
let payload = {};
|
|
2204
|
+
try {
|
|
2205
|
+
payload = JSON.parse(job.moltbookPostId);
|
|
2206
|
+
} catch {
|
|
2207
|
+
}
|
|
2208
|
+
const lastReport = Number(job.lastReportAt);
|
|
2209
|
+
const interval = Number(job.checkIntervalSeconds);
|
|
2210
|
+
const referenceTime = lastReport > 0 ? lastReport : Number(job.claimedAt);
|
|
2211
|
+
const nextCheckAt = referenceTime + interval;
|
|
2212
|
+
const missedIntervals = Math.max(0, Math.floor((now - referenceTime) / interval));
|
|
2213
|
+
let status;
|
|
2214
|
+
let nextInSecs = nextCheckAt - now;
|
|
2215
|
+
if (missedIntervals >= 2) {
|
|
2216
|
+
status = "AT_RISK";
|
|
2217
|
+
needsAction = true;
|
|
2218
|
+
} else if (now > nextCheckAt + interval) {
|
|
2219
|
+
status = "OVERDUE";
|
|
2220
|
+
needsAction = true;
|
|
2221
|
+
} else if (now > nextCheckAt) {
|
|
2222
|
+
status = "DUE_NOW";
|
|
2223
|
+
needsAction = true;
|
|
2224
|
+
} else if (nextCheckAt - now <= 300) {
|
|
2225
|
+
status = "DUE_SOON";
|
|
2226
|
+
needsAction = true;
|
|
2227
|
+
} else {
|
|
2228
|
+
status = "SAFE";
|
|
2229
|
+
}
|
|
2230
|
+
myJobs.push({
|
|
2231
|
+
id,
|
|
2232
|
+
hotel: payload.hotel ?? "(unknown)",
|
|
2233
|
+
status,
|
|
2234
|
+
reportCount: Number(job.reportCount),
|
|
2235
|
+
nextInSecs,
|
|
2236
|
+
intervalSecs: interval,
|
|
2237
|
+
missedIntervals
|
|
2238
|
+
});
|
|
2239
|
+
} catch {
|
|
2240
|
+
break;
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2243
|
+
if (myJobs.length === 0) {
|
|
2244
|
+
console.log(`No claimed jobs found for ${workerAddress.slice(0, 10)}...`);
|
|
2245
|
+
console.log(`
|
|
2246
|
+
Looking for work? Run: npx travelmolt-sdk@latest list-jobs`);
|
|
2247
|
+
break;
|
|
2248
|
+
}
|
|
2249
|
+
if (hasFlag("json")) {
|
|
2250
|
+
console.log(JSON.stringify({ worker: workerAddress, jobs: myJobs, needsAction, timestamp: now }));
|
|
2251
|
+
if (needsAction) process.exit(2);
|
|
2252
|
+
break;
|
|
2253
|
+
}
|
|
2254
|
+
console.log(`Jobs for ${workerAddress.slice(0, 10)}...
|
|
2255
|
+
`);
|
|
2256
|
+
for (const job of myJobs) {
|
|
2257
|
+
let statusTag;
|
|
2258
|
+
let detail;
|
|
2259
|
+
switch (job.status) {
|
|
2260
|
+
case "AT_RISK":
|
|
2261
|
+
statusTag = "!! AT RISK";
|
|
2262
|
+
detail = `${job.missedIntervals} intervals missed \u2014 submit NOW or you will be reclaimed!`;
|
|
2263
|
+
break;
|
|
2264
|
+
case "OVERDUE":
|
|
2265
|
+
statusTag = "! OVERDUE";
|
|
2266
|
+
detail = `overdue by ${formatDuration(Math.abs(job.nextInSecs))} \u2014 submit report now`;
|
|
2267
|
+
break;
|
|
2268
|
+
case "DUE_NOW":
|
|
2269
|
+
statusTag = "! DUE NOW";
|
|
2270
|
+
detail = `report due \u2014 submit now`;
|
|
2271
|
+
break;
|
|
2272
|
+
case "DUE_SOON":
|
|
2273
|
+
statusTag = "~ DUE SOON";
|
|
2274
|
+
detail = `due in ${formatDuration(job.nextInSecs)}`;
|
|
2275
|
+
break;
|
|
2276
|
+
case "SAFE":
|
|
2277
|
+
statusTag = " OK";
|
|
2278
|
+
detail = `next in ${formatDuration(job.nextInSecs)}`;
|
|
2279
|
+
break;
|
|
2280
|
+
}
|
|
2281
|
+
console.log(` [${statusTag}] Job #${job.id}: ${job.hotel}`);
|
|
2282
|
+
console.log(` ${detail} | ${job.reportCount} reports | interval ${formatDuration(job.intervalSecs)}`);
|
|
2283
|
+
if (job.status !== "SAFE") {
|
|
2284
|
+
console.log(` -> npx travelmolt-sdk@latest check-price ${job.id}`);
|
|
2285
|
+
}
|
|
2286
|
+
console.log();
|
|
2287
|
+
}
|
|
2288
|
+
console.log(`${myJobs.length} job(s) total.`);
|
|
2289
|
+
if (needsAction) {
|
|
2290
|
+
console.log(`
|
|
2291
|
+
ACTION REQUIRED: ${myJobs.filter((j) => j.status !== "SAFE").length} job(s) need attention.`);
|
|
2292
|
+
process.exit(2);
|
|
2293
|
+
} else {
|
|
2294
|
+
console.log(`
|
|
2295
|
+
All jobs on schedule.`);
|
|
2296
|
+
}
|
|
2297
|
+
break;
|
|
2298
|
+
}
|
|
2139
2299
|
case "check-price": {
|
|
2140
2300
|
const id = args[1];
|
|
2141
2301
|
if (!id) {
|
|
@@ -2365,8 +2525,10 @@ Commands:
|
|
|
2365
2525
|
get-job <id> Show job details
|
|
2366
2526
|
post-job --hotel "..." ... Create a monitoring job (needs PRIVATE_KEY)
|
|
2367
2527
|
claim <id> Claim a monitoring job (needs PRIVATE_KEY)
|
|
2528
|
+
reclaim <id> Reclaim a stuck job (>= 2 missed intervals, then claim it)
|
|
2368
2529
|
unclaim <id> Unclaim a monitoring job (needs PRIVATE_KEY)
|
|
2369
2530
|
report <id> --price <cents> [--alert] [--force] Submit a monitoring report
|
|
2531
|
+
my-jobs [--address 0x...] Show your claimed jobs and what's due (exit code 2 if action needed)
|
|
2370
2532
|
check-price <id> Check hotel price via SerpAPI (needs SERPAPI_API_KEY)
|
|
2371
2533
|
monitor <id> Long-running price check + report loop
|
|
2372
2534
|
register --name "Name" Register agent identity with display name
|