@xingular/harness 1.0.0 → 1.1.1
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/CHANGELOG.md +33 -5
- package/README.md +70 -19
- package/dist/cli.js +10321 -5785
- package/dist/sdk/agents/builtin.d.ts +2 -0
- package/dist/sdk/agents/builtin.js +34 -0
- package/dist/sdk/agents/narrow.d.ts +13 -0
- package/dist/sdk/agents/narrow.js +29 -0
- package/dist/sdk/agents/profiles.d.ts +80 -0
- package/dist/sdk/agents/profiles.js +221 -0
- package/dist/sdk/annotations/bridge.d.ts +42 -0
- package/dist/sdk/annotations/bridge.js +264 -0
- package/dist/sdk/annotations/capture.d.ts +116 -0
- package/dist/sdk/annotations/capture.js +308 -0
- package/dist/sdk/annotations/delivery.d.ts +72 -0
- package/dist/sdk/annotations/delivery.js +130 -0
- package/dist/sdk/annotations/inspect.d.ts +10 -0
- package/dist/sdk/annotations/inspect.js +33 -0
- package/dist/sdk/annotations/isolated.d.ts +6 -0
- package/dist/sdk/annotations/isolated.js +51 -0
- package/dist/sdk/annotations/pageScript.d.ts +28 -0
- package/dist/sdk/annotations/pageScript.js +570 -0
- package/dist/sdk/annotations/part.d.ts +10 -0
- package/dist/sdk/annotations/part.js +144 -0
- package/dist/sdk/annotations/queue.d.ts +4 -0
- package/dist/sdk/annotations/queue.js +40 -0
- package/dist/sdk/annotations/serverMethods.d.ts +3 -0
- package/dist/sdk/annotations/serverMethods.js +62 -0
- package/dist/sdk/annotations/store.d.ts +160 -0
- package/dist/sdk/annotations/store.js +386 -0
- package/dist/sdk/browser/index.d.ts +14 -3
- package/dist/sdk/browser/index.js +11 -4
- package/dist/sdk/browser/serverMethods.d.ts +3 -0
- package/dist/sdk/browser/serverMethods.js +7 -0
- package/dist/sdk/browser/session.d.ts +1 -0
- package/dist/sdk/browser/session.js +45 -9
- package/dist/sdk/cli.js +19 -11
- package/dist/sdk/core/bus.js +1 -1
- package/dist/sdk/core/env.d.ts +1 -1
- package/dist/sdk/core/env.js +1 -1
- package/dist/sdk/doctor/serverMethods.d.ts +3 -0
- package/dist/sdk/doctor/serverMethods.js +8 -0
- package/dist/sdk/harness.d.ts +15 -0
- package/dist/sdk/harness.js +44 -5
- package/dist/sdk/history/recall.js +8 -8
- package/dist/sdk/history/serverMethods.d.ts +3 -0
- package/dist/sdk/history/serverMethods.js +8 -0
- package/dist/sdk/hub/cli.d.ts +1 -1
- package/dist/sdk/hub/cli.js +1 -1
- package/dist/sdk/hub/schema.d.ts +1 -1
- package/dist/sdk/hub/serverMethods.d.ts +3 -0
- package/dist/sdk/hub/serverMethods.js +11 -0
- package/dist/sdk/index.d.ts +10 -0
- package/dist/sdk/index.js +7 -0
- package/dist/sdk/mcp/connections.js +14 -7
- package/dist/sdk/mcp/serverMethods.d.ts +3 -0
- package/dist/sdk/mcp/serverMethods.js +22 -0
- package/dist/sdk/plugins/builtin.d.ts +1 -0
- package/dist/sdk/plugins/builtin.js +128 -33
- package/dist/sdk/plugins/builtinRegistry.d.ts +53 -0
- package/dist/sdk/plugins/builtinRegistry.js +49 -0
- package/dist/sdk/plugins/cli.d.ts +1 -1
- package/dist/sdk/plugins/cli.js +4 -3
- package/dist/sdk/prompt/compile.d.ts +5 -2
- package/dist/sdk/prompt/compile.js +25 -8
- package/dist/sdk/refine/cli.d.ts +1 -1
- package/dist/sdk/refine/cli.js +2 -2
- package/dist/sdk/refine/serverMethods.d.ts +3 -0
- package/dist/sdk/refine/serverMethods.js +20 -0
- package/dist/sdk/server/methods.js +9 -86
- package/dist/sdk/server/params.d.ts +4 -0
- package/dist/sdk/server/params.js +11 -0
- package/dist/sdk/sessions/contracts.d.ts +17 -0
- package/dist/sdk/sessions/contracts.js +42 -3
- package/dist/sdk/sessions/planMode.d.ts +5 -2
- package/dist/sdk/sessions/planMode.js +12 -3
- package/dist/sdk/sessions/reasoningSummaries.d.ts +2 -2
- package/dist/sdk/sessions/reasoningSummaries.js +3 -3
- package/dist/sdk/sessions/registry.d.ts +1 -0
- package/dist/sdk/sessions/registry.js +4 -1
- package/dist/sdk/sessions/runIndex.js +7 -7
- package/dist/sdk/sessions/runStore.js +3 -1
- package/dist/sdk/sessions/runner.js +2 -2
- package/dist/sdk/sessions/sessions.d.ts +17 -5
- package/dist/sdk/sessions/sessions.js +79 -12
- package/dist/sdk/sessions/sideQuestionServerMethods.d.ts +3 -0
- package/dist/sdk/sessions/sideQuestionServerMethods.js +19 -0
- package/dist/sdk/sessions/sideQuestions.d.ts +66 -0
- package/dist/sdk/sessions/sideQuestions.js +290 -0
- package/dist/sdk/sessions/subagentServerMethods.d.ts +3 -0
- package/dist/sdk/sessions/subagentServerMethods.js +41 -0
- package/dist/sdk/sessions/subagentService.d.ts +13 -2
- package/dist/sdk/sessions/subagentService.js +34 -16
- package/dist/sdk/sessions/subagents.d.ts +9 -1
- package/dist/sdk/sessions/subagents.js +19 -0
- package/dist/sdk/terminal/browser.d.ts +62 -0
- package/dist/sdk/terminal/browser.js +41 -0
- package/dist/sdk/terminal/browserControl.d.ts +29 -0
- package/dist/sdk/terminal/browserControl.js +186 -0
- package/dist/sdk/terminal/commandHelp.d.ts +17 -0
- package/dist/sdk/terminal/commandHelp.js +20 -0
- package/dist/sdk/terminal/controller.d.ts +7 -1
- package/dist/sdk/terminal/controller.js +34 -2
- package/dist/sdk/terminal/opentui/subagentsWidget.d.ts +28 -0
- package/dist/sdk/terminal/opentui/subagentsWidget.js +52 -0
- package/dist/sdk/terminal/piExtension.js +33 -48
- package/dist/sdk/terminal/socket.d.ts +6 -3
- package/dist/sdk/terminal/socket.js +40 -9
- package/dist/sdk/terminal/standalone.d.ts +2 -0
- package/dist/sdk/terminal/standalone.js +50 -10
- package/dist/sdk/terminal/subagentMonitor.d.ts +38 -0
- package/dist/sdk/terminal/subagentMonitor.js +121 -0
- package/dist/sdk/terminal/terminals.d.ts +42 -6
- package/dist/sdk/terminal/terminals.js +359 -18
- package/dist/sdk/terminal/trajectory.js +2 -2
- package/dist/sdk/terminal/tui/annotateCommand.d.ts +3 -0
- package/dist/sdk/terminal/tui/annotateCommand.js +22 -0
- package/dist/sdk/terminal/tui/commandPages.d.ts +5 -1
- package/dist/sdk/terminal/tui/commandPages.js +54 -6
- package/dist/sdk/terminal/tui/commands.d.ts +8 -4
- package/dist/sdk/terminal/tui/commands.js +181 -58
- package/dist/sdk/terminal/tui/dialogGeometry.d.ts +2 -2
- package/dist/sdk/terminal/tui/dialogGeometry.js +4 -2
- package/dist/sdk/terminal/tui/dialogs.d.ts +6 -1
- package/dist/sdk/terminal/tui/dialogs.js +9 -2
- package/dist/sdk/terminal/tui/modelPicker.js +1 -6
- package/dist/sdk/terminal/tui/page.d.ts +3 -0
- package/dist/sdk/terminal/tui/pageOverlay.js +17 -6
- package/dist/sdk/terminal/tui/pages/accountsPage.d.ts +26 -4
- package/dist/sdk/terminal/tui/pages/accountsPage.js +264 -36
- package/dist/sdk/terminal/tui/pages/agentsPage.d.ts +74 -0
- package/dist/sdk/terminal/tui/pages/agentsPage.js +190 -0
- package/dist/sdk/terminal/tui/pages/annotatePage.d.ts +94 -0
- package/dist/sdk/terminal/tui/pages/annotatePage.js +341 -0
- package/dist/sdk/terminal/tui/pages/browserPage.d.ts +87 -0
- package/dist/sdk/terminal/tui/pages/browserPage.js +738 -0
- package/dist/sdk/terminal/tui/pages/doctorPage.d.ts +2 -0
- package/dist/sdk/terminal/tui/pages/doctorPage.js +103 -15
- package/dist/sdk/terminal/tui/pages/notesPage.d.ts +75 -0
- package/dist/sdk/terminal/tui/pages/notesPage.js +201 -0
- package/dist/sdk/terminal/tui/pages/settingsPage.d.ts +8 -0
- package/dist/sdk/terminal/tui/pages/settingsPage.js +16 -1
- package/dist/sdk/terminal/tui/pages/subagentsPage.d.ts +23 -0
- package/dist/sdk/terminal/tui/pages/subagentsPage.js +70 -0
- package/dist/sdk/terminal/tui/pages/trajectoryPage.d.ts +12 -5
- package/dist/sdk/terminal/tui/pages/trajectoryPage.js +156 -83
- package/dist/sdk/terminal/tui/pages/worktreesPage.d.ts +72 -0
- package/dist/sdk/terminal/tui/pages/worktreesPage.js +203 -0
- package/dist/sdk/terminal/tui/planMode.d.ts +1 -1
- package/dist/sdk/terminal/tui/planMode.js +1 -1
- package/dist/sdk/terminal/tui/pluginCommands.d.ts +14 -0
- package/dist/sdk/terminal/tui/pluginCommands.js +27 -0
- package/dist/sdk/terminal/tui/questions.d.ts +2 -0
- package/dist/sdk/terminal/tui/runtime.js +4 -2
- package/dist/sdk/terminal/tui/settingsModel.d.ts +2 -0
- package/dist/sdk/terminal/tui/settingsModel.js +32 -2
- package/dist/sdk/terminal/tui/sideQuestion.d.ts +26 -26
- package/dist/sdk/terminal/tui/sideQuestion.js +131 -98
- package/dist/sdk/terminal/tui/ui.d.ts +2 -0
- package/dist/sdk/terminal/tui/ui.js +1 -0
- package/dist/sdk/tools/askUser.js +26 -8
- package/dist/sdk/tools/checklist.js +2 -2
- package/dist/sdk/tools/harnessTools.d.ts +2 -0
- package/dist/sdk/tools/harnessTools.js +1 -1
- package/dist/sdk/tools/spawnSubagent.js +13 -4
- package/dist/sdk/web/part.d.ts +10 -0
- package/dist/sdk/web/part.js +6 -0
- package/dist/sdk/web/serverMethods.d.ts +3 -0
- package/dist/sdk/web/serverMethods.js +7 -0
- package/dist/sdk/wiki/serverMethods.d.ts +3 -0
- package/dist/sdk/wiki/serverMethods.js +26 -0
- package/dist/sdk/worktrees/cli.d.ts +11 -0
- package/dist/sdk/worktrees/cli.js +146 -0
- package/dist/sdk/worktrees/copy.d.ts +9 -0
- package/dist/sdk/worktrees/copy.js +111 -0
- package/dist/sdk/worktrees/git.d.ts +16 -0
- package/dist/sdk/worktrees/git.js +81 -0
- package/dist/sdk/worktrees/serverMethods.d.ts +3 -0
- package/dist/sdk/worktrees/serverMethods.js +30 -0
- package/dist/sdk/worktrees/service.d.ts +73 -0
- package/dist/sdk/worktrees/service.js +483 -0
- package/dist/sdk/worktrees/setup.d.ts +23 -0
- package/dist/sdk/worktrees/setup.js +65 -0
- package/dist/sdk/worktrees/types.d.ts +91 -0
- package/dist/sdk/worktrees/types.js +1 -0
- package/dist/sdk/world/cli.d.ts +2 -2
- package/dist/sdk/world/cli.js +6 -6
- package/dist/sdk/world/world.d.ts +3 -1
- package/dist/sdk/world/world.js +8 -9
- package/dist/terminal-opentui.js +14540 -7374
- package/dist/terminal-pi.js +285 -666
- package/docs/README.md +22 -2
- package/docs/architecture/00-overview.md +28 -16
- package/docs/architecture/02-sessions-and-runs.md +72 -15
- package/docs/architecture/03-variants.md +10 -2
- package/docs/architecture/04-prompt.md +26 -6
- package/docs/architecture/06-plugins-and-capabilities.md +39 -3
- package/docs/architecture/07-tools-and-gate.md +21 -6
- package/docs/architecture/09-accounts-and-connectors.md +8 -0
- package/docs/architecture/10-server-protocol.md +111 -2
- package/docs/architecture/11-terminal.md +185 -5
- package/docs/architecture/12-storage.md +15 -1
- package/docs/architecture/13-world-tools.md +28 -6
- package/docs/architecture/14-surfaces-and-parity.md +89 -0
- package/docs/architecture/README.md +15 -4
- package/docs/architecture/proposals/PROP-0006-run-checklist.md +4 -2
- package/docs/architecture/proposals/PROP-0013-resource-browser.md +58 -0
- package/docs/architecture/proposals/PROP-0014-fullscreen-browser-tabs.md +58 -0
- package/docs/architecture/proposals/PROP-0015-one-capability-surface.md +88 -0
- package/docs/architecture/proposals/PROP-0016-managed-worktrees.md +156 -0
- package/docs/architecture/proposals/PROP-0017-page-annotations.md +127 -0
- package/docs/architecture/proposals/PROP-0018-annotation-queue-and-app-targets.md +179 -0
- package/docs/architecture/proposals/PROP-0019-contextual-side-questions.md +122 -0
- package/docs/architecture/proposals/PROP-0020-builtin-feature-plugins.md +105 -0
- package/docs/architecture/proposals/PROP-0021-command-namespaces-and-duplicates.md +99 -0
- package/docs/architecture/proposals/PROP-0022-plan-only-questions.md +102 -0
- package/docs/architecture/proposals/PROP-0023-xagent-cli-name.md +61 -0
- package/docs/architecture/proposals/PROP-0024-question-sketch-previews.md +130 -0
- package/docs/architecture/proposals/PROP-0025-contextual-question-wireframes.md +49 -0
- package/docs/architecture/proposals/PROP-0026-connected-sketch-display.md +94 -0
- package/docs/architecture/proposals/PROP-0027-xingular-agent-product-identity.md +60 -0
- package/docs/architecture/proposals/PROP-0028-normalize-question-wireframes.md +55 -0
- package/docs/architecture/proposals/PROP-0029-agent-profiles.md +124 -0
- package/docs/architecture/proposals/PROP-0030-subagent-title.md +86 -0
- package/docs/architecture/proposals/README.md +25 -0
- package/docs/decisions/ADR-0009-xharness-command.md +6 -5
- package/docs/decisions/ADR-0014-public-release-baseline.md +3 -2
- package/docs/decisions/ADR-0015-resource-browser.md +28 -0
- package/docs/decisions/ADR-0016-fullscreen-browser-tabs.md +31 -0
- package/docs/decisions/ADR-0017-one-capability-surface.md +34 -0
- package/docs/decisions/ADR-0018-builtin-feature-plugins.md +35 -0
- package/docs/decisions/ADR-0019-managed-git-worktrees.md +31 -0
- package/docs/decisions/ADR-0020-page-annotations.md +37 -0
- package/docs/decisions/ADR-0021-required-command-namespaces.md +40 -0
- package/docs/decisions/ADR-0022-plan-only-question-tools.md +34 -0
- package/docs/decisions/ADR-0023-contextual-side-question-runs.md +35 -0
- package/docs/decisions/ADR-0024-xagent-command.md +40 -0
- package/docs/decisions/ADR-0025-question-choice-sketches.md +49 -0
- package/docs/decisions/ADR-0026-contextual-question-wireframes.md +58 -0
- package/docs/decisions/ADR-0027-connected-sketch-display.md +60 -0
- package/docs/decisions/ADR-0028-xingular-agent-product-identity.md +56 -0
- package/docs/decisions/ADR-0029-normalized-question-wireframe-borders.md +33 -0
- package/docs/decisions/ADR-0030-agent-profiles.md +43 -0
- package/docs/doctor.md +10 -4
- package/docs/features/README.md +26 -0
- package/docs/features/interfaces/README.md +1 -1
- package/docs/future-work.md +48 -0
- package/docs/parity.md +78 -0
- package/docs/releases/1.0.0.md +2 -2
- package/docs/releases/4.0.0.md +46 -0
- package/docs/releases/README.md +20 -15
- package/docs/study/00-index.md +5 -0
- package/docs/study/autonomous-engineering-sources.md +277 -0
- package/docs/study/autonomy-sources.md +147 -0
- package/docs/study/proactive-autonomy.md +408 -0
- package/docs/study/tui-resource-browser.md +78 -0
- package/docs/terminal-tui.md +325 -93
- package/package.json +3 -3
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The Accounts page: one row per account with its health
|
|
3
|
-
*
|
|
4
|
-
* the ones the `/accounts` menu of a native `pi` session uses.
|
|
2
|
+
* The Accounts page: one row per account with its health and Pools as a second section. Opening
|
|
3
|
+
* an account shows its details and grouped actions in a wide inspector or a compact detail page;
|
|
4
|
+
* the actions themselves are the ones the `/accounts` menu of a native `pi` session uses.
|
|
5
5
|
*/
|
|
6
6
|
import type { Theme } from '@earendil-works/pi-coding-agent';
|
|
7
7
|
import { type AccountsCall, type AccountsUi, type SessionAccount } from "../../accountsMenu.js";
|
|
8
|
+
import type { GroupedSelectOption } from "../dialogs.js";
|
|
8
9
|
import type { TerminalPage } from "../page.js";
|
|
9
10
|
import type { TerminalMouseInput } from "../mouse.js";
|
|
10
11
|
export interface AccountsPageOptions {
|
|
@@ -12,19 +13,30 @@ export interface AccountsPageOptions {
|
|
|
12
13
|
call: AccountsCall;
|
|
13
14
|
/** The account this session runs on. */
|
|
14
15
|
session: () => SessionAccount | null;
|
|
15
|
-
ui: AccountsUi
|
|
16
|
+
ui: AccountsUi & {
|
|
17
|
+
selectGrouped?(title: string, options: readonly GroupedSelectOption[]): Promise<string | undefined>;
|
|
18
|
+
};
|
|
16
19
|
/** The session is switching accounts: the host ends it so the launcher can start it again. */
|
|
17
20
|
relaunch(): void;
|
|
18
21
|
changed(): void;
|
|
19
22
|
}
|
|
20
23
|
export declare class AccountsPage implements TerminalPage {
|
|
21
24
|
private readonly options;
|
|
25
|
+
readonly presentation = "fullscreen";
|
|
26
|
+
readonly ownsNavigation = true;
|
|
22
27
|
private overview;
|
|
23
28
|
private error;
|
|
24
29
|
private section;
|
|
30
|
+
private focus;
|
|
31
|
+
private detailOpen;
|
|
32
|
+
private detailAccountId;
|
|
25
33
|
private busy;
|
|
26
34
|
private disposed;
|
|
35
|
+
private compact;
|
|
36
|
+
private wideListWidth;
|
|
37
|
+
private actionRowsStart;
|
|
27
38
|
private readonly list;
|
|
39
|
+
private readonly actionList;
|
|
28
40
|
constructor(options: AccountsPageOptions);
|
|
29
41
|
get focused(): boolean;
|
|
30
42
|
set focused(value: boolean);
|
|
@@ -38,6 +50,7 @@ export declare class AccountsPage implements TerminalPage {
|
|
|
38
50
|
readonly title: "Pools";
|
|
39
51
|
}];
|
|
40
52
|
activeTab(): string;
|
|
53
|
+
selectTab(id: string): void;
|
|
41
54
|
hint(): string;
|
|
42
55
|
private reload;
|
|
43
56
|
/** Searching looks through accounts and pools together; otherwise the page shows the current section. */
|
|
@@ -45,9 +58,18 @@ export declare class AccountsPage implements TerminalPage {
|
|
|
45
58
|
private accountRows;
|
|
46
59
|
private poolRows;
|
|
47
60
|
private account;
|
|
61
|
+
private detailAccount;
|
|
62
|
+
private selectGrouped;
|
|
63
|
+
private accountActionGroup;
|
|
64
|
+
private fillActions;
|
|
65
|
+
private detailLines;
|
|
48
66
|
/** Runs one thing at a time and always reads the accounts again afterwards. */
|
|
49
67
|
private guarded;
|
|
68
|
+
private closeDetails;
|
|
69
|
+
private openDetails;
|
|
50
70
|
private activateRow;
|
|
71
|
+
private activateAction;
|
|
72
|
+
private moveSection;
|
|
51
73
|
handleMouse(event: TerminalMouseInput): import("@earendil-works/pi-tui").TuiMouseEventResult | undefined;
|
|
52
74
|
handleInput(data: string): boolean;
|
|
53
75
|
render(width: number, rows: number): string[];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { matchesKey, truncateToWidth } from '@earendil-works/pi-tui';
|
|
1
|
+
import { matchesKey, truncateToWidth, visibleWidth, wrapTextWithAnsi } from '@earendil-works/pi-tui';
|
|
2
2
|
import { HEALTH, accountActions, accountDetails, addAccount, editPool, runAccountAction } from "../../accountsMenu.js";
|
|
3
3
|
import { safeText } from "../chrome.js";
|
|
4
4
|
import { FilterList } from "../list.js";
|
|
@@ -10,27 +10,59 @@ const BACK = 'Back';
|
|
|
10
10
|
const HEALTH_TONE = { ok: 'success', limited: 'warning', auth_expired: 'error', signed_out: 'error', error: 'error', unknown: 'dim' };
|
|
11
11
|
export class AccountsPage {
|
|
12
12
|
options;
|
|
13
|
+
presentation = 'fullscreen';
|
|
14
|
+
ownsNavigation = true;
|
|
13
15
|
overview;
|
|
14
16
|
error;
|
|
15
17
|
section = 'accounts';
|
|
18
|
+
focus = 'list';
|
|
19
|
+
detailOpen = false;
|
|
20
|
+
detailAccountId;
|
|
16
21
|
busy = false;
|
|
17
22
|
disposed = false;
|
|
23
|
+
compact = false;
|
|
24
|
+
wideListWidth = 0;
|
|
25
|
+
actionRowsStart = 0;
|
|
18
26
|
list;
|
|
27
|
+
actionList;
|
|
19
28
|
constructor(options) {
|
|
20
29
|
this.options = options;
|
|
21
30
|
this.list = new FilterList({
|
|
22
31
|
theme: options.theme, search: 'always', noun: ['row', 'rows'], empty: 'Nothing here yet',
|
|
23
32
|
searchContext: () => 'accounts and pools',
|
|
24
|
-
onActivate: row =>
|
|
33
|
+
onActivate: row => this.activateRow(row)
|
|
34
|
+
});
|
|
35
|
+
this.actionList = new FilterList({
|
|
36
|
+
theme: options.theme, search: 'never', noun: ['action', 'actions'],
|
|
37
|
+
onActivate: row => { if (row.data)
|
|
38
|
+
void this.activateAction(row.data); }
|
|
25
39
|
});
|
|
26
40
|
}
|
|
27
|
-
get focused() { return this.list.focused; }
|
|
28
|
-
set focused(value) { this.list.focused = value; }
|
|
41
|
+
get focused() { return this.list.focused || this.actionList.focused; }
|
|
42
|
+
set focused(value) { this.list.focused = value && this.focus === 'list'; this.actionList.focused = value && this.focus === 'actions'; }
|
|
29
43
|
activate() { void this.reload(); }
|
|
30
44
|
dispose() { this.disposed = true; }
|
|
31
45
|
tabs() { return SECTIONS; }
|
|
32
46
|
activeTab() { return this.section; }
|
|
33
|
-
|
|
47
|
+
selectTab(id) {
|
|
48
|
+
if (!SECTIONS.some(tab => tab.id === id))
|
|
49
|
+
return;
|
|
50
|
+
this.section = id;
|
|
51
|
+
this.detailOpen = false;
|
|
52
|
+
this.detailAccountId = undefined;
|
|
53
|
+
this.focus = 'list';
|
|
54
|
+
this.list.focused = true;
|
|
55
|
+
this.actionList.focused = false;
|
|
56
|
+
this.fill();
|
|
57
|
+
this.options.changed();
|
|
58
|
+
}
|
|
59
|
+
hint() {
|
|
60
|
+
if (this.compact && this.detailOpen)
|
|
61
|
+
return '↑/↓ actions · Enter activate';
|
|
62
|
+
if (this.focus === 'actions')
|
|
63
|
+
return '↑/↓ actions · Enter activate · Tab focus';
|
|
64
|
+
return this.list.query ? 'Tab focus · Enter open · Esc clear' : 'Tab focus · ←/→ sections · Enter open';
|
|
65
|
+
}
|
|
34
66
|
async reload() {
|
|
35
67
|
try {
|
|
36
68
|
this.overview = await this.options.call('accounts.list');
|
|
@@ -45,8 +77,12 @@ export class AccountsPage {
|
|
|
45
77
|
this.options.changed();
|
|
46
78
|
}
|
|
47
79
|
/** Searching looks through accounts and pools together; otherwise the page shows the current section. */
|
|
48
|
-
fill() {
|
|
80
|
+
fill(compact = this.compact) {
|
|
81
|
+
this.compact = compact;
|
|
49
82
|
this.list.setRows(this.list.query ? [...this.accountRows(), ...this.poolRows()] : this.section === 'accounts' ? this.accountRows() : this.poolRows());
|
|
83
|
+
const account = this.detailOpen ? this.detailAccount() : undefined;
|
|
84
|
+
if (account)
|
|
85
|
+
this.fillActions(account);
|
|
50
86
|
}
|
|
51
87
|
accountRows() {
|
|
52
88
|
const overview = this.overview;
|
|
@@ -56,7 +92,7 @@ export class AccountsPage {
|
|
|
56
92
|
const accounts = overview.accounts.map(account => ({
|
|
57
93
|
id: `account:${account.id}`, group: 'Accounts',
|
|
58
94
|
label: `${account.isDefault ? '●' : '○'} ${safeText(account.label)}`,
|
|
59
|
-
detail: `${HEALTH[account.state.health] ?? HEALTH.unknown}${session?.accountId === account.id ?
|
|
95
|
+
detail: `${HEALTH[account.state.health] ?? HEALTH.unknown}${session?.accountId === account.id ? ` · ${this.compact ? 'current' : 'this session'}` : ''}`,
|
|
60
96
|
tone: HEALTH_TONE[account.state.health] ?? 'dim',
|
|
61
97
|
searchText: `${account.label} ${account.state.identity ?? ''} ${HEALTH[account.state.health] ?? ''}`
|
|
62
98
|
}));
|
|
@@ -80,6 +116,71 @@ export class AccountsPage {
|
|
|
80
116
|
];
|
|
81
117
|
}
|
|
82
118
|
account(id) { return this.overview?.accounts.find(account => `account:${account.id}` === id); }
|
|
119
|
+
detailAccount() { return this.detailAccountId ? this.overview?.accounts.find(account => account.id === this.detailAccountId) : undefined; }
|
|
120
|
+
selectGrouped(title, options) {
|
|
121
|
+
return this.options.ui.selectGrouped
|
|
122
|
+
? this.options.ui.selectGrouped(title, options)
|
|
123
|
+
: this.options.ui.select(title, options.map(option => option.label));
|
|
124
|
+
}
|
|
125
|
+
accountActionGroup(label) {
|
|
126
|
+
if (label.startsWith('Use for this session') || label === 'Make default for new sessions')
|
|
127
|
+
return 'Session';
|
|
128
|
+
if (['Rename', 'Check', 'Activity'].includes(label))
|
|
129
|
+
return 'Account';
|
|
130
|
+
if (['Sign in', 'Remove a provider', 'Sign out'].includes(label))
|
|
131
|
+
return 'Sign-in';
|
|
132
|
+
if (label === 'Remove account')
|
|
133
|
+
return 'Remove';
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
fillActions(account) {
|
|
137
|
+
const isSession = this.options.session()?.accountId === account.id;
|
|
138
|
+
this.actionList.setRows(accountActions(account, isSession, false).map(label => ({
|
|
139
|
+
id: label, label, group: this.accountActionGroup(label), data: label
|
|
140
|
+
})));
|
|
141
|
+
this.actionList.focused = this.focus === 'actions';
|
|
142
|
+
}
|
|
143
|
+
detailLines(width, summary = false) {
|
|
144
|
+
const theme = this.options.theme();
|
|
145
|
+
const account = this.detailOpen ? this.detailAccount() : this.section === 'accounts' ? this.account(this.list.selected?.id ?? '') : undefined;
|
|
146
|
+
let title;
|
|
147
|
+
let lines;
|
|
148
|
+
if (account) {
|
|
149
|
+
title = account.label;
|
|
150
|
+
if (summary) {
|
|
151
|
+
const state = account.state;
|
|
152
|
+
const signedIn = state.signedIn === null ? 'not checked' : state.signedIn ? 'yes' : 'no';
|
|
153
|
+
lines = [
|
|
154
|
+
`Health: ${HEALTH[state.health] ?? HEALTH.unknown} · Default: ${account.isDefault ? 'yes' : 'no'} · Signed in: ${signedIn}`,
|
|
155
|
+
`Providers: ${state.piProviders.length ? state.piProviders.join(', ') : 'none'}`,
|
|
156
|
+
...(state.identity ? [`Identity: ${state.identity}`] : []),
|
|
157
|
+
...(state.plan ? [`Plan: ${state.plan}`] : []),
|
|
158
|
+
`Last checked: ${state.lastCheckedAt ? new Date(state.lastCheckedAt).toLocaleString() : 'never'}`,
|
|
159
|
+
...state.limits.map(limit => `${limit.label}: ${limit.usedPercent ?? '?'}% used${limit.resetsAt ? `, resets ${new Date(limit.resetsAt).toLocaleString()}` : ''}`),
|
|
160
|
+
...(state.limits.length ? [] : ['Usage limits: not reported']),
|
|
161
|
+
...(state.capabilities.length ? [`Detected: ${state.capabilities.join(', ')}`] : []),
|
|
162
|
+
...(state.lastError ? [`Last error: ${state.lastError.message}`] : [])
|
|
163
|
+
];
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
lines = accountDetails(account).split('\n').slice(1);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
else if (this.section === 'pools') {
|
|
170
|
+
const pool = this.overview?.pools.find(item => `pool:${item.id}` === this.list.selected?.id);
|
|
171
|
+
const names = new Map(this.overview?.accounts.map(item => [item.id, item.label]) ?? []);
|
|
172
|
+
title = pool ? `Pool · ${pool.label}` : 'Pools';
|
|
173
|
+
lines = pool
|
|
174
|
+
? ['New sessions use the first ready account in this order:', ...pool.accountIds.map((id, index) => `${index + 1}. ${names.get(id) ?? 'removed account'}`)]
|
|
175
|
+
: ['Select a pool to see its account order.'];
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
title = 'Account details';
|
|
179
|
+
lines = ['Select an account to see health and usage details.'];
|
|
180
|
+
}
|
|
181
|
+
const wrap = (text) => wrapTextWithAnsi(safeText(text), Math.max(1, width));
|
|
182
|
+
return [theme.bold(theme.fg('accent', safeText(title))), ...lines.flatMap(wrap).map(line => theme.fg('muted', line))];
|
|
183
|
+
}
|
|
83
184
|
/** Runs one thing at a time and always reads the accounts again afterwards. */
|
|
84
185
|
async guarded(work) {
|
|
85
186
|
if (this.busy)
|
|
@@ -96,8 +197,31 @@ export class AccountsPage {
|
|
|
96
197
|
}
|
|
97
198
|
await this.reload();
|
|
98
199
|
}
|
|
200
|
+
closeDetails() {
|
|
201
|
+
this.detailOpen = false;
|
|
202
|
+
this.detailAccountId = undefined;
|
|
203
|
+
this.focus = 'list';
|
|
204
|
+
this.list.focused = true;
|
|
205
|
+
this.actionList.focused = false;
|
|
206
|
+
this.options.changed();
|
|
207
|
+
}
|
|
208
|
+
openDetails() {
|
|
209
|
+
const account = this.account(this.list.selected?.id ?? '');
|
|
210
|
+
if (!account)
|
|
211
|
+
return;
|
|
212
|
+
this.detailOpen = true;
|
|
213
|
+
this.detailAccountId = account.id;
|
|
214
|
+
this.focus = 'actions';
|
|
215
|
+
this.list.focused = false;
|
|
216
|
+
this.fillActions(account);
|
|
217
|
+
this.options.changed();
|
|
218
|
+
}
|
|
99
219
|
activateRow(row) {
|
|
100
|
-
|
|
220
|
+
if (this.account(row.id)) {
|
|
221
|
+
this.openDetails();
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
void this.guarded(async () => {
|
|
101
225
|
const { ui, call } = this.options;
|
|
102
226
|
const overview = this.overview;
|
|
103
227
|
if (!overview)
|
|
@@ -120,43 +244,119 @@ export class AccountsPage {
|
|
|
120
244
|
return;
|
|
121
245
|
}
|
|
122
246
|
const pool = overview.pools.find(candidate => `pool:${candidate.id}` === row.id);
|
|
123
|
-
if (pool)
|
|
124
|
-
const action = await ui.select(pool.label, ['Edit', 'Delete', BACK]);
|
|
125
|
-
if (action === 'Edit')
|
|
126
|
-
await editPool(ui, call, overview, pool);
|
|
127
|
-
else if (action === 'Delete' && await ui.confirm(`Delete ${pool.label}?`, 'The accounts stay. Only the pool is removed.')) {
|
|
128
|
-
try {
|
|
129
|
-
await call('accounts.removePool', { poolId: pool.id });
|
|
130
|
-
}
|
|
131
|
-
catch (error) {
|
|
132
|
-
ui.notify(error instanceof Error ? error.message : String(error), 'error');
|
|
133
|
-
}
|
|
134
|
-
}
|
|
247
|
+
if (!pool)
|
|
135
248
|
return;
|
|
249
|
+
const action = await this.selectGrouped(pool.label, [
|
|
250
|
+
{ label: 'Edit', group: 'Manage' }, { label: 'Delete', group: 'Remove' }, { label: BACK }
|
|
251
|
+
]);
|
|
252
|
+
if (action === 'Edit')
|
|
253
|
+
await editPool(ui, call, overview, pool);
|
|
254
|
+
else if (action === 'Delete' && await ui.confirm(`Delete ${pool.label}?`, 'The accounts stay. Only the pool is removed.')) {
|
|
255
|
+
try {
|
|
256
|
+
await call('accounts.removePool', { poolId: pool.id });
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
ui.notify(error instanceof Error ? error.message : String(error), 'error');
|
|
260
|
+
}
|
|
136
261
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
async activateAction(action) {
|
|
265
|
+
if (this.busy)
|
|
266
|
+
return;
|
|
267
|
+
const account = this.detailAccount();
|
|
268
|
+
const overview = this.overview;
|
|
269
|
+
if (!account || !overview)
|
|
270
|
+
return;
|
|
271
|
+
await this.guarded(async () => {
|
|
272
|
+
const outcome = await runAccountAction(this.options.ui, this.options.call, overview, account, action);
|
|
145
273
|
if (outcome === 'relaunch')
|
|
146
274
|
this.options.relaunch();
|
|
275
|
+
if (outcome === 'gone') {
|
|
276
|
+
this.detailOpen = false;
|
|
277
|
+
this.detailAccountId = undefined;
|
|
278
|
+
this.focus = 'list';
|
|
279
|
+
this.list.focused = true;
|
|
280
|
+
this.actionList.focused = false;
|
|
281
|
+
}
|
|
147
282
|
});
|
|
148
283
|
}
|
|
284
|
+
moveSection(delta) {
|
|
285
|
+
const index = SECTIONS.findIndex(tab => tab.id === this.section);
|
|
286
|
+
this.section = SECTIONS[(index + delta + SECTIONS.length) % SECTIONS.length].id;
|
|
287
|
+
this.detailOpen = false;
|
|
288
|
+
this.detailAccountId = undefined;
|
|
289
|
+
this.focus = 'list';
|
|
290
|
+
this.list.focused = true;
|
|
291
|
+
this.actionList.focused = false;
|
|
292
|
+
this.fill();
|
|
293
|
+
this.options.changed();
|
|
294
|
+
}
|
|
149
295
|
handleMouse(event) {
|
|
296
|
+
if (this.compact && this.detailOpen) {
|
|
297
|
+
this.focus = 'actions';
|
|
298
|
+
this.actionList.focused = true;
|
|
299
|
+
return this.actionList.handleMouse({ ...event, y: event.y - this.actionRowsStart });
|
|
300
|
+
}
|
|
301
|
+
if (this.wideListWidth && event.x >= this.wideListWidth + 3) {
|
|
302
|
+
if (!this.detailOpen)
|
|
303
|
+
return undefined;
|
|
304
|
+
this.focus = 'actions';
|
|
305
|
+
this.actionList.focused = true;
|
|
306
|
+
return this.actionList.handleMouse({ ...event, x: event.x - this.wideListWidth - 3, y: event.y - this.actionRowsStart });
|
|
307
|
+
}
|
|
308
|
+
this.focus = 'list';
|
|
309
|
+
this.actionList.focused = false;
|
|
310
|
+
this.list.focused = true;
|
|
150
311
|
return this.list.handleMouse(event);
|
|
151
312
|
}
|
|
152
313
|
handleInput(data) {
|
|
153
|
-
if (matchesKey(data, '
|
|
154
|
-
if (
|
|
155
|
-
this.
|
|
156
|
-
|
|
314
|
+
if (matchesKey(data, 'escape')) {
|
|
315
|
+
if (this.detailOpen) {
|
|
316
|
+
this.closeDetails();
|
|
317
|
+
return true;
|
|
318
|
+
}
|
|
319
|
+
if (this.focus !== 'list') {
|
|
320
|
+
this.focus = 'list';
|
|
321
|
+
this.list.focused = true;
|
|
322
|
+
this.actionList.focused = false;
|
|
323
|
+
this.options.changed();
|
|
324
|
+
return true;
|
|
157
325
|
}
|
|
326
|
+
return this.list.handleInput(data);
|
|
327
|
+
}
|
|
328
|
+
if (matchesKey(data, 'tab') || matchesKey(data, 'shift+tab')) {
|
|
329
|
+
const order = this.compact && this.detailOpen
|
|
330
|
+
? ['tabs', 'actions']
|
|
331
|
+
: this.detailOpen ? ['tabs', 'list', 'actions'] : ['tabs', 'list'];
|
|
332
|
+
const index = order.indexOf(this.focus);
|
|
333
|
+
const step = matchesKey(data, 'shift+tab') ? -1 : 1;
|
|
334
|
+
this.focus = order[(index + step + order.length) % order.length];
|
|
335
|
+
this.list.focused = this.focus === 'list';
|
|
336
|
+
this.actionList.focused = this.focus === 'actions';
|
|
337
|
+
this.options.changed();
|
|
158
338
|
return true;
|
|
159
339
|
}
|
|
340
|
+
if (this.focus === 'tabs') {
|
|
341
|
+
if (matchesKey(data, 'left')) {
|
|
342
|
+
this.moveSection(-1);
|
|
343
|
+
return true;
|
|
344
|
+
}
|
|
345
|
+
if (matchesKey(data, 'right')) {
|
|
346
|
+
this.moveSection(1);
|
|
347
|
+
return true;
|
|
348
|
+
}
|
|
349
|
+
if (matchesKey(data, 'enter')) {
|
|
350
|
+
this.focus = this.compact && this.detailOpen ? 'actions' : 'list';
|
|
351
|
+
this.list.focused = this.focus === 'list';
|
|
352
|
+
this.actionList.focused = this.focus === 'actions';
|
|
353
|
+
this.options.changed();
|
|
354
|
+
return true;
|
|
355
|
+
}
|
|
356
|
+
return false;
|
|
357
|
+
}
|
|
358
|
+
if (this.focus === 'actions')
|
|
359
|
+
return this.actionList.handleInput(data);
|
|
160
360
|
const before = this.list.query;
|
|
161
361
|
const used = this.list.handleInput(data);
|
|
162
362
|
if (this.list.query !== before)
|
|
@@ -169,13 +369,41 @@ export class AccountsPage {
|
|
|
169
369
|
return [];
|
|
170
370
|
if (!this.overview)
|
|
171
371
|
return [theme.fg(this.error ? 'error' : 'dim', this.error ? `Could not read accounts: ${this.error}` : 'Reading accounts…')];
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
372
|
+
const compact = width < 88;
|
|
373
|
+
if (compact !== this.compact)
|
|
374
|
+
this.fill(compact);
|
|
375
|
+
if (compact && this.detailOpen && this.detailAccount()) {
|
|
376
|
+
const details = this.detailLines(width, true);
|
|
377
|
+
this.actionRowsStart = details.length + 1;
|
|
378
|
+
const actions = this.actionList.render(width, Math.max(1, rows - this.actionRowsStart));
|
|
379
|
+
return [...details, '', ...actions].slice(0, rows).map(line => truncateToWidth(line, width, ''));
|
|
380
|
+
}
|
|
381
|
+
if (!compact) {
|
|
382
|
+
this.wideListWidth = Math.min(width - 36, Math.max(32, Math.floor((width - 3) * 0.42)));
|
|
383
|
+
const detailWidth = Math.max(1, width - this.wideListWidth - 3);
|
|
384
|
+
const list = this.list.render(this.wideListWidth, rows);
|
|
385
|
+
const details = this.detailLines(detailWidth);
|
|
386
|
+
let inspector = details;
|
|
387
|
+
const account = this.detailOpen ? this.detailAccount() : undefined;
|
|
388
|
+
if (this.detailOpen && account) {
|
|
389
|
+
this.fillActions(account);
|
|
390
|
+
this.actionRowsStart = inspector.length + 1;
|
|
391
|
+
inspector = [...inspector, '', ...this.actionList.render(detailWidth, Math.max(1, rows - this.actionRowsStart))];
|
|
392
|
+
}
|
|
393
|
+
inspector = inspector.slice(0, rows);
|
|
394
|
+
return Array.from({ length: Math.min(rows, Math.max(list.length, inspector.length)) }, (_, index) => {
|
|
395
|
+
const left = truncateToWidth(list[index] ?? '', this.wideListWidth, '');
|
|
396
|
+
const right = truncateToWidth(inspector[index] ?? '', detailWidth, '');
|
|
397
|
+
const gap = ' '.repeat(Math.max(0, this.wideListWidth - visibleWidth(left)));
|
|
398
|
+
return truncateToWidth(`${left}${gap}${theme.fg('dim', ' │ ')}${right}`, width, '');
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
this.wideListWidth = 0;
|
|
402
|
+
const details = this.detailLines(width).slice(1);
|
|
175
403
|
const detailRows = Math.min(details.length, 9, Math.max(0, rows - 2 - this.list.height));
|
|
176
404
|
const lines = this.list.render(width, Math.max(2, rows - 1 - (detailRows ? detailRows + 1 : 0)));
|
|
177
405
|
if (detailRows)
|
|
178
|
-
lines.push('', ...details.slice(0, detailRows)
|
|
406
|
+
lines.push('', ...details.slice(0, detailRows));
|
|
179
407
|
return lines.slice(0, rows).map(line => truncateToWidth(line, width, ''));
|
|
180
408
|
}
|
|
181
409
|
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Agents page (`/agents`): the agent profiles a subagent can start from (PROP-0029). Built-in
|
|
3
|
+
* and variant profiles are read-only and can be copied; a person's own global or project profiles
|
|
4
|
+
* open in their editor and can be deleted. The harness writes every file.
|
|
5
|
+
*/
|
|
6
|
+
import type { Theme } from '@earendil-works/pi-coding-agent';
|
|
7
|
+
import type { AgentProfile, ProfileListing, ProfileProblem } from "../../../agents/profiles.js";
|
|
8
|
+
import type { MenuCall, MenuUi } from "../../pluginMenu.js";
|
|
9
|
+
import { type TabInfo } from "../chrome.js";
|
|
10
|
+
import { type ListRow } from "../list.js";
|
|
11
|
+
import type { TerminalMouseInput } from "../mouse.js";
|
|
12
|
+
import type { TerminalPage } from "../page.js";
|
|
13
|
+
export interface AgentsOverview extends ProfileListing {
|
|
14
|
+
dirs: {
|
|
15
|
+
global: string;
|
|
16
|
+
project: string | null;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export interface AgentsPageOptions {
|
|
20
|
+
theme(): Theme;
|
|
21
|
+
call: MenuCall;
|
|
22
|
+
ui: MenuUi & {
|
|
23
|
+
input(title: string, placeholder?: string): Promise<string | undefined>;
|
|
24
|
+
};
|
|
25
|
+
/** Opens a file in the person's editor; absent when the host has none. */
|
|
26
|
+
edit?(path: string): Promise<void>;
|
|
27
|
+
changed(): void;
|
|
28
|
+
}
|
|
29
|
+
type Row = {
|
|
30
|
+
kind: 'new';
|
|
31
|
+
} | {
|
|
32
|
+
kind: 'profile';
|
|
33
|
+
profile: AgentProfile;
|
|
34
|
+
} | {
|
|
35
|
+
kind: 'problem';
|
|
36
|
+
problem: ProfileProblem;
|
|
37
|
+
};
|
|
38
|
+
export declare const AGENT_ACTIONS: {
|
|
39
|
+
readonly edit: "Edit in your editor";
|
|
40
|
+
readonly copy: "Copy to a new profile";
|
|
41
|
+
readonly remove: "Delete";
|
|
42
|
+
};
|
|
43
|
+
export declare const AGENT_SCOPES: {
|
|
44
|
+
readonly project: "Project: this repository only";
|
|
45
|
+
readonly global: "Global: every folder";
|
|
46
|
+
};
|
|
47
|
+
export declare class AgentsPage implements TerminalPage {
|
|
48
|
+
private readonly options;
|
|
49
|
+
presentation: "fullscreen";
|
|
50
|
+
private overview;
|
|
51
|
+
private error;
|
|
52
|
+
private busy;
|
|
53
|
+
private disposed;
|
|
54
|
+
private readonly list;
|
|
55
|
+
constructor(options: AgentsPageOptions);
|
|
56
|
+
get focused(): boolean;
|
|
57
|
+
set focused(value: boolean);
|
|
58
|
+
activate(): void;
|
|
59
|
+
dispose(): void;
|
|
60
|
+
tabs(): readonly TabInfo[];
|
|
61
|
+
activeTab(): string;
|
|
62
|
+
hint(): string;
|
|
63
|
+
reload(): Promise<void>;
|
|
64
|
+
rows(): ListRow<Row>[];
|
|
65
|
+
private attempt;
|
|
66
|
+
private open;
|
|
67
|
+
private create;
|
|
68
|
+
activateRow(row: Row): Promise<void>;
|
|
69
|
+
private remove;
|
|
70
|
+
handleMouse(event: TerminalMouseInput): import("@earendil-works/pi-tui").TuiMouseEventResult | undefined;
|
|
71
|
+
handleInput(data: string): boolean;
|
|
72
|
+
render(width: number, rows: number): string[];
|
|
73
|
+
}
|
|
74
|
+
export {};
|