harlay 0.4.0 → 0.4.2
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/app/dist/main.cjs +71 -10
- package/dist/harlay.mjs +3 -3
- package/package.json +3 -2
package/app/dist/main.cjs
CHANGED
|
@@ -3195,24 +3195,85 @@ function assessHealth(s, context) {
|
|
|
3195
3195
|
|
|
3196
3196
|
// ../../packages/core/src/webchat.ts
|
|
3197
3197
|
var import_node_path19 = require("node:path");
|
|
3198
|
+
var LIMIT_NOTICES = [
|
|
3199
|
+
// claude.ai
|
|
3200
|
+
"Usage limit reached",
|
|
3201
|
+
"You've reached your session limit",
|
|
3202
|
+
"You've reached your weekly limit",
|
|
3203
|
+
"You've reached your weekly Cowork limit",
|
|
3204
|
+
"You've reached your usage limit",
|
|
3205
|
+
"You've reached your plan's usage limit",
|
|
3206
|
+
"You've reached your monthly spend limit",
|
|
3207
|
+
"You've reached your * limit. Get more usage",
|
|
3208
|
+
"Reached your session limit",
|
|
3209
|
+
"Reached your weekly limit",
|
|
3210
|
+
"Reached your weekly Cowork limit",
|
|
3211
|
+
"Reached your usage limit",
|
|
3212
|
+
"Reached your * limit$",
|
|
3213
|
+
"* limit reached$",
|
|
3214
|
+
"Out of free messages",
|
|
3215
|
+
"You hit your message limit",
|
|
3216
|
+
"You hit your 5-hour message limit",
|
|
3217
|
+
"You hit your weekly message limit",
|
|
3218
|
+
"You hit your weekly * limit",
|
|
3219
|
+
"You hit your usage limit",
|
|
3220
|
+
"You hit the Free plan's",
|
|
3221
|
+
"You just hit your free message limit",
|
|
3222
|
+
"You've hit your limit for Claude messages",
|
|
3223
|
+
"You've hit your session limit",
|
|
3224
|
+
"You've hit your weekly limit",
|
|
3225
|
+
"You've hit your usage limit",
|
|
3226
|
+
"You've hit your monthly spend limit",
|
|
3227
|
+
"Your usage limit is still exceeded",
|
|
3228
|
+
"Paused: Your usage limit",
|
|
3229
|
+
"Limit hit. Your week resets",
|
|
3230
|
+
"Limit hit. You can send new messages",
|
|
3231
|
+
"Try again after your * resets",
|
|
3232
|
+
"Send a message after your * resets",
|
|
3233
|
+
"Wait for your * to reset",
|
|
3234
|
+
"Your * resets * Try again then",
|
|
3235
|
+
"Your * resets * Send a message then",
|
|
3236
|
+
// ChatGPT, signed out
|
|
3237
|
+
"You have reached the anonymous message limit",
|
|
3238
|
+
// ChatGPT, signed in ("You've hit your usage limit", above, is ChatGPT's too). The server's own 429 message, which
|
|
3239
|
+
// ChatGPT itself spots by this start; the model cap; the out-of-messages panel's titles; Work's limit.
|
|
3240
|
+
"You've reached our limit of messages",
|
|
3241
|
+
"You've reached our limits of messages",
|
|
3242
|
+
"You've reached the current usage cap for",
|
|
3243
|
+
"You're out of messages with",
|
|
3244
|
+
"You've reached a limit$",
|
|
3245
|
+
"You've reached your Work limit"
|
|
3246
|
+
];
|
|
3198
3247
|
function isLimitText(s) {
|
|
3199
|
-
const
|
|
3200
|
-
if (
|
|
3201
|
-
|
|
3202
|
-
|
|
3248
|
+
const self = isLimitText;
|
|
3249
|
+
if (!self.patterns) {
|
|
3250
|
+
self.patterns = (self.notices ?? LIMIT_NOTICES).map((n) => {
|
|
3251
|
+
const whole2 = n.endsWith("$");
|
|
3252
|
+
const body = (whole2 ? n.slice(0, -1) : n).replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".{1,60}?");
|
|
3253
|
+
return new RegExp(`^${body}${whole2 ? "$" : ""}`, "i");
|
|
3254
|
+
});
|
|
3255
|
+
}
|
|
3256
|
+
const t = s.replace(/[’‘]/g, "'").replace(/\s+/g, " ").trim();
|
|
3257
|
+
if (/\b(?:upload|image|file|length|storage|project|artifact|translation|read aloud)\b/i.test(t)) return false;
|
|
3258
|
+
return t.length > 0 && t.length <= 300 && self.patterns.some((p) => p.test(t));
|
|
3203
3259
|
}
|
|
3260
|
+
var WEEKDAYS = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"];
|
|
3204
3261
|
function limitResetsAt(s, now) {
|
|
3205
3262
|
const inHours = /\bin (\d+) hours?\b/i.exec(s);
|
|
3206
3263
|
if (inHours) return new Date(now.getTime() + Number(inHours[1]) * 36e5);
|
|
3207
3264
|
const inMinutes = /\bin (\d+) min(?:ute)?s?\b/i.exec(s);
|
|
3208
3265
|
if (inMinutes) return new Date(now.getTime() + Number(inMinutes[1]) * 6e4);
|
|
3209
|
-
const m = /\b(\d{1,2})(?::(\d{2}))?\s*(am|pm)\b/i.exec(s);
|
|
3266
|
+
const m = /\b(\d{1,2})(?::(\d{2}))?\s*(am|pm)\b/i.exec(s) ?? /\b(\d{1,2}):(\d{2})\b()/.exec(s);
|
|
3210
3267
|
if (!m) return null;
|
|
3211
|
-
let h = Number(m[1])
|
|
3212
|
-
if (m[3].toLowerCase() === "pm"
|
|
3268
|
+
let h = Number(m[1]);
|
|
3269
|
+
if (m[3]) h = h % 12 + (m[3].toLowerCase() === "pm" ? 12 : 0);
|
|
3213
3270
|
const at = new Date(now);
|
|
3214
3271
|
at.setHours(h, m[2] ? Number(m[2]) : 0, 0, 0);
|
|
3215
|
-
|
|
3272
|
+
const day = /\b(sun|mon|tue|wed|thu|fri|sat)[a-z]*\b/i.exec(s);
|
|
3273
|
+
if (day) {
|
|
3274
|
+
at.setDate(at.getDate() + (WEEKDAYS.indexOf(day[1].toLowerCase()) - at.getDay() + 7) % 7);
|
|
3275
|
+
if (at.getTime() <= now.getTime()) at.setDate(at.getDate() + 7);
|
|
3276
|
+
} else if (at.getTime() <= now.getTime()) at.setDate(at.getDate() + 1);
|
|
3216
3277
|
return at;
|
|
3217
3278
|
}
|
|
3218
3279
|
function webChatTitle(c) {
|
|
@@ -3732,9 +3793,8 @@ function scanPage(root, ax, watch3, isLimit) {
|
|
|
3732
3793
|
for (const k of ax.kids(n)) walk(k, depth + 1);
|
|
3733
3794
|
};
|
|
3734
3795
|
walk(root, 0);
|
|
3735
|
-
const NOTICE = /^(?:you['’]?ve|you have|you['’]re|you are|usage limit|message limit|rate limit|limit reached)\b/i;
|
|
3736
3796
|
const tail2 = watch3 ? last ? message(last.n, last.h) : void 0 : turns.at(-1);
|
|
3737
|
-
if (tail2 && tail2.who === "agent" &&
|
|
3797
|
+
if (tail2 && tail2.who === "agent" && isLimit(tail2.text)) {
|
|
3738
3798
|
if (!limit) limit = tail2.text;
|
|
3739
3799
|
if (!watch3) turns.pop();
|
|
3740
3800
|
}
|
|
@@ -3775,6 +3835,7 @@ ObjC.bindFunction('AXUIElementCopyAttributeValue', ['int', ['id', 'id', 'id*']])
|
|
|
3775
3835
|
ObjC.bindFunction('AXUIElementSetAttributeValue', ['int', ['id', 'id', 'id']]);
|
|
3776
3836
|
ObjC.bindFunction('getppid', ['int', []]);
|
|
3777
3837
|
const isLimitText = ${isLimitText.toString()};
|
|
3838
|
+
isLimitText.notices = ${JSON.stringify(LIMIT_NOTICES)};
|
|
3778
3839
|
const scanPage = ${scanPage.toString()};
|
|
3779
3840
|
function get(el, name) { const r = Ref(); return $.AXUIElementCopyAttributeValue(el, name, r) === 0 ? r[0] : null; }
|
|
3780
3841
|
function str(v) { if (!v) return null; try { const s = ObjC.unwrap(v); return typeof s === 'string' ? s : null; } catch (e) { return null; } }
|
package/dist/harlay.mjs
CHANGED
|
@@ -2479,7 +2479,8 @@ function harlayDataDir(home = homedir3()) {
|
|
|
2479
2479
|
return join12(home, "Library", "Application Support", "Harlay");
|
|
2480
2480
|
}
|
|
2481
2481
|
async function uninstallApp(opts) {
|
|
2482
|
-
|
|
2482
|
+
const app = join12(opts.dir, "Harlay.app");
|
|
2483
|
+
if (existsSync2(app)) await (opts.quit ?? quitRunningApp)();
|
|
2483
2484
|
try {
|
|
2484
2485
|
await removeBridge(opts.home, harlayDataDir(opts.home));
|
|
2485
2486
|
opts.log("Restored your Claude Code status line settings.\n");
|
|
@@ -2487,7 +2488,6 @@ async function uninstallApp(opts) {
|
|
|
2487
2488
|
opts.log(`Left ~/.claude/settings.json unchanged: ${e instanceof Error ? e.message : String(e)}
|
|
2488
2489
|
`);
|
|
2489
2490
|
}
|
|
2490
|
-
const app = join12(opts.dir, "Harlay.app");
|
|
2491
2491
|
if (!existsSync2(app)) {
|
|
2492
2492
|
opts.log(`No Harlay.app in ${opts.dir}.
|
|
2493
2493
|
`);
|
|
@@ -2553,7 +2553,7 @@ async function installApp(opts) {
|
|
|
2553
2553
|
}
|
|
2554
2554
|
|
|
2555
2555
|
// src/main.ts
|
|
2556
|
-
var VERSION = "0.4.
|
|
2556
|
+
var VERSION = "0.4.2";
|
|
2557
2557
|
var HELP = `harlay ${VERSION} \u2014 hand off a stalled AI coding session to another agent
|
|
2558
2558
|
|
|
2559
2559
|
Usage:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "harlay",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "Move a coding task between AI agents (Claude Code, Codex, Cursor) when one stops. macOS desktop app and CLI.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"type": "module",
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
},
|
|
20
20
|
"scripts": {
|
|
21
21
|
"build": "esbuild src/bin.ts --bundle --platform=node --format=esm --target=node22 --external:@electron/packager --outfile=dist/harlay.mjs --banner:js='#!/usr/bin/env node'",
|
|
22
|
-
"prepack": "npm run build && node scripts/bundle-app.mjs"
|
|
22
|
+
"prepack": "npm run build && node scripts/bundle-app.mjs",
|
|
23
|
+
"postpack": "node -e \"require('node:fs').rmSync('app',{recursive:true,force:true})\""
|
|
23
24
|
},
|
|
24
25
|
"dependencies": {
|
|
25
26
|
"@electron/packager": "^20.3.0"
|