pi-chatgpt-pool 0.2.1 → 0.2.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 +5 -2
- package/package.json +1 -1
- package/src/index.ts +24 -7
- package/src/panel.ts +10 -6
package/README.md
CHANGED
|
@@ -12,14 +12,15 @@ Requires pi 0.99.1 or later. The plugin uses pi's own Sign in with ChatGPT flow,
|
|
|
12
12
|
|
|
13
13
|
## Set up accounts
|
|
14
14
|
|
|
15
|
-
Run `/chatgpt-pool`
|
|
15
|
+
Run `/chatgpt-pool`, press `a` to add an account, then Enter to sign in. Or from the editor:
|
|
16
16
|
|
|
17
17
|
```text
|
|
18
18
|
/chatgpt-pool add personal
|
|
19
19
|
/chatgpt-pool add work
|
|
20
|
-
/login
|
|
21
20
|
```
|
|
22
21
|
|
|
22
|
+
After adding an account, pi fills in `/login chatgpt-<n>` for it; press Enter to open the ChatGPT sign-in. Sign in to each account with a different ChatGPT account. `/chatgpt-pool login <label>` does the same for an existing account.
|
|
23
|
+
|
|
23
24
|
`/login` lists each account as `ChatGPT (<label>)`, or under a name you choose with `/chatgpt-pool name`. Renaming keeps the account signed in. Sign in to each one with a different ChatGPT account. Then pick a pooled model:
|
|
24
25
|
|
|
25
26
|
```text
|
|
@@ -34,6 +35,7 @@ The pool offers the ChatGPT subscription models: the OpenAI models that pi also
|
|
|
34
35
|
| --- | --- |
|
|
35
36
|
| `/chatgpt-pool` | Open the pool panel (see below) |
|
|
36
37
|
| `/chatgpt-pool add <label>` | Add an account slot. Sign in to it with `/login` |
|
|
38
|
+
| `/chatgpt-pool login <label>` | Fill in `/login` for that account, so Enter starts its sign-in |
|
|
37
39
|
| `/chatgpt-pool name <label> <name>` | Set the name the account shows in `/login`, for example `Work (jal@mastra.ai)`. Leave the name empty to go back to `ChatGPT (<label>)` |
|
|
38
40
|
| `/chatgpt-pool remove <label>` | Remove an account. Run `/logout` for it first to delete its token |
|
|
39
41
|
| `/chatgpt-pool spend <label>` | Spend one banked reset on that account, after you confirm |
|
|
@@ -49,6 +51,7 @@ The command autocompletes its actions and account labels.
|
|
|
49
51
|
| Key | Action |
|
|
50
52
|
| --- | --- |
|
|
51
53
|
| `↑` `↓` or `j` `k` | Select an account |
|
|
54
|
+
| `enter` | Sign in to the selected account (fills in `/login` for it; press Enter again) |
|
|
52
55
|
| `s` | Spend a banked reset on the selected account, after you confirm |
|
|
53
56
|
| `c` | Clear the selected account's recorded limit |
|
|
54
57
|
| `n` | Set the name the selected account shows in `/login` |
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -284,7 +284,7 @@ export default function chatgptPool(pi: ExtensionAPI) {
|
|
|
284
284
|
|
|
285
285
|
const describe = ({ account, signedIn, usage, error }: AccountStatus): string => {
|
|
286
286
|
const limitedUntil = account.resetsAt && account.resetsAt > Date.now() ? account.resetsAt : undefined;
|
|
287
|
-
if (!signedIn) return `${account.label} · not signed in, run /login
|
|
287
|
+
if (!signedIn) return `${account.label} · not signed in, run /chatgpt-pool login ${account.label}`;
|
|
288
288
|
const state = limitedUntil ? `limited until ${formatReset(limitedUntil)}` : "ready";
|
|
289
289
|
if (!usage) return `${account.label} · ${state} · usage unavailable${error ? ` (${error})` : ""}`;
|
|
290
290
|
const windows = usage.windows.map(
|
|
@@ -306,7 +306,8 @@ export default function chatgptPool(pi: ExtensionAPI) {
|
|
|
306
306
|
update((config) => ({ ...config, accounts: [...config.accounts, account] }));
|
|
307
307
|
registerPooledModels();
|
|
308
308
|
pi.registerProvider(accountProvider(openai, account, onReset));
|
|
309
|
-
ctx.ui.notify(`Added ${loginName(account)}
|
|
309
|
+
ctx.ui.notify(`Added ${loginName(account)}.`, "info");
|
|
310
|
+
startLogin(ctx, account);
|
|
310
311
|
};
|
|
311
312
|
|
|
312
313
|
const setLoginName = (ctx: ExtensionContext, account: Account, name: string) => {
|
|
@@ -327,6 +328,14 @@ export default function chatgptPool(pi: ExtensionAPI) {
|
|
|
327
328
|
if (name !== undefined) setLoginName(ctx, account, name);
|
|
328
329
|
};
|
|
329
330
|
|
|
331
|
+
const startLogin = (ctx: ExtensionContext, account: Account) => {
|
|
332
|
+
if (ctx.mode !== "tui") {
|
|
333
|
+
return ctx.ui.notify(`Run /login ${account.id} to sign in to ${loginName(account)}.`, "info");
|
|
334
|
+
}
|
|
335
|
+
ctx.ui.setEditorText(`/login ${account.id}`);
|
|
336
|
+
ctx.ui.notify(`Press Enter to sign in to ${loginName(account)}.`, "info");
|
|
337
|
+
};
|
|
338
|
+
|
|
330
339
|
const removeAccount = (ctx: ExtensionContext, account: Account) => {
|
|
331
340
|
update((config) => ({ ...config, accounts: config.accounts.filter((candidate) => candidate.id !== account.id) }));
|
|
332
341
|
pi.unregisterProvider(account.id);
|
|
@@ -398,13 +407,14 @@ export default function chatgptPool(pi: ExtensionAPI) {
|
|
|
398
407
|
reload = true;
|
|
399
408
|
} else if (action.type === "add") {
|
|
400
409
|
const label = (await ctx.ui.input("Label for the new ChatGPT account", "work"))?.trim();
|
|
401
|
-
if (label) addAccount(ctx, label);
|
|
402
|
-
reload = !!label;
|
|
410
|
+
if (label) return addAccount(ctx, label);
|
|
403
411
|
} else if (action.type === "strategy") {
|
|
404
412
|
await chooseStrategy(ctx);
|
|
405
413
|
} else if (status && action.type === "spend") {
|
|
406
414
|
await spend(ctx, status.account, status.usage);
|
|
407
415
|
reload = true;
|
|
416
|
+
} else if (status && action.type === "login") {
|
|
417
|
+
return startLogin(ctx, status.account);
|
|
408
418
|
} else if (status && action.type === "name") {
|
|
409
419
|
await askLoginName(ctx, status.account);
|
|
410
420
|
} else if (status && action.type === "clear") {
|
|
@@ -462,13 +472,14 @@ export default function chatgptPool(pi: ExtensionAPI) {
|
|
|
462
472
|
{ value: "add", label: "add", description: "Add an account slot" },
|
|
463
473
|
{ value: "remove", label: "remove", description: "Remove an account" },
|
|
464
474
|
{ value: "spend", label: "spend", description: "Spend a banked reset, after confirming" },
|
|
475
|
+
{ value: "login", label: "login", description: "Sign in to an account" },
|
|
465
476
|
{ value: "name", label: "name", description: "Set the name an account shows in /login" },
|
|
466
477
|
{ value: "reset", label: "reset", description: "Forget recorded limits" },
|
|
467
478
|
{ value: "strategy", label: "strategy", description: "Choose how new conversations pick an account" },
|
|
468
479
|
];
|
|
469
480
|
|
|
470
481
|
pi.registerCommand("chatgpt-pool", {
|
|
471
|
-
description: "Manage pooled ChatGPT accounts: usage, banked resets, strategy, add, remove, name, spend, reset",
|
|
482
|
+
description: "Manage pooled ChatGPT accounts: usage, banked resets, strategy, add, login, remove, name, spend, reset",
|
|
472
483
|
getArgumentCompletions(prefix) {
|
|
473
484
|
const [action, ...rest] = prefix.split(" ");
|
|
474
485
|
if (rest.length === 0) return SUBCOMMANDS.filter((item) => item.value.startsWith(action));
|
|
@@ -477,7 +488,7 @@ export default function chatgptPool(pi: ExtensionAPI) {
|
|
|
477
488
|
.filter(([name]) => name.startsWith(rest.join(" ")))
|
|
478
489
|
.map(([name, description]) => ({ value: `strategy ${name}`, label: name, description }));
|
|
479
490
|
}
|
|
480
|
-
if (
|
|
491
|
+
if (!["remove", "spend", "name", "login"].includes(action)) return null;
|
|
481
492
|
if (action === "name" && rest.length > 1) return null;
|
|
482
493
|
const partial = rest.join(" ");
|
|
483
494
|
return accounts
|
|
@@ -499,6 +510,12 @@ export default function chatgptPool(pi: ExtensionAPI) {
|
|
|
499
510
|
return removeAccount(ctx, account);
|
|
500
511
|
}
|
|
501
512
|
|
|
513
|
+
if (action === "login") {
|
|
514
|
+
const account = findAccount(label);
|
|
515
|
+
if (!account) return ctx.ui.notify("Usage: /chatgpt-pool login <label>", "warning");
|
|
516
|
+
return startLogin(ctx, account);
|
|
517
|
+
}
|
|
518
|
+
|
|
502
519
|
if (action === "name") {
|
|
503
520
|
const [target = "", ...nameParts] = rest;
|
|
504
521
|
const account = findAccount(target);
|
|
@@ -533,7 +550,7 @@ export default function chatgptPool(pi: ExtensionAPI) {
|
|
|
533
550
|
return spend(ctx, account);
|
|
534
551
|
}
|
|
535
552
|
|
|
536
|
-
if (action) return ctx.ui.notify(`Unknown action "${action}". Use add, remove, name, spend, reset, or strategy.`, "warning");
|
|
553
|
+
if (action) return ctx.ui.notify(`Unknown action "${action}". Use add, login, remove, name, spend, reset, or strategy.`, "warning");
|
|
537
554
|
if (accounts.length === 0) {
|
|
538
555
|
const label = (await ctx.ui.input("No ChatGPT accounts yet. Label for the first one", "personal"))?.trim();
|
|
539
556
|
if (label) addAccount(ctx, label);
|
package/src/panel.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { type Component, Key, matchesKey, truncateToWidth, type TUI, visibleWidth } from "@earendil-works/pi-tui";
|
|
3
3
|
import { formatIn, formatWindow, usageBar } from "./format.ts";
|
|
4
|
-
import {
|
|
4
|
+
import type { Account } from "./pool.ts";
|
|
5
5
|
import type { Usage } from "./usage.ts";
|
|
6
6
|
|
|
7
7
|
export type AccountStatus = { account: Account; signedIn: boolean; usage?: Usage; error?: string };
|
|
@@ -11,7 +11,7 @@ export type PanelAction =
|
|
|
11
11
|
| { type: "refresh" }
|
|
12
12
|
| { type: "add" }
|
|
13
13
|
| { type: "strategy" }
|
|
14
|
-
| { type: "spend" | "clear" | "remove" | "name"; accountId: string };
|
|
14
|
+
| { type: "spend" | "clear" | "remove" | "name" | "login"; accountId: string };
|
|
15
15
|
|
|
16
16
|
const ACCOUNT_KEYS = new Map<string, "spend" | "clear" | "remove" | "name">([
|
|
17
17
|
["s", "spend"],
|
|
@@ -26,9 +26,9 @@ const POOL_KEYS = new Map<string, "refresh" | "add" | "strategy">([
|
|
|
26
26
|
]);
|
|
27
27
|
const BAR_WIDTH = 16;
|
|
28
28
|
const HINTS = [
|
|
29
|
-
"↑↓ select · s spend reset · c clear limit · n login name · d remove · a add · t strategy · r refresh · esc close",
|
|
30
|
-
"↑↓ · s spend · c clear · n name · d remove · a add · t strategy ·
|
|
31
|
-
"
|
|
29
|
+
"↑↓ select · enter sign in · s spend reset · c clear limit · n login name · d remove · a add · t strategy · r refresh · esc close",
|
|
30
|
+
"↑↓ · enter sign in · s spend · c clear · n name · d remove · a add · t strategy · esc",
|
|
31
|
+
"enter sign in · s spend · n name · d del · a add · esc",
|
|
32
32
|
];
|
|
33
33
|
|
|
34
34
|
export class PoolPanel implements Component {
|
|
@@ -47,6 +47,10 @@ export class PoolPanel implements Component {
|
|
|
47
47
|
if (matchesKey(data, Key.escape) || data === "q") return this.done({ type: "close" });
|
|
48
48
|
if (matchesKey(data, Key.up) || data === "k") return this.move(-1);
|
|
49
49
|
if (matchesKey(data, Key.down) || data === "j") return this.move(1);
|
|
50
|
+
if (matchesKey(data, Key.enter)) {
|
|
51
|
+
const account = this.statuses[this.selected]?.account;
|
|
52
|
+
return account ? this.done({ type: "login", accountId: account.id }) : undefined;
|
|
53
|
+
}
|
|
50
54
|
const poolAction = POOL_KEYS.get(data);
|
|
51
55
|
const accountAction = ACCOUNT_KEYS.get(data);
|
|
52
56
|
const account = this.statuses[this.selected]?.account;
|
|
@@ -96,7 +100,7 @@ export class PoolPanel implements Component {
|
|
|
96
100
|
: "";
|
|
97
101
|
const header = `${pointer}${name}${active} ${state}${banked}`;
|
|
98
102
|
|
|
99
|
-
if (!signedIn) return [header, theme.fg("dim",
|
|
103
|
+
if (!signedIn) return [header, theme.fg("dim", " press enter to sign in")];
|
|
100
104
|
if (!usage) return [header, theme.fg("dim", ` usage unavailable${error ? `: ${error}` : ""}`)];
|
|
101
105
|
return [
|
|
102
106
|
header,
|