pi-harness-runtime 0.3.2-beta.0 → 0.3.2-beta.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/README.md CHANGED
@@ -1,13 +1,11 @@
1
1
  # Pi Harness Runtime
2
2
 
3
3
  [![Beta](https://img.shields.io/badge/version-beta-orange?style=for-the-badge)](https://github.com/ManotLuijiu/pi-harness-runtime)
4
- [![npm downloads](https://img.shields.io/npm/dm/pi-harness-runtime?style=for-the-badge)](https://www.npmjs.com/package/pi-harness-runtime)
5
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=for-the-badge)](https://opensource.org/licenses/MIT)
6
5
  [![Platform](https://img.shields.io/badge/Platform-macOS%20|Linux-blue?style=for-the-badge)]()
7
6
 
8
- > ⚠️ **⚠️ Beta Notice — Not Production Ready ⚠️**
7
+ > ⚠️ **Beta Notice — Not Production Ready ⚠️**
9
8
  > This project is in active development. Core features work but E2E testing is not yet implemented.
10
- > **148+ downloads** and counting — we appreciate the interest! Help us test by filing issues.
11
9
 
12
10
  **Autonomous AI coding harness for pi: local token tracking + provider mirror + task orchestration.**
13
11
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-harness-runtime",
3
- "version": "0.3.2-beta.0",
3
+ "version": "0.3.2-beta.2",
4
4
  "description": "[BETA] Codex-style /usage status + autonomous coding harness for pi. Not production ready — expect breaking changes.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -9,7 +9,13 @@
9
9
  "release:patch": "standard-version --release-as patch",
10
10
  "release:minor": "standard-version --release-as minor",
11
11
  "release:major": "standard-version --release-as major",
12
- "release:dry-run": "standard-version --dry-run"
12
+ "release:dry-run": "standard-version --dry-run",
13
+ "auth:minimax": "bun packages/auth/src/run-minimax-auth.ts auth",
14
+ "auth:minimax:check": "bun packages/auth/src/run-minimax-auth.ts check",
15
+ "auth:minimax:scrape": "bun packages/auth/src/run-minimax-auth.ts scrape"
16
+ },
17
+ "bin": {
18
+ "harness-auth": "packages/auth/src/run-minimax-auth.ts"
13
19
  },
14
20
  "files": [
15
21
  "index.ts",
@@ -63,6 +69,7 @@
63
69
  },
64
70
  "devDependencies": {
65
71
  "@types/node": "^20.14.0",
72
+ "playwright": "^1.61.1",
66
73
  "standard-version": "^9.5.0"
67
74
  }
68
75
  }
@@ -0,0 +1,60 @@
1
+ # Auth Package
2
+
3
+ Safe browser authentication for pi-harness-runtime.
4
+
5
+ ## Purpose
6
+
7
+ Provides a secure way to authenticate with provider websites (like MiniMax) using a Playwright-managed browser profile. The human owns the authentication - the agent never sees credentials.
8
+
9
+ ## Security Rules
10
+
11
+ - Human logs in manually in the browser
12
+ - No username, password, raw cookies, or session tokens stored
13
+ - Only a safe status file is written
14
+ - Agent can only check if authentication succeeded
15
+
16
+ ## Files
17
+
18
+ - `src/minimax-browser-auth.ts` - Main auth module
19
+ - `src/run-minimax-auth.ts` - CLI runner
20
+
21
+ ## Usage
22
+
23
+ ```bash
24
+ # Authenticate (opens browser for manual login)
25
+ bun packages/auth/src/run-minimax-auth.ts auth
26
+
27
+ # Check authentication status
28
+ bun packages/auth/src/run-minimax-auth.ts check
29
+ ```
30
+
31
+ ## Storage
32
+
33
+ - **Browser profile**: `~/.pi-harness-runtime/browser-profiles/minimax/`
34
+ - **Status file**: `~/.pi-harness-runtime/auth/minimax-auth-status.json`
35
+
36
+ ## Status File Format
37
+
38
+ ```json
39
+ {
40
+ "provider": "minimax",
41
+ "authenticated": true,
42
+ "checked_at": "2026-07-04T08:00:00.000Z",
43
+ "page_url": "https://platform.minimax.io/console/usage",
44
+ "detected_text_sample": "Usage Plan Credits Reset"
45
+ }
46
+ ```
47
+
48
+ ## What is NOT stored
49
+
50
+ - Passwords
51
+ - Raw cookies
52
+ - Session tokens
53
+ - LocalStorage values
54
+ - API keys
55
+
56
+ ## Limitations
57
+
58
+ - Requires a display (headed browser mode)
59
+ - Works on Mac/Linux with display
60
+ - Cannot run in truly headless environments without Xvfb
@@ -0,0 +1,437 @@
1
+ /**
2
+ * minimax-browser-auth.ts
3
+ *
4
+ * MiniMax browser authentication using persistent Playwright profile.
5
+ *
6
+ * SECURITY RULES:
7
+ * - Human owns authentication. Agent never receives credentials.
8
+ * - No username, password, raw cookies, or session tokens stored.
9
+ * - Playwright persistent profile saves browser session automatically.
10
+ * - Profile is stored at ~/.pi-harness-runtime/browser-profiles/minimax/
11
+ */
12
+
13
+ import * as fs from "fs";
14
+ import * as path from "path";
15
+ import * as os from "os";
16
+ import { chromium, type BrowserContext } from "playwright";
17
+
18
+ export interface MinimaxAuthStatus {
19
+ provider: "minimax";
20
+ authenticated: boolean;
21
+ checked_at: string;
22
+ page_url: string;
23
+ detected_text_sample: string | null;
24
+ profile_path: string;
25
+ error_message?: string;
26
+ }
27
+
28
+ export interface MinimaxBrowserAuthConfig {
29
+ profilePath?: string;
30
+ statusPath?: string;
31
+ targetUrl?: string;
32
+ }
33
+
34
+ const DEFAULT_USAGE_KEYWORDS = [
35
+ "Usage",
36
+ "Token",
37
+ "Plan",
38
+ "Credits",
39
+ "Reset",
40
+ "Limit",
41
+ "5h",
42
+ "Weekly",
43
+ "quota",
44
+ "used",
45
+ "coding_plan",
46
+ "subscription",
47
+ ];
48
+
49
+ export function getRuntimeDir(): string {
50
+ return path.join(os.homedir(), ".pi-harness-runtime");
51
+ }
52
+
53
+ export function getProfileDir(): string {
54
+ return path.join(getRuntimeDir(), "browser-profiles", "minimax");
55
+ }
56
+
57
+ export function getStatusPath(): string {
58
+ return path.join(getRuntimeDir(), "auth", "minimax-auth-status.json");
59
+ }
60
+
61
+ function ensureDirs(config: MinimaxBrowserAuthConfig): void {
62
+ const profileDir = config.profilePath ?? getProfileDir();
63
+ const statusPath = config.statusPath ?? getStatusPath();
64
+ fs.mkdirSync(path.dirname(profileDir), { recursive: true });
65
+ fs.mkdirSync(path.dirname(statusPath), { recursive: true });
66
+ }
67
+
68
+ export function saveAuthStatus(
69
+ status: MinimaxAuthStatus,
70
+ config?: MinimaxBrowserAuthConfig,
71
+ ): void {
72
+ const statusPath = config?.statusPath ?? getStatusPath();
73
+ ensureDirs(config ?? {});
74
+ fs.writeFileSync(statusPath, JSON.stringify(status, null, 2));
75
+ }
76
+
77
+ export function detectUsagePage(bodyText: string): {
78
+ detected: boolean;
79
+ sample: string | null;
80
+ } {
81
+ const words = DEFAULT_USAGE_KEYWORDS.map((k) => k.toLowerCase());
82
+ const lowerText = bodyText.toLowerCase();
83
+ const foundKeywords = words.filter((w) => lowerText.includes(w));
84
+ const detected = foundKeywords.length >= 2;
85
+ const sample = detected
86
+ ? bodyText.substring(0, 200).replace(/\s+/g, " ").trim()
87
+ : null;
88
+ return { detected, sample };
89
+ }
90
+
91
+ /**
92
+ * Launch persistent browser - profile is saved automatically
93
+ * Reusing the profile means user stays logged in
94
+ */
95
+ export async function authenticateWithPersistentBrowser(
96
+ config: MinimaxBrowserAuthConfig = {},
97
+ ): Promise<MinimaxAuthStatus> {
98
+ const profileDir = config.profilePath ?? getProfileDir();
99
+ const statusPath = config.statusPath ?? getStatusPath();
100
+ const targetUrl =
101
+ config.targetUrl ?? "https://platform.minimax.io/console/usage";
102
+
103
+ ensureDirs(config);
104
+
105
+ console.log("=".repeat(60));
106
+ console.log("🔐 MiniMax Browser Authentication (Persistent Mode)");
107
+ console.log("=".repeat(60));
108
+ console.log("");
109
+ console.log("SECURITY: Human owns authentication.");
110
+ console.log("- Login once, profile is saved for reuse");
111
+ console.log("");
112
+ console.log(`Profile directory: ${profileDir}`);
113
+ console.log("");
114
+
115
+ let context: BrowserContext | null = null;
116
+
117
+ try {
118
+ // Check if profile exists (user already logged in)
119
+ const isFirstRun = !fs.existsSync(path.join(profileDir, "SingletonLock"));
120
+
121
+ // Launch persistent context - profile auto-saves!
122
+ console.log("🚀 Launching persistent browser...");
123
+ if (isFirstRun) {
124
+ console.log(" First run - creating new profile");
125
+ } else {
126
+ console.log(" Reusing existing profile (you stay logged in)");
127
+ }
128
+ console.log("");
129
+
130
+ context = await chromium.launchPersistentContext(profileDir, {
131
+ headless: false,
132
+ chromiumSandbox: false,
133
+ args: ["--no-first-run", "--no-default-browser-check"],
134
+ viewport: { width: 1280, height: 800 },
135
+ });
136
+
137
+ const page = context.pages()[0] ?? (await context.newPage());
138
+
139
+ // Navigate to MiniMax
140
+ console.log("🖥️ Navigating to MiniMax...");
141
+ await page.goto(targetUrl, {
142
+ waitUntil: "domcontentloaded",
143
+ timeout: 15000,
144
+ });
145
+ await page.waitForTimeout(2000);
146
+
147
+ const initialUrl = page.url();
148
+ console.log(` Current URL: ${initialUrl}`);
149
+ console.log("");
150
+
151
+ // If redirected to login, wait for user to log in
152
+ if (initialUrl.includes("login") || initialUrl.includes("unified-login")) {
153
+ console.log(
154
+ "🔓 Login required - please log in to MiniMax in the browser window",
155
+ );
156
+ console.log(" (Profile will auto-save when you navigate)");
157
+ console.log("");
158
+ console.log("⏳ Waiting for you to log in and navigate to usage page...");
159
+ console.log(" Press Enter when done, or wait 60 seconds...");
160
+ console.log("");
161
+
162
+ // Wait for user input or timeout
163
+ await Promise.race([
164
+ new Promise<void>((resolve) => {
165
+ process.stdin.once("data", () => {
166
+ console.log("✅ Resuming...");
167
+ resolve();
168
+ });
169
+ }),
170
+ new Promise<void>((resolve) =>
171
+ setTimeout(() => {
172
+ console.log("⏰ Timeout - checking current state...");
173
+ resolve();
174
+ }, 60000),
175
+ ),
176
+ ]);
177
+ }
178
+
179
+ // Extract usage data
180
+ console.log("");
181
+ console.log("📊 Extracting usage data...");
182
+
183
+ await page.goto(targetUrl, { waitUntil: "networkidle", timeout: 30000 });
184
+ await page.waitForTimeout(2000);
185
+
186
+ const url = page.url();
187
+ const bodyText = (await page.textContent("body")) ?? "";
188
+ const { detected, sample } = detectUsagePage(bodyText);
189
+
190
+ console.log("");
191
+ console.log(` URL: ${url}`);
192
+ console.log(` Detected usage page: ${detected}`);
193
+ if (sample) {
194
+ console.log(` Sample: ${sample.substring(0, 100)}...`);
195
+ }
196
+ console.log("");
197
+
198
+ // Profile is auto-saved by Playwright - no manual save needed
199
+ console.log("💾 Profile auto-saved to:");
200
+ console.log(` ${profileDir}`);
201
+ console.log("");
202
+
203
+ const status: MinimaxAuthStatus = {
204
+ provider: "minimax",
205
+ authenticated: detected,
206
+ checked_at: new Date().toISOString(),
207
+ page_url: url,
208
+ detected_text_sample: sample,
209
+ profile_path: profileDir,
210
+ };
211
+
212
+ saveAuthStatus(status, config);
213
+
214
+ if (detected) {
215
+ console.log("✅ Authentication successful!");
216
+ } else {
217
+ console.log("⚠️ Could not verify usage page. Try again after login.");
218
+ }
219
+
220
+ // Close browser
221
+ await context.close();
222
+ context = null;
223
+
224
+ return status;
225
+ } catch (error) {
226
+ const errorMessage = error instanceof Error ? error.message : String(error);
227
+ console.error(`❌ Error: ${errorMessage}`);
228
+
229
+ if (context) {
230
+ await context.close().catch(() => {});
231
+ }
232
+
233
+ const status: MinimaxAuthStatus = {
234
+ provider: "minimax",
235
+ authenticated: false,
236
+ checked_at: new Date().toISOString(),
237
+ page_url: "",
238
+ detected_text_sample: null,
239
+ profile_path: profileDir,
240
+ error_message: errorMessage,
241
+ };
242
+
243
+ saveAuthStatus(status, config);
244
+ return status;
245
+ }
246
+ }
247
+
248
+ /**
249
+ * Use existing profile to scrape usage data (no browser window)
250
+ */
251
+ export async function scrapeWithExistingProfile(
252
+ config: MinimaxBrowserAuthConfig = {},
253
+ ): Promise<MinimaxAuthStatus> {
254
+ const profileDir = config.profilePath ?? getProfileDir();
255
+ const statusPath = config.statusPath ?? getStatusPath();
256
+ const targetUrl =
257
+ config.targetUrl ?? "https://platform.minimax.io/console/usage";
258
+
259
+ ensureDirs(config);
260
+
261
+ console.log("=".repeat(60));
262
+ console.log("📊 MiniMax Usage Scraper (Silent Mode)");
263
+ console.log("=".repeat(60));
264
+ console.log("");
265
+
266
+ // Check if profile exists
267
+ const profileExists = fs.existsSync(path.join(profileDir, "SingletonLock"));
268
+ if (!profileExists) {
269
+ console.log("⚠️ No profile found. Run auth first:");
270
+ console.log(" bun packages/auth/src/run-minimax-auth.ts auth");
271
+ return {
272
+ provider: "minimax",
273
+ authenticated: false,
274
+ checked_at: new Date().toISOString(),
275
+ page_url: "",
276
+ detected_text_sample: null,
277
+ profile_path: profileDir,
278
+ error_message: "No profile found - run auth first",
279
+ };
280
+ }
281
+
282
+ let context: BrowserContext | null = null;
283
+
284
+ try {
285
+ // Launch with existing profile (headless for scraping)
286
+ console.log("🚀 Launching browser with saved profile...");
287
+ console.log(" (Silent/headless mode for scraping)");
288
+
289
+ context = await chromium.launchPersistentContext(profileDir, {
290
+ headless: true, // Silent mode for scraping
291
+ chromiumSandbox: false,
292
+ args: ["--no-first-run", "--no-default-browser-check"],
293
+ viewport: { width: 1280, height: 800 },
294
+ });
295
+
296
+ const page = context.pages()[0] ?? (await context.newPage());
297
+
298
+ // Navigate to usage
299
+ console.log("🖥️ Navigating to usage page...");
300
+ await page.goto(targetUrl, { waitUntil: "networkidle", timeout: 30000 });
301
+ await page.waitForTimeout(2000);
302
+
303
+ const url = page.url();
304
+ const bodyText = (await page.textContent("body")) ?? "";
305
+ const { detected, sample } = detectUsagePage(bodyText);
306
+
307
+ console.log("");
308
+ console.log(` URL: ${url}`);
309
+ console.log(` Detected usage page: ${detected}`);
310
+
311
+ // Extract all numbers/percentages
312
+ const usageData: string[] = [];
313
+ const elements = await page.$$("body *");
314
+ for (const el of elements.slice(0, 300)) {
315
+ const text = await el.textContent();
316
+ if (
317
+ text &&
318
+ (text.includes("%") ||
319
+ text.match(/\d+\s*[/:]\s*\d+/) ||
320
+ (/^\d+$/.test(text.trim()) && text.length < 10))
321
+ ) {
322
+ const clean = text.trim().substring(0, 50);
323
+ if (!usageData.includes(clean)) {
324
+ usageData.push(clean);
325
+ }
326
+ }
327
+ }
328
+
329
+ if (usageData.length > 0) {
330
+ console.log("");
331
+ console.log("📊 Usage Data Found:");
332
+ usageData.slice(0, 15).forEach((item) => {
333
+ console.log(` - ${item}`);
334
+ });
335
+ }
336
+
337
+ console.log("");
338
+
339
+ const status: MinimaxAuthStatus = {
340
+ provider: "minimax",
341
+ authenticated: detected,
342
+ checked_at: new Date().toISOString(),
343
+ page_url: url,
344
+ detected_text_sample: sample,
345
+ profile_path: profileDir,
346
+ };
347
+
348
+ saveAuthStatus(status, config);
349
+
350
+ if (detected) {
351
+ console.log("✅ Scraped successfully!");
352
+ }
353
+
354
+ await context.close();
355
+ context = null;
356
+
357
+ return status;
358
+ } catch (error) {
359
+ const errorMessage = error instanceof Error ? error.message : String(error);
360
+ console.error(`❌ Error: ${errorMessage}`);
361
+
362
+ if (context) {
363
+ await context.close().catch(() => {});
364
+ }
365
+
366
+ const status: MinimaxAuthStatus = {
367
+ provider: "minimax",
368
+ authenticated: false,
369
+ checked_at: new Date().toISOString(),
370
+ page_url: "",
371
+ detected_text_sample: null,
372
+ profile_path: profileDir,
373
+ error_message: errorMessage,
374
+ };
375
+
376
+ saveAuthStatus(status, config);
377
+ return status;
378
+ }
379
+ }
380
+
381
+ /**
382
+ * Check auth status
383
+ */
384
+ export async function checkAuthStatus(
385
+ config: MinimaxBrowserAuthConfig = {},
386
+ ): Promise<MinimaxAuthStatus> {
387
+ const statusPath = config.statusPath ?? getStatusPath();
388
+ const profileDir = config.profilePath ?? getProfileDir();
389
+
390
+ console.log("Checking MiniMax authentication status...");
391
+
392
+ if (fs.existsSync(statusPath)) {
393
+ const content = fs.readFileSync(statusPath, "utf-8");
394
+ try {
395
+ const status = JSON.parse(content) as MinimaxAuthStatus;
396
+ console.log("");
397
+ console.log(
398
+ status.authenticated
399
+ ? "✅ User is logged in"
400
+ : "⚠️ User is NOT logged in",
401
+ );
402
+ console.log(` Last checked: ${status.checked_at}`);
403
+ console.log(` Profile: ${status.profile_path}`);
404
+ return status;
405
+ } catch {
406
+ console.log("Invalid status file. Run auth first.");
407
+ }
408
+ }
409
+
410
+ const profileExists = fs.existsSync(path.join(profileDir, "SingletonLock"));
411
+ if (profileExists) {
412
+ console.log("");
413
+ console.log("✅ Profile exists - run 'scrape' to check usage");
414
+ return {
415
+ provider: "minimax",
416
+ authenticated: true,
417
+ checked_at: new Date().toISOString(),
418
+ page_url: "",
419
+ detected_text_sample: null,
420
+ profile_path: profileDir,
421
+ };
422
+ }
423
+
424
+ console.log("No profile found. Run auth first.");
425
+ return {
426
+ provider: "minimax",
427
+ authenticated: false,
428
+ checked_at: new Date().toISOString(),
429
+ page_url: "",
430
+ detected_text_sample: null,
431
+ profile_path: profileDir,
432
+ error_message: "No profile found - run auth first",
433
+ };
434
+ }
435
+
436
+ // Alias for backward compatibility
437
+ export const authenticateWithCurator = authenticateWithPersistentBrowser;
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * run-minimax-auth.ts
4
+ *
5
+ * CLI to authenticate with MiniMax using Playwright persistent browser.
6
+ *
7
+ * Usage:
8
+ * bun packages/auth/src/run-minimax-auth.ts auth # First-time: login in browser
9
+ * bun packages/auth/src/run-minimax-auth.ts scrape # Scrape usage (silent)
10
+ * bun packages/auth/src/run-minimax-auth.ts check # Check status
11
+ * bun packages/auth/src/run-minimax-auth.ts open # Open browser with profile
12
+ *
13
+ * Security:
14
+ * - Human logs in manually in the browser
15
+ * - Playwright persistent profile auto-saves session
16
+ * - No credentials are processed by this code
17
+ */
18
+
19
+ import {
20
+ authenticateWithPersistentBrowser,
21
+ scrapeWithExistingProfile,
22
+ checkAuthStatus,
23
+ getProfileDir,
24
+ getStatusPath,
25
+ getRuntimeDir,
26
+ } from "./minimax-browser-auth.js";
27
+
28
+ async function main() {
29
+ const args = process.argv.slice(2);
30
+ const command = args[0] ?? "auth";
31
+
32
+ console.log("");
33
+ console.log("🔐 MiniMax Browser Authentication");
34
+ console.log("=".repeat(50));
35
+ console.log("");
36
+
37
+ if (command === "check") {
38
+ console.log("Command: check");
39
+ console.log("");
40
+ const status = await checkAuthStatus();
41
+ console.log("");
42
+ console.log("Status:");
43
+ console.log(JSON.stringify(status, null, 2));
44
+ console.log("");
45
+ console.log("Profile dir:", getProfileDir());
46
+ console.log("Status file:", getStatusPath());
47
+ return;
48
+ }
49
+
50
+ if (command === "scrape" || command === "usage") {
51
+ console.log("Command: scrape (silent - uses saved profile)");
52
+ console.log("");
53
+ const status = await scrapeWithExistingProfile();
54
+ console.log("");
55
+ console.log("Result:");
56
+ console.log(JSON.stringify(status, null, 2));
57
+ return;
58
+ }
59
+
60
+ if (command === "auth" || command === "login" || command === "open") {
61
+ console.log("Command: auth (persistent browser mode)");
62
+ console.log("");
63
+ console.log("This will:");
64
+ console.log("1. Launch a Chrome browser with persistent profile");
65
+ console.log("2. Navigate to MiniMax usage page");
66
+ console.log("3. Let you log in (first time only)");
67
+ console.log("4. Auto-save profile for future use");
68
+ console.log("");
69
+ console.log("Profile dir:", getProfileDir());
70
+ console.log("");
71
+
72
+ const status = await authenticateWithPersistentBrowser();
73
+ console.log("");
74
+ console.log("Result:");
75
+ console.log(JSON.stringify(status, null, 2));
76
+ return;
77
+ }
78
+
79
+ console.log("Usage:");
80
+ console.log(
81
+ " bun packages/auth/src/run-minimax-auth.ts auth # Login (first time)",
82
+ );
83
+ console.log(
84
+ " bun packages/auth/src/run-minimax-auth.ts scrape # Scrape usage",
85
+ );
86
+ console.log(
87
+ " bun packages/auth/src/run-minimax-auth.ts check # Check status",
88
+ );
89
+ console.log(
90
+ " bun packages/auth/src/run-minimax-auth.ts open # Open browser",
91
+ );
92
+ console.log("");
93
+ console.log("Security:");
94
+ console.log("- Human logs in manually in the browser");
95
+ console.log("- Playwright auto-saves persistent profile");
96
+ console.log("- No credentials are processed by this code");
97
+ }
98
+
99
+ main().catch((error) => {
100
+ console.error("Error:", error.message);
101
+ process.exit(1);
102
+ });
@@ -45,6 +45,30 @@ Human Requirement
45
45
  | `/harness resume` | Resume a paused job |
46
46
  | `/harness cancel` | Cancel the current job |
47
47
 
48
+ ### Authentication Commands
49
+
50
+ | Command | Description |
51
+ |---------|-------------|
52
+ | `/harness auth minimax` | Authenticate with MiniMax (opens browser) |
53
+ | `/harness auth check` | Check MiniMax auth status |
54
+ | `/harness auth scrape` | Scrape MiniMax usage (silent, uses saved profile) |
55
+
56
+ **First-time setup:**
57
+
58
+ ```bash
59
+ harness-auth auth
60
+ # → Opens Chrome browser, login once, profile auto-saves
61
+ # Or: bun packages/auth/src/run-minimax-auth.ts auth
62
+ ```
63
+
64
+ **Subsequent usage:**
65
+
66
+ ```bash
67
+ harness-auth scrape
68
+ # → Silent scraping with saved profile
69
+ # Or: bun packages/auth/src/run-minimax-auth.ts scrape
70
+ ```
71
+
48
72
  ## Job State Machine
49
73
 
50
74
  ```