@q-agent/agent 0.1.18 → 0.1.19

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@q-agent/agent",
3
- "version": "0.1.18",
3
+ "version": "0.1.19",
4
4
  "description": "Q-Agent Local Agent — claims execution jobs from the Q-Agent server and runs Playwright locally, so manual login/MFA happens on the user's own machine and session credentials never leave it.",
5
5
  "license": "UNLICENSED",
6
6
  "bin": {
@@ -75,36 +75,44 @@ async function waitForCDP(port, timeoutMs) {
75
75
  return false;
76
76
  }
77
77
 
78
- // Replay saved sessionStorage into the running Chrome so MSAL/SPA apps that keep
79
- // their token in sessionStorage are authenticated. Playwright is OPTIONAL: if it
80
- // can't be required (the API container ships none) or no path was given, this is
81
- // a no-op. Returns the connected Playwright browser (kept alive so the init-script
82
- // registration survives) or null.
83
- async function replaySessionStorage(port) {
78
+ // Load the saved sessionStorage map ({origin: {k:v}}), or null if unusable.
79
+ function loadSessionStorage() {
84
80
  if (!sessionStoragePath) return null;
85
- let byOrigin;
86
- try { byOrigin = JSON.parse(fs.readFileSync(sessionStoragePath, 'utf-8')); }
87
- catch { return null; }
88
- if (!byOrigin || typeof byOrigin !== 'object' || !Object.keys(byOrigin).length) return null;
89
- let chromium;
90
- try { ({ chromium } = require('playwright')); } catch { return null; }
91
81
  try {
92
- const browser = await chromium.connectOverCDP(`http://127.0.0.1:${port}`);
93
- const ctx = browser.contexts()[0];
94
- if (ctx) {
95
- await ctx.addInitScript((data) => {
96
- try {
97
- const o = data && data[location.origin];
98
- if (o) for (const k of Object.keys(o)) window.sessionStorage.setItem(k, o[k]);
99
- } catch (e) {}
100
- }, byOrigin);
101
- console.error('authoring_browser: sessionStorage replay armed for', Object.keys(byOrigin).join(','));
102
- }
103
- return browser;
82
+ const m = JSON.parse(fs.readFileSync(sessionStoragePath, 'utf-8'));
83
+ return m && typeof m === 'object' && Object.keys(m).length ? m : null;
84
+ } catch { return null; }
85
+ }
86
+
87
+ // Require Playwright if available (agent side); null in the API container.
88
+ function tryPlaywright() {
89
+ try { return require('playwright').chromium; } catch { return null; }
90
+ }
91
+
92
+ // Arm sessionStorage replay and navigate the VISIBLE tab to baseUrl WITH the
93
+ // token restored, so MSAL/SPA apps load authenticated. Critical ordering: the
94
+ // init script must be registered BEFORE the first navigation to the app (that's
95
+ // why Chrome is launched at about:blank, not baseUrl). Returns the connected
96
+ // Playwright browser (kept alive so the init-script registration + tab survive).
97
+ async function armAuthAndNavigate(chromium, port, byOrigin) {
98
+ const browser = await chromium.connectOverCDP(`http://127.0.0.1:${port}`);
99
+ const ctx = browser.contexts()[0];
100
+ if (!ctx) return browser;
101
+ await ctx.addInitScript((data) => {
102
+ try {
103
+ const o = data && data[location.origin];
104
+ if (o) for (const k of Object.keys(o)) window.sessionStorage.setItem(k, o[k]);
105
+ } catch (e) {}
106
+ }, byOrigin);
107
+ const page = ctx.pages()[0] || (await ctx.newPage());
108
+ try {
109
+ await page.goto(baseUrl, { waitUntil: 'domcontentloaded', timeout: 45000 });
110
+ await page.waitForLoadState('networkidle', { timeout: 15000 }).catch(() => {});
104
111
  } catch (e) {
105
- console.error('authoring_browser: sessionStorage replay failed:', e && e.message);
106
- return null;
112
+ console.error('authoring_browser: navigate after replay failed:', e && e.message);
107
113
  }
114
+ console.error('authoring_browser: sessionStorage replay armed for', Object.keys(byOrigin).join(','));
115
+ return browser;
108
116
  }
109
117
 
110
118
  (async () => {
@@ -127,14 +135,22 @@ async function replaySessionStorage(port) {
127
135
  }
128
136
  } catch (e) { console.error('pref seed failed:', e && e.message); }
129
137
 
138
+ // If we can replay sessionStorage (agent side: Playwright resolvable + a saved
139
+ // map), launch to about:blank and let Playwright navigate AFTER arming the init
140
+ // script — so the app never loads before the token is restored. Otherwise launch
141
+ // straight to baseUrl (profile-only / API container).
142
+ const byOrigin = loadSessionStorage();
143
+ const chromium = byOrigin ? tryPlaywright() : null;
144
+ const launchUrl = chromium ? 'about:blank' : baseUrl;
145
+
130
146
  const child = spawn(exe, [
131
147
  `--remote-debugging-port=${PORT}`,
132
148
  `--user-data-dir=${profileDir}`,
133
149
  '--no-first-run', '--no-default-browser-check', '--new-window',
134
150
  ...containerFlags(),
135
- baseUrl,
151
+ launchUrl,
136
152
  ], { detached: false, stdio: 'ignore' });
137
- console.error('authoring_browser launched:', exe, 'port', PORT);
153
+ console.error('authoring_browser launched:', exe, 'port', PORT, 'replay:', Boolean(chromium));
138
154
 
139
155
  if (!(await waitForCDP(PORT, 20000))) {
140
156
  console.error('authoring_browser: CDP endpoint never came up on port', PORT);
@@ -142,9 +158,13 @@ async function replaySessionStorage(port) {
142
158
  process.exit(1);
143
159
  }
144
160
 
145
- // Arm sessionStorage replay (best-effort) BEFORE signalling readiness, so the
146
- // token is restored before browser-harness navigates.
147
- const pw = await replaySessionStorage(PORT);
161
+ // Arm sessionStorage replay + navigate the visible tab authenticated, BEFORE
162
+ // signalling readiness so browser-harness attaches to a logged-in tab.
163
+ let pw = null;
164
+ if (chromium) {
165
+ try { pw = await armAuthAndNavigate(chromium, PORT, byOrigin); }
166
+ catch (e) { console.error('authoring_browser: replay failed:', e && e.message); }
167
+ }
148
168
 
149
169
  // Signal readiness on stdout so the parent proceeds. The daemon resolves
150
170
  // BU_CDP_URL to the WS.