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
|
[](https://github.com/ManotLuijiu/pi-harness-runtime)
|
|
4
|
-
[](https://www.npmjs.com/package/pi-harness-runtime)
|
|
5
4
|
[](https://opensource.org/licenses/MIT)
|
|
6
5
|
[]()
|
|
7
6
|
|
|
8
|
-
> ⚠️
|
|
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.
|
|
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
|
```
|