halo-agent 2.2.3 → 2.2.4

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.
Files changed (4) hide show
  1. package/browser.js +34 -1
  2. package/index.js +1 -1
  3. package/package.json +1 -1
  4. package/ui.js +21 -1
package/browser.js CHANGED
@@ -135,7 +135,7 @@ async function connectToChrome(retries = 10, opts = {}) {
135
135
  ctx = fresh.contexts()[0] || await fresh.newContext();
136
136
  conn.context = ctx;
137
137
  }
138
- const page = await ctx.newPage();
138
+ let page = await ctx.newPage();
139
139
  if (url) {
140
140
  try {
141
141
  await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 60000 });
@@ -153,6 +153,39 @@ async function connectToChrome(retries = 10, opts = {}) {
153
153
  await page.waitForTimeout(8000);
154
154
  }
155
155
  }
156
+
157
+ // VERIFY-AFTER-GOTO. connectOverCDP against the user's REAL Chrome
158
+ // has a tab-handle race: ctx.newPage() may hand back a different
159
+ // tab than the one that actually navigated (e.g. Chrome already
160
+ // had a restored New Tab open). Symptom: the agent then scans
161
+ // about:blank / chrome://newtab forever while the target URL is
162
+ // loaded in a sibling tab. Self-correct: if our handle isn't on
163
+ // the target host, find the tab that IS and adopt it.
164
+ try {
165
+ const here = page.url();
166
+ const targetHost = new URL(url).hostname;
167
+ const onTarget = (u) => { try { return new URL(u).hostname === targetHost; } catch { return false; } };
168
+ if (!onTarget(here)) {
169
+ // Look across all pages in this context for the one that navigated.
170
+ const pages = ctx.pages();
171
+ const match = pages.find((p) => onTarget(p.url()));
172
+ if (match && match !== page) {
173
+ console.warn(`[halo-agent] newPage handle was on "${here.slice(0, 40)}" but target loaded in another tab — adopting it.`);
174
+ // Close the stray blank tab we opened so it doesn't litter.
175
+ if (/^about:blank$|^chrome:\/\/newtab/i.test(here)) {
176
+ try { await page.close(); } catch {}
177
+ }
178
+ page = match;
179
+ await page.bringToFront().catch(() => {});
180
+ } else if (/^about:blank$|^chrome:\/\/newtab/i.test(here)) {
181
+ // Our tab is genuinely blank and nothing else navigated —
182
+ // the goto silently no-op'd. Retry the navigation on THIS tab.
183
+ console.warn('[halo-agent] Tab still blank after goto — retrying navigation on this handle...');
184
+ await page.goto(url, { waitUntil: 'commit', timeout: 30000 }).catch(() => {});
185
+ await page.waitForTimeout(4000);
186
+ }
187
+ }
188
+ } catch { /* best-effort self-correction; fall through */ }
156
189
  }
157
190
  return page;
158
191
  },
package/index.js CHANGED
@@ -479,7 +479,7 @@ async function runStart() {
479
479
 
480
480
  let version = '';
481
481
  try { version = require('./package.json').version; } catch {}
482
- ui.banner(version);
482
+ ui.bigBanner(version); // hero frame — the one you'd screenshot
483
483
 
484
484
  // The agent runs its own isolated Chrome instance (separate --user-data-dir
485
485
  // at ~/.halo-agent/chrome-profile) so it never collides with the user's
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "halo-agent",
3
- "version": "2.2.3",
3
+ "version": "2.2.4",
4
4
  "description": "HALO local apply agent — auto-fills job applications using your real Chrome session",
5
5
  "main": "index.js",
6
6
  "bin": {
package/ui.js CHANGED
@@ -73,6 +73,26 @@ function banner(version) {
73
73
  console.log('');
74
74
  }
75
75
 
76
+ // Big block-letter wordmark for the hero moment (`start`) — the frame you'd
77
+ // screenshot for a LinkedIn post. Five-row ASCII HALO in ember, tagline +
78
+ // version underneath. Falls back to the compact boxed banner when color is
79
+ // off (logs/CI) so the auto-start service log doesn't get a wall of blocks.
80
+ const HALO_ART = [
81
+ '██ ██ █████ ██ ██████ ',
82
+ '██ ██ ██ ██ ██ ██ ██ ',
83
+ '███████ ███████ ██ ██ ██ ',
84
+ '██ ██ ██ ██ ██ ██ ██ ',
85
+ '██ ██ ██ ██ ███████ ██████ ',
86
+ ];
87
+ function bigBanner(version) {
88
+ if (!useColor) { banner(version); return; }
89
+ const ver = version ? gray('· agent v' + version) : gray('· agent');
90
+ console.log('');
91
+ for (const row of HALO_ART) console.log(' ' + ember(row));
92
+ console.log(' ' + gray('auto-apply, in your own Chrome') + ' ' + ver);
93
+ console.log('');
94
+ }
95
+
76
96
  // Affordance helper: the obvious next command, rendered consistently so the
77
97
  // user always knows what to type next. Shown after pair/init/etc.
78
98
  function nextStep(cmd, note) {
@@ -123,5 +143,5 @@ function spinner(message) {
123
143
  module.exports = {
124
144
  bold, dim, red, green, yellow, blue, magenta, cyan, gray, ember,
125
145
  sym, success, fail, info, warn, muted, step,
126
- banner, spinner, nextStep, hint, commandTable, visLen,
146
+ banner, bigBanner, spinner, nextStep, hint, commandTable, visLen,
127
147
  };