@meistrari/remy-cli 1.1.1 → 1.2.0
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 +6 -3
- package/dist/remy.js +272 -75
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -42,7 +42,9 @@ Use `Tab` while writing the request to complete a local file path. Remy attaches
|
|
|
42
42
|
|
|
43
43
|
### Resume or follow up
|
|
44
44
|
|
|
45
|
-
Use
|
|
45
|
+
Use Up/Down to select a session in the dashboard, Left/Right to load the adjacent page, and `Enter` to open it. After the dashboard list or repository-review list has focus, Vim keys work too: `j`/`k` move, `h`/`l` go to the previous/next dashboard page (or clear/mark a repository), `G` goes to the last row, and `gg` goes to the first row. Press `Shift+L` in the dashboard to start a logout confirmation. In a session, type a follow-up and press `Enter` to send it.
|
|
46
|
+
|
|
47
|
+
Drag to select visible text in any Remy view; Remy copies it to the local clipboard and emits OSC 52 for terminal or remote-session clipboard support, then clears the selection highlight. When the conversation has focus, press `Tab` to return to the composer.
|
|
46
48
|
|
|
47
49
|
| Control | Result |
|
|
48
50
|
| --- | --- |
|
|
@@ -50,11 +52,12 @@ Use the arrow keys to select a session in the dashboard and press `Enter` to ope
|
|
|
50
52
|
| `/complete` | Complete an open session after its active turn stops. |
|
|
51
53
|
| `/sessions` | Return to the dashboard. |
|
|
52
54
|
| `/new` | Start another new-session flow. |
|
|
55
|
+
| `/logout` | Sign out of Remy after confirmation. |
|
|
53
56
|
| `/help` | Show session controls. |
|
|
54
57
|
| `/exit` | Leave Remy. |
|
|
55
58
|
| `Ctrl+O` | Expand or collapse activity details. |
|
|
56
59
|
|
|
57
|
-
After a session becomes terminal, press `Esc` to return to the dashboard or `n` to start new work. From the dashboard, press `
|
|
60
|
+
After a session becomes terminal, press `Esc` to return to the dashboard or `n` to start new work. From the dashboard, press `q` to quit. `Shift+L` in the dashboard and `/logout` in a session ask for confirmation; press `y` to sign out or `Esc` to cancel. `Ctrl+C` exits Remy from the dashboard, wizard, or session view.
|
|
58
61
|
|
|
59
62
|
## Commands for direct or scripted use
|
|
60
63
|
|
|
@@ -112,7 +115,7 @@ remy login \
|
|
|
112
115
|
--target-application-id <target-application-uuid>
|
|
113
116
|
```
|
|
114
117
|
|
|
115
|
-
Remy stores endpoint configuration in `~/.config/remy/config.json` and tokens in `~/.local/state/remy/tokens.json`. Set `XDG_CONFIG_HOME` or `XDG_STATE_HOME` to use different base directories. `remy logout` removes tokens but retains endpoint configuration; pass `--api-url` to require that the supplied URL matches the active API host before logging out.
|
|
118
|
+
Remy stores endpoint configuration in `~/.config/remy/config.json` and tokens in `~/.local/state/remy/tokens.json`. Set `XDG_CONFIG_HOME` or `XDG_STATE_HOME` to use different base directories. It refreshes an expired access token before each Coding Agent API request, so long-running sessions and reconnects remain authenticated. `remy logout` removes tokens but retains endpoint configuration; pass `--api-url` to require that the supplied URL matches the active API host before logging out.
|
|
116
119
|
|
|
117
120
|
## Complete command reference
|
|
118
121
|
|
package/dist/remy.js
CHANGED
|
@@ -34355,7 +34355,7 @@ async function sleepMs(ms, signal) {
|
|
|
34355
34355
|
}
|
|
34356
34356
|
|
|
34357
34357
|
// src/tui/dashboard.ts
|
|
34358
|
-
import { bg, BoxRenderable, bold as bold2, CliRenderEvents, fg as fg3,
|
|
34358
|
+
import { bg, BoxRenderable, bold as bold2, CliRenderEvents as CliRenderEvents2, fg as fg3, ScrollBoxRenderable, stringToStyledText as stringToStyledText2, TextRenderable } from "@opentui/core";
|
|
34359
34359
|
|
|
34360
34360
|
// src/tui/composer-divider.ts
|
|
34361
34361
|
import { dim as dim2, fg as fg2, StyledText as StyledText2 } from "@opentui/core";
|
|
@@ -34551,18 +34551,20 @@ function renderComposerDivider({ width, tag, leadLabel }) {
|
|
|
34551
34551
|
}
|
|
34552
34552
|
|
|
34553
34553
|
// src/tui/renderer.ts
|
|
34554
|
-
import { createCliRenderer } from "@opentui/core";
|
|
34554
|
+
import { CliRenderEvents, createClipboard, createCliRenderer, createHostClipboard, createRendererClipboardAdapter } from "@opentui/core";
|
|
34555
34555
|
var rendererDestroyed = new WeakMap;
|
|
34556
34556
|
var enterAlternateScreen = "\x1B[?1049h";
|
|
34557
34557
|
var clearScreen = "\x1B[2J\x1B[H";
|
|
34558
34558
|
var exitAlternateScreen = "\x1B[?1049l";
|
|
34559
34559
|
var transitionScreenWriter;
|
|
34560
|
+
var ignoreTerminalFocus = (sequence) => sequence === "\x1B[I" || sequence === "\x1B[O";
|
|
34560
34561
|
async function createRemyRenderer({
|
|
34561
34562
|
createRenderer = createCliRenderer
|
|
34562
34563
|
} = {}) {
|
|
34563
34564
|
let resolveDestroyed = () => {
|
|
34564
34565
|
return;
|
|
34565
34566
|
};
|
|
34567
|
+
let selectionClipboard;
|
|
34566
34568
|
const destroyed = new Promise((resolve) => {
|
|
34567
34569
|
resolveDestroyed = resolve;
|
|
34568
34570
|
});
|
|
@@ -34571,9 +34573,34 @@ async function createRemyRenderer({
|
|
|
34571
34573
|
exitSignals: [],
|
|
34572
34574
|
screenMode: "alternate-screen",
|
|
34573
34575
|
useMouse: true,
|
|
34574
|
-
|
|
34576
|
+
prependInputHandlers: [ignoreTerminalFocus],
|
|
34577
|
+
onDestroy: () => {
|
|
34578
|
+
selectionClipboard?.dispose().catch(() => {
|
|
34579
|
+
return;
|
|
34580
|
+
});
|
|
34581
|
+
resolveDestroyed();
|
|
34582
|
+
}
|
|
34575
34583
|
});
|
|
34576
34584
|
transitionScreenWriter = undefined;
|
|
34585
|
+
try {
|
|
34586
|
+
selectionClipboard = createClipboard({
|
|
34587
|
+
host: createHostClipboard(),
|
|
34588
|
+
terminal: createRendererClipboardAdapter(renderer)
|
|
34589
|
+
});
|
|
34590
|
+
} catch {}
|
|
34591
|
+
renderer.on(CliRenderEvents.SELECTION, (selection) => {
|
|
34592
|
+
const text = selection.getSelectedText();
|
|
34593
|
+
if (text.length === 0)
|
|
34594
|
+
return;
|
|
34595
|
+
renderer.clearSelection();
|
|
34596
|
+
if (selectionClipboard) {
|
|
34597
|
+
selectionClipboard.writeText(text, { destination: "all-available" }).catch(() => {
|
|
34598
|
+
return;
|
|
34599
|
+
});
|
|
34600
|
+
return;
|
|
34601
|
+
}
|
|
34602
|
+
renderer.copyToClipboardOSC52(text);
|
|
34603
|
+
});
|
|
34577
34604
|
rendererDestroyed.set(renderer, destroyed);
|
|
34578
34605
|
return renderer;
|
|
34579
34606
|
}
|
|
@@ -34611,6 +34638,7 @@ async function createDashboardTui({
|
|
|
34611
34638
|
}) {
|
|
34612
34639
|
const renderer = await createRenderer();
|
|
34613
34640
|
let inputHandler;
|
|
34641
|
+
let keyHandler;
|
|
34614
34642
|
let resizeHandler;
|
|
34615
34643
|
let rendererDestroyed2 = false;
|
|
34616
34644
|
try {
|
|
@@ -34637,8 +34665,7 @@ async function createDashboardTui({
|
|
|
34637
34665
|
previewBand.visible = showPreview;
|
|
34638
34666
|
previewBand.content = showPreview ? formatSessionPreview({ session: selectedSession, width: contentWidth }) : stringToStyledText2("");
|
|
34639
34667
|
separator.content = renderComposerDivider({ width: contentWidth });
|
|
34640
|
-
footer.content =
|
|
34641
|
-
input.placeholder = logoutConfirmationOpen ? "Log out of Remy?" : "Type here to start a new session";
|
|
34668
|
+
footer.content = dashboardFooter({ loadingPage, pageError, rowsBelow, logoutConfirmationOpen });
|
|
34642
34669
|
renderer.requestRender();
|
|
34643
34670
|
}, finish = function(value) {
|
|
34644
34671
|
if (settled)
|
|
@@ -34650,6 +34677,11 @@ async function createDashboardTui({
|
|
|
34650
34677
|
return;
|
|
34651
34678
|
settled = true;
|
|
34652
34679
|
rejectDraft(error93);
|
|
34680
|
+
}, moveSelectionTo = function(index) {
|
|
34681
|
+
if (page.sessions.length === 0)
|
|
34682
|
+
return;
|
|
34683
|
+
selectedIndex = Math.max(0, Math.min(index, page.sessions.length - 1));
|
|
34684
|
+
render();
|
|
34653
34685
|
}, handleSessionKey = function(key) {
|
|
34654
34686
|
if (logoutConfirmationOpen) {
|
|
34655
34687
|
key.preventDefault();
|
|
@@ -34661,16 +34693,57 @@ async function createDashboardTui({
|
|
|
34661
34693
|
}
|
|
34662
34694
|
return;
|
|
34663
34695
|
}
|
|
34696
|
+
const listFocused = renderer.currentFocusedRenderable === listRegion;
|
|
34664
34697
|
if (key.name === "down") {
|
|
34665
34698
|
key.preventDefault();
|
|
34699
|
+
awaitingVimGoToTop = false;
|
|
34666
34700
|
moveSelection("next");
|
|
34667
34701
|
return;
|
|
34668
34702
|
}
|
|
34669
34703
|
if (key.name === "up") {
|
|
34670
34704
|
key.preventDefault();
|
|
34705
|
+
awaitingVimGoToTop = false;
|
|
34671
34706
|
moveSelection("previous");
|
|
34672
34707
|
return;
|
|
34673
34708
|
}
|
|
34709
|
+
if (key.name === "right") {
|
|
34710
|
+
key.preventDefault();
|
|
34711
|
+
awaitingVimGoToTop = false;
|
|
34712
|
+
loadAdjacentPage("next");
|
|
34713
|
+
return;
|
|
34714
|
+
}
|
|
34715
|
+
if (key.name === "left") {
|
|
34716
|
+
key.preventDefault();
|
|
34717
|
+
awaitingVimGoToTop = false;
|
|
34718
|
+
loadAdjacentPage("previous");
|
|
34719
|
+
return;
|
|
34720
|
+
}
|
|
34721
|
+
if (listFocused && (key.name === "G" || key.name === "g" && key.shift)) {
|
|
34722
|
+
key.preventDefault();
|
|
34723
|
+
awaitingVimGoToTop = false;
|
|
34724
|
+
moveSelectionTo(page.sessions.length - 1);
|
|
34725
|
+
return;
|
|
34726
|
+
}
|
|
34727
|
+
if (listFocused && key.name === "g") {
|
|
34728
|
+
key.preventDefault();
|
|
34729
|
+
if (awaitingVimGoToTop)
|
|
34730
|
+
moveSelectionTo(0);
|
|
34731
|
+
awaitingVimGoToTop = !awaitingVimGoToTop;
|
|
34732
|
+
return;
|
|
34733
|
+
}
|
|
34734
|
+
if (listFocused && (key.name === "j" || key.name === "k")) {
|
|
34735
|
+
key.preventDefault();
|
|
34736
|
+
awaitingVimGoToTop = false;
|
|
34737
|
+
moveSelection(key.name === "j" ? "next" : "previous");
|
|
34738
|
+
return;
|
|
34739
|
+
}
|
|
34740
|
+
if (listFocused && !key.shift && (key.name === "h" || key.name === "l")) {
|
|
34741
|
+
key.preventDefault();
|
|
34742
|
+
awaitingVimGoToTop = false;
|
|
34743
|
+
loadAdjacentPage(key.name === "l" ? "next" : "previous");
|
|
34744
|
+
return;
|
|
34745
|
+
}
|
|
34746
|
+
awaitingVimGoToTop = false;
|
|
34674
34747
|
if (key.name === "return" || key.name === "enter") {
|
|
34675
34748
|
key.preventDefault();
|
|
34676
34749
|
const session = page.sessions[selectedIndex];
|
|
@@ -34683,7 +34756,7 @@ async function createDashboardTui({
|
|
|
34683
34756
|
finish({ kind: "new" });
|
|
34684
34757
|
return;
|
|
34685
34758
|
}
|
|
34686
|
-
if (key.name === "l") {
|
|
34759
|
+
if (key.name === "L" || key.shift && key.name === "l") {
|
|
34687
34760
|
key.preventDefault();
|
|
34688
34761
|
logoutConfirmationOpen = true;
|
|
34689
34762
|
render();
|
|
@@ -34706,20 +34779,23 @@ async function createDashboardTui({
|
|
|
34706
34779
|
const previewBand = new TextRenderable(renderer, { content: "", flexShrink: 0 });
|
|
34707
34780
|
const separator = new TextRenderable(renderer, { content: "", flexShrink: 0 });
|
|
34708
34781
|
const footer = new TextRenderable(renderer, { content: "", flexShrink: 0 });
|
|
34709
|
-
|
|
34782
|
+
root.onMouseDown = (event) => {
|
|
34783
|
+
event.preventDefault();
|
|
34784
|
+
listRegion.focus();
|
|
34785
|
+
};
|
|
34710
34786
|
listRegion.add(content);
|
|
34711
34787
|
root.add(statusBand);
|
|
34712
34788
|
root.add(listRegion);
|
|
34713
34789
|
root.add(previewBand);
|
|
34714
34790
|
root.add(separator);
|
|
34715
34791
|
root.add(footer);
|
|
34716
|
-
root.add(input);
|
|
34717
34792
|
renderer.root.add(root);
|
|
34718
34793
|
let page = initialPage;
|
|
34719
34794
|
let pageIndex = 1;
|
|
34720
34795
|
let selectedIndex = 0;
|
|
34721
34796
|
let loadingPage = false;
|
|
34722
34797
|
let pageError;
|
|
34798
|
+
let awaitingVimGoToTop = false;
|
|
34723
34799
|
let logoutConfirmationOpen = false;
|
|
34724
34800
|
let destroyed = false;
|
|
34725
34801
|
let rendererDestroyPromise;
|
|
@@ -34734,16 +34810,10 @@ async function createDashboardTui({
|
|
|
34734
34810
|
resolveAction = resolve;
|
|
34735
34811
|
rejectDraft = reject;
|
|
34736
34812
|
});
|
|
34737
|
-
async function
|
|
34813
|
+
async function loadAdjacentPage(direction) {
|
|
34738
34814
|
if (loadingPage)
|
|
34739
34815
|
return;
|
|
34740
|
-
const isBoundary = direction === "next" ? selectedIndex === page.sessions.length - 1 : selectedIndex === 0;
|
|
34741
34816
|
const canLoad = direction === "next" ? page.hasMore : page.canGoPrevious;
|
|
34742
|
-
if (!isBoundary) {
|
|
34743
|
-
selectedIndex += direction === "next" ? 1 : -1;
|
|
34744
|
-
render();
|
|
34745
|
-
return;
|
|
34746
|
-
}
|
|
34747
34817
|
if (!canLoad)
|
|
34748
34818
|
return;
|
|
34749
34819
|
loadingPage = true;
|
|
@@ -34760,6 +34830,17 @@ async function createDashboardTui({
|
|
|
34760
34830
|
render();
|
|
34761
34831
|
}
|
|
34762
34832
|
}
|
|
34833
|
+
async function moveSelection(direction) {
|
|
34834
|
+
if (loadingPage || page.sessions.length === 0)
|
|
34835
|
+
return;
|
|
34836
|
+
const isBoundary = direction === "next" ? selectedIndex === page.sessions.length - 1 : selectedIndex === 0;
|
|
34837
|
+
if (!isBoundary) {
|
|
34838
|
+
selectedIndex += direction === "next" ? 1 : -1;
|
|
34839
|
+
render();
|
|
34840
|
+
return;
|
|
34841
|
+
}
|
|
34842
|
+
await loadAdjacentPage(direction);
|
|
34843
|
+
}
|
|
34763
34844
|
inputHandler = (sequence) => {
|
|
34764
34845
|
if (sequence === "\x03") {
|
|
34765
34846
|
finish({ kind: "interrupt" });
|
|
@@ -34767,16 +34848,17 @@ async function createDashboardTui({
|
|
|
34767
34848
|
}
|
|
34768
34849
|
return false;
|
|
34769
34850
|
};
|
|
34770
|
-
|
|
34851
|
+
keyHandler = handleSessionKey;
|
|
34771
34852
|
renderer.addInputHandler(inputHandler);
|
|
34772
|
-
renderer.on(
|
|
34853
|
+
renderer.keyInput.on("keypress", keyHandler);
|
|
34854
|
+
renderer.on(CliRenderEvents2.RENDER_ERROR, (event) => fail(event.error));
|
|
34773
34855
|
resizeHandler = () => {
|
|
34774
34856
|
if (!destroyed)
|
|
34775
34857
|
render();
|
|
34776
34858
|
};
|
|
34777
|
-
renderer.on(
|
|
34778
|
-
input.focus();
|
|
34859
|
+
renderer.on(CliRenderEvents2.RESIZE, resizeHandler);
|
|
34779
34860
|
render();
|
|
34861
|
+
listRegion.focus();
|
|
34780
34862
|
return {
|
|
34781
34863
|
waitForAction: async () => await action,
|
|
34782
34864
|
destroy() {
|
|
@@ -34785,8 +34867,10 @@ async function createDashboardTui({
|
|
|
34785
34867
|
destroyed = true;
|
|
34786
34868
|
if (inputHandler)
|
|
34787
34869
|
renderer.removeInputHandler(inputHandler);
|
|
34870
|
+
if (keyHandler)
|
|
34871
|
+
renderer.keyInput.off("keypress", keyHandler);
|
|
34788
34872
|
if (resizeHandler)
|
|
34789
|
-
renderer.off(
|
|
34873
|
+
renderer.off(CliRenderEvents2.RESIZE, resizeHandler);
|
|
34790
34874
|
rendererDestroyed2 = true;
|
|
34791
34875
|
renderer.destroy();
|
|
34792
34876
|
},
|
|
@@ -34797,8 +34881,10 @@ async function createDashboardTui({
|
|
|
34797
34881
|
} catch (error93) {
|
|
34798
34882
|
if (inputHandler)
|
|
34799
34883
|
renderer.removeInputHandler(inputHandler);
|
|
34884
|
+
if (keyHandler)
|
|
34885
|
+
renderer.keyInput.off("keypress", keyHandler);
|
|
34800
34886
|
if (resizeHandler)
|
|
34801
|
-
renderer.off(
|
|
34887
|
+
renderer.off(CliRenderEvents2.RESIZE, resizeHandler);
|
|
34802
34888
|
if (!rendererDestroyed2) {
|
|
34803
34889
|
rendererDestroyed2 = true;
|
|
34804
34890
|
renderer.destroy();
|
|
@@ -34811,22 +34897,24 @@ function formatSessionRow({ session, selected, width }) {
|
|
|
34811
34897
|
const outcome = sessionOutcome(session);
|
|
34812
34898
|
const updated = formatUpdatedAt(session.updatedAt ?? session.createdAt);
|
|
34813
34899
|
const prefix = `#${session.sessionNumber} `;
|
|
34814
|
-
const title = truncate(session.title ?? "Untitled session", Math.max(8,
|
|
34900
|
+
const title = truncate(session.title ?? "Untitled session", Math.max(8, Math.min(40, Math.floor(width * 0.24))));
|
|
34901
|
+
const attribution = `${session.creator} (${session.source})`;
|
|
34902
|
+
const metadataWidth = Math.max(0, width - prefix.length - title.length - activity.label.length - 6);
|
|
34903
|
+
const metadata = truncate(`${attribution} \xB7 ${outcome} \xB7 ${updated}`, metadataWidth);
|
|
34815
34904
|
const line = [
|
|
34816
34905
|
fg3(PALETTE.dimText)(prefix),
|
|
34817
34906
|
fg3(PALETTE.bodyText)(title),
|
|
34818
34907
|
fg3(PALETTE.dimText)(" "),
|
|
34819
34908
|
bold2(fg3(activity.color)(activity.label)),
|
|
34820
34909
|
fg3(PALETTE.dimText)(" "),
|
|
34821
|
-
fg3(PALETTE.
|
|
34822
|
-
fg3(PALETTE.dimText)(` ${updated}`)
|
|
34910
|
+
fg3(PALETTE.dimText)(metadata)
|
|
34823
34911
|
];
|
|
34824
34912
|
const lineLength = line.reduce((total, chunk) => total + chunk.text.length, 0);
|
|
34825
34913
|
const padded = selected ? [...line, fg3(PALETTE.dimText)(" ".repeat(Math.max(0, width - lineLength)))] : line;
|
|
34826
34914
|
return selected ? padded.map((chunk) => bg(PALETTE.selectionBg)(chunk)) : padded;
|
|
34827
34915
|
}
|
|
34828
34916
|
function visibleListWindow({ sessionCount, selectedIndex, height }) {
|
|
34829
|
-
const rowCount = Math.max(1, height -
|
|
34917
|
+
const rowCount = Math.max(1, height - 9);
|
|
34830
34918
|
const firstIndex = Math.min(Math.max(0, selectedIndex - Math.floor(rowCount / 2)), Math.max(0, sessionCount - rowCount));
|
|
34831
34919
|
return { firstIndex, rowCount };
|
|
34832
34920
|
}
|
|
@@ -34929,7 +35017,9 @@ function dashboardStatusBand({ page, pageIndex }) {
|
|
|
34929
35017
|
paging.push("more \u2192");
|
|
34930
35018
|
return `remy \xB7 sessions \xB7 ${openCount} open \xB7 ${paging.join(" \xB7 ")}`;
|
|
34931
35019
|
}
|
|
34932
|
-
function dashboardFooter({ loadingPage, pageError,
|
|
35020
|
+
function dashboardFooter({ loadingPage, pageError, rowsBelow, logoutConfirmationOpen }) {
|
|
35021
|
+
if (logoutConfirmationOpen)
|
|
35022
|
+
return "Log out of Remy? y confirms \xB7 Esc cancels";
|
|
34933
35023
|
const parts = [];
|
|
34934
35024
|
if (loadingPage)
|
|
34935
35025
|
parts.push("Loading sessions\u2026");
|
|
@@ -34937,9 +35027,7 @@ function dashboardFooter({ loadingPage, pageError, page, rowsBelow }) {
|
|
|
34937
35027
|
parts.push(`Could not load sessions: ${pageError}`);
|
|
34938
35028
|
if (rowsBelow > 0)
|
|
34939
35029
|
parts.push(`\u2193 ${rowsBelow} more below`);
|
|
34940
|
-
|
|
34941
|
-
parts.push("\u2191\u2193 pages at the ends of the list");
|
|
34942
|
-
parts.push("\u2191\u2193 move \xB7 \u23CE open \xB7 n new session \xB7 q quit \xB7 ctrl+c discard & quit");
|
|
35030
|
+
parts.push("\u2191\u2193 move \xB7 \u23CE open \xB7 n new session \xB7 \u21E7l log out \xB7 q quit");
|
|
34943
35031
|
return parts.join(" \xB7 ");
|
|
34944
35032
|
}
|
|
34945
35033
|
function truncate(value, width) {
|
|
@@ -34952,7 +35040,7 @@ async function createDefaultRenderer() {
|
|
|
34952
35040
|
}
|
|
34953
35041
|
|
|
34954
35042
|
// src/tui/new-session-wizard.ts
|
|
34955
|
-
import { bg as bg3, BoxRenderable as BoxRenderable3, bold as bold3, CliRenderEvents as
|
|
35043
|
+
import { bg as bg3, BoxRenderable as BoxRenderable3, bold as bold3, CliRenderEvents as CliRenderEvents3, dim as dim3, fg as fg5, ScrollBoxRenderable as ScrollBoxRenderable2, StyledText as StyledText4, stringToStyledText as stringToStyledText4, TextRenderable as TextRenderable3 } from "@opentui/core";
|
|
34956
35044
|
|
|
34957
35045
|
// src/tui/composer.ts
|
|
34958
35046
|
import { BoxRenderable as BoxRenderable2, TextRenderable as TextRenderable2, TextareaRenderable } from "@opentui/core";
|
|
@@ -35297,7 +35385,7 @@ async function createNewSessionWizard({
|
|
|
35297
35385
|
if (composerMounted)
|
|
35298
35386
|
composer.render();
|
|
35299
35387
|
if (repositoryListVisible) {
|
|
35300
|
-
renderer.once(
|
|
35388
|
+
renderer.once(CliRenderEvents3.FRAME, () => {
|
|
35301
35389
|
if (destroyed || step !== "repositories" && step !== "repositorySearch")
|
|
35302
35390
|
return;
|
|
35303
35391
|
const nextRepositoryHeaderHeight = Math.min(content.virtualLineCount, Math.floor(renderer.height * 0.45));
|
|
@@ -35526,6 +35614,24 @@ async function createNewSessionWizard({
|
|
|
35526
35614
|
if (step !== "repositories")
|
|
35527
35615
|
return;
|
|
35528
35616
|
const choices = visibleRepositories();
|
|
35617
|
+
const repositoryListFocused = renderer.currentFocusedRenderable === repositoryScrollBox;
|
|
35618
|
+
if (repositoryListFocused && (key.name === "G" || key.name === "g" && key.shift)) {
|
|
35619
|
+
key.preventDefault();
|
|
35620
|
+
awaitingVimGoToTop = false;
|
|
35621
|
+
if (choices.length > 0)
|
|
35622
|
+
repositoryIndex = choices.length - 1;
|
|
35623
|
+
render();
|
|
35624
|
+
return;
|
|
35625
|
+
}
|
|
35626
|
+
if (repositoryListFocused && key.name === "g") {
|
|
35627
|
+
key.preventDefault();
|
|
35628
|
+
if (awaitingVimGoToTop && choices.length > 0)
|
|
35629
|
+
repositoryIndex = 0;
|
|
35630
|
+
awaitingVimGoToTop = !awaitingVimGoToTop;
|
|
35631
|
+
render();
|
|
35632
|
+
return;
|
|
35633
|
+
}
|
|
35634
|
+
awaitingVimGoToTop = false;
|
|
35529
35635
|
if (key.name === "up" || key.name === "down" || key.name === "j" || key.name === "k") {
|
|
35530
35636
|
key.preventDefault();
|
|
35531
35637
|
if (choices.length > 0)
|
|
@@ -35599,6 +35705,7 @@ async function createNewSessionWizard({
|
|
|
35599
35705
|
let completionIndex = 0;
|
|
35600
35706
|
let pathCompletionMenuOpen = false;
|
|
35601
35707
|
let repositoryHeaderHeight;
|
|
35708
|
+
let awaitingVimGoToTop = false;
|
|
35602
35709
|
const manualSelections = new Map;
|
|
35603
35710
|
let searchSelections;
|
|
35604
35711
|
let suggestedIds = new Set;
|
|
@@ -35822,7 +35929,7 @@ async function createNewSessionWizard({
|
|
|
35822
35929
|
};
|
|
35823
35930
|
renderer.addInputHandler(inputHandler);
|
|
35824
35931
|
renderer.keyInput.on("keypress", keyHandler);
|
|
35825
|
-
renderer.on(
|
|
35932
|
+
renderer.on(CliRenderEvents3.RENDER_ERROR, (event) => fail(event.error));
|
|
35826
35933
|
resizeHandler = () => {
|
|
35827
35934
|
if (destroyed)
|
|
35828
35935
|
return;
|
|
@@ -35831,7 +35938,7 @@ async function createNewSessionWizard({
|
|
|
35831
35938
|
repositoryIndex = Math.min(repositoryIndex, choices.length - 1);
|
|
35832
35939
|
render();
|
|
35833
35940
|
};
|
|
35834
|
-
renderer.on(
|
|
35941
|
+
renderer.on(CliRenderEvents3.RESIZE, resizeHandler);
|
|
35835
35942
|
render();
|
|
35836
35943
|
if (!Array.isArray(repositories))
|
|
35837
35944
|
requestRepositoryLoad({ retry: false });
|
|
@@ -35846,7 +35953,7 @@ async function createNewSessionWizard({
|
|
|
35846
35953
|
if (keyHandler)
|
|
35847
35954
|
renderer.keyInput.off("keypress", keyHandler);
|
|
35848
35955
|
if (resizeHandler)
|
|
35849
|
-
renderer.off(
|
|
35956
|
+
renderer.off(CliRenderEvents3.RESIZE, resizeHandler);
|
|
35850
35957
|
if (suggestionSpinnerTimer) {
|
|
35851
35958
|
clearInterval(suggestionSpinnerTimer);
|
|
35852
35959
|
suggestionSpinnerTimer = undefined;
|
|
@@ -35867,7 +35974,7 @@ async function createNewSessionWizard({
|
|
|
35867
35974
|
if (keyHandler)
|
|
35868
35975
|
renderer.keyInput.off("keypress", keyHandler);
|
|
35869
35976
|
if (resizeHandler)
|
|
35870
|
-
renderer.off(
|
|
35977
|
+
renderer.off(CliRenderEvents3.RESIZE, resizeHandler);
|
|
35871
35978
|
if (!rendererDestroyed2) {
|
|
35872
35979
|
rendererDestroyed2 = true;
|
|
35873
35980
|
renderer.destroy();
|
|
@@ -35950,11 +36057,12 @@ async function createDefaultRenderer2() {
|
|
|
35950
36057
|
}
|
|
35951
36058
|
|
|
35952
36059
|
// src/tui/session-view.ts
|
|
35953
|
-
import { CliRenderEvents as
|
|
36060
|
+
import { CliRenderEvents as CliRenderEvents4, BoxRenderable as BoxRenderable4, bg as bg4, bold as bold4, dim as dim4, fg as fg6, ScrollBoxRenderable as ScrollBoxRenderable3, StyledText as StyledText5, stringToStyledText as stringToStyledText5, TextRenderable as TextRenderable4 } from "@opentui/core";
|
|
35954
36061
|
var composerSlashCommands = [
|
|
35955
36062
|
{ value: "/sessions", description: "Back to the session list" },
|
|
35956
36063
|
{ value: "/complete", description: "Finish this session" },
|
|
35957
36064
|
{ value: "/new", description: "Start a new session" },
|
|
36065
|
+
{ value: "/logout", description: "Sign out of Remy" },
|
|
35958
36066
|
{ value: "/help", description: "Show what you can do here" },
|
|
35959
36067
|
{ value: "/exit", description: "Leave Remy" }
|
|
35960
36068
|
];
|
|
@@ -36180,6 +36288,7 @@ async function createSessionTui({
|
|
|
36180
36288
|
let stopState = { kind: "idle" };
|
|
36181
36289
|
let completeState = { kind: "idle" };
|
|
36182
36290
|
let helpVisible = false;
|
|
36291
|
+
let logoutConfirmationOpen = false;
|
|
36183
36292
|
let latestState = controller.getState();
|
|
36184
36293
|
let spinnerFrameIndex = 0;
|
|
36185
36294
|
let renderedTranscript;
|
|
@@ -36236,6 +36345,17 @@ async function createSessionTui({
|
|
|
36236
36345
|
return true;
|
|
36237
36346
|
};
|
|
36238
36347
|
keyHandler = (key) => {
|
|
36348
|
+
if (logoutConfirmationOpen) {
|
|
36349
|
+
key.preventDefault();
|
|
36350
|
+
if (key.name === "y") {
|
|
36351
|
+
finish({ kind: "logout" });
|
|
36352
|
+
} else if (key.name === "escape") {
|
|
36353
|
+
logoutConfirmationOpen = false;
|
|
36354
|
+
composerFeedback = undefined;
|
|
36355
|
+
render();
|
|
36356
|
+
}
|
|
36357
|
+
return;
|
|
36358
|
+
}
|
|
36239
36359
|
if (key.ctrl && key.name === "c") {
|
|
36240
36360
|
key.preventDefault();
|
|
36241
36361
|
finish({ kind: "exit", signal: "SIGINT" });
|
|
@@ -36311,6 +36431,10 @@ async function createSessionTui({
|
|
|
36311
36431
|
}
|
|
36312
36432
|
}
|
|
36313
36433
|
composer.onKeyDown = (key) => {
|
|
36434
|
+
if (logoutConfirmationOpen) {
|
|
36435
|
+
key.preventDefault();
|
|
36436
|
+
return;
|
|
36437
|
+
}
|
|
36314
36438
|
if (slashCompletions.length > 0 && (key.name === "up" || key.name === "down")) {
|
|
36315
36439
|
key.preventDefault();
|
|
36316
36440
|
slashCompletionIndex = (slashCompletionIndex + (key.name === "up" ? -1 : 1) + slashCompletions.length) % slashCompletions.length;
|
|
@@ -36464,6 +36588,14 @@ async function createSessionTui({
|
|
|
36464
36588
|
await handleCompleteRequest();
|
|
36465
36589
|
return true;
|
|
36466
36590
|
}
|
|
36591
|
+
if (command === "/logout") {
|
|
36592
|
+
composer.setText("");
|
|
36593
|
+
composerDraft = { ...composerDraft, text: "" };
|
|
36594
|
+
logoutConfirmationOpen = true;
|
|
36595
|
+
composerFeedback = "Log out of Remy? Press y to confirm or Escape to cancel.";
|
|
36596
|
+
render();
|
|
36597
|
+
return true;
|
|
36598
|
+
}
|
|
36467
36599
|
if (command === "/help") {
|
|
36468
36600
|
composer.setText("");
|
|
36469
36601
|
composerDraft = { ...composerDraft, text: "" };
|
|
@@ -36590,12 +36722,12 @@ async function createSessionTui({
|
|
|
36590
36722
|
renderer.addInputHandler(inputHandler);
|
|
36591
36723
|
renderer.keyInput.on("keypress", keyHandler);
|
|
36592
36724
|
composer.setText(composerDraft.text);
|
|
36593
|
-
renderer.on(
|
|
36725
|
+
renderer.on(CliRenderEvents4.RENDER_ERROR, (event) => finish(undefined, event.error));
|
|
36594
36726
|
resizeHandler = () => {
|
|
36595
36727
|
if (!destroyed)
|
|
36596
36728
|
render();
|
|
36597
36729
|
};
|
|
36598
|
-
renderer.on(
|
|
36730
|
+
renderer.on(CliRenderEvents4.RESIZE, resizeHandler);
|
|
36599
36731
|
render();
|
|
36600
36732
|
if (latestState.aggregateStatus === "open")
|
|
36601
36733
|
composer.focus();
|
|
@@ -36615,7 +36747,7 @@ async function createSessionTui({
|
|
|
36615
36747
|
if (keyHandler)
|
|
36616
36748
|
renderer.keyInput.off("keypress", keyHandler);
|
|
36617
36749
|
if (resizeHandler)
|
|
36618
|
-
renderer.off(
|
|
36750
|
+
renderer.off(CliRenderEvents4.RESIZE, resizeHandler);
|
|
36619
36751
|
composerLayout.destroy();
|
|
36620
36752
|
rendererDestroyed2 = true;
|
|
36621
36753
|
renderer.destroy();
|
|
@@ -36632,7 +36764,7 @@ async function createSessionTui({
|
|
|
36632
36764
|
if (keyHandler)
|
|
36633
36765
|
renderer.keyInput.off("keypress", keyHandler);
|
|
36634
36766
|
if (resizeHandler)
|
|
36635
|
-
renderer.off(
|
|
36767
|
+
renderer.off(CliRenderEvents4.RESIZE, resizeHandler);
|
|
36636
36768
|
if (!rendererDestroyed2) {
|
|
36637
36769
|
rendererDestroyed2 = true;
|
|
36638
36770
|
renderer.destroy();
|
|
@@ -36660,11 +36792,11 @@ var helpEntries = [
|
|
|
36660
36792
|
{ group: "command", token: "/new", description: "start a new session" },
|
|
36661
36793
|
{ group: "command", token: "/sessions", description: "back to the session list" },
|
|
36662
36794
|
{ group: "command", token: "/complete", description: "finish this session" },
|
|
36795
|
+
{ group: "command", token: "/logout", description: "sign out of Remy" },
|
|
36663
36796
|
{ group: "command", token: "/exit", description: "leave Remy" },
|
|
36664
36797
|
{ group: "key", token: "esc", description: "stop Remy\u2019s current turn" },
|
|
36665
36798
|
{ group: "key", token: "ctrl+o", description: "expand the activity trail" },
|
|
36666
|
-
{ group: "key", token: "ctrl+r", description: "retry the oldest failed message" }
|
|
36667
|
-
{ group: "key", token: "ctrl+c", description: "quit" }
|
|
36799
|
+
{ group: "key", token: "ctrl+r", description: "retry the oldest failed message" }
|
|
36668
36800
|
];
|
|
36669
36801
|
function renderHelpPanel() {
|
|
36670
36802
|
const width = helpEntries.reduce((max, entry) => Math.max(max, entry.token.length), 0) + 2;
|
|
@@ -36724,7 +36856,7 @@ function renderTerminalSessionBand({ aggregateStatus }) {
|
|
|
36724
36856
|
}
|
|
36725
36857
|
function renderActionBar({ state, working, stopState, completeState }) {
|
|
36726
36858
|
if (completeState.kind === "completing")
|
|
36727
|
-
return new StyledText5([dim4(fg6(PALETTE.dimText)("completing the session\u2026
|
|
36859
|
+
return new StyledText5([dim4(fg6(PALETTE.dimText)("completing the session\u2026"))]);
|
|
36728
36860
|
if (state.aggregateStatus !== "open")
|
|
36729
36861
|
return new StyledText5([]);
|
|
36730
36862
|
const stopToken = working && stopState.kind === "failed" ? ["esc retry stop"] : [];
|
|
@@ -37042,7 +37174,7 @@ var compactMarkRows = 9;
|
|
|
37042
37174
|
var compactMinWidth = 48;
|
|
37043
37175
|
var compactMinHeight = 20;
|
|
37044
37176
|
var markBrightnessGain = 4.2;
|
|
37045
|
-
var remyCliVersion = "1.
|
|
37177
|
+
var remyCliVersion = "1.2.0";
|
|
37046
37178
|
async function showRemySplash({
|
|
37047
37179
|
createRenderer = createRemyRenderer,
|
|
37048
37180
|
durationMs = splashDurationMs,
|
|
@@ -37224,7 +37356,7 @@ Verification URL: `), bold5(fg7(PALETTE.humanAccent)(state.verificationUrl))] :
|
|
|
37224
37356
|
fg7(PALETTE.bodyText)("Continue authentication in your browser."),
|
|
37225
37357
|
...url3,
|
|
37226
37358
|
fg7(PALETTE.dimText)(`
|
|
37227
|
-
${spinner} Waiting for approval \xB7 q / esc
|
|
37359
|
+
${spinner} Waiting for approval \xB7 q / esc cancel`)
|
|
37228
37360
|
]);
|
|
37229
37361
|
}
|
|
37230
37362
|
async function waitForSplashAbort(signal) {
|
|
@@ -37517,7 +37649,7 @@ ${approvalUrl}
|
|
|
37517
37649
|
`);
|
|
37518
37650
|
});
|
|
37519
37651
|
if (!splashPresented)
|
|
37520
|
-
dependencies.output.writeStderr(`Waiting for approval\u2026
|
|
37652
|
+
dependencies.output.writeStderr(`Waiting for approval\u2026
|
|
37521
37653
|
`);
|
|
37522
37654
|
});
|
|
37523
37655
|
let journalPrepared = false;
|
|
@@ -37681,7 +37813,6 @@ function parseOptionalStringFlag(value, name) {
|
|
|
37681
37813
|
}
|
|
37682
37814
|
async function createAuthenticatedClient(dependencies) {
|
|
37683
37815
|
const paths = resolveCliPaths(dependencies.environment);
|
|
37684
|
-
const candidateTokenPath = await createCandidateTokenPath(paths.tokenPath);
|
|
37685
37816
|
const snapshot = await withCredentialState({
|
|
37686
37817
|
stateDirectory: dirname6(paths.tokenPath),
|
|
37687
37818
|
action: async () => {
|
|
@@ -37693,43 +37824,21 @@ async function createAuthenticatedClient(dependencies) {
|
|
|
37693
37824
|
});
|
|
37694
37825
|
const configuration = await readCliConfiguration(paths.configPath);
|
|
37695
37826
|
const tokenGeneration = await readOptionalTokenFile(paths.tokenPath);
|
|
37696
|
-
if (tokenGeneration !== undefined)
|
|
37697
|
-
await writeFile4(candidateTokenPath, tokenGeneration, { mode: 384 });
|
|
37698
37827
|
return { configuration, tokenGeneration };
|
|
37699
37828
|
}
|
|
37700
37829
|
});
|
|
37701
|
-
const
|
|
37702
|
-
const identity = await loadStoredIdentity(
|
|
37830
|
+
const authConfiguration = toAuthConfiguration({ configuration: snapshot.configuration, tokenPath: paths.tokenPath });
|
|
37831
|
+
const identity = await loadStoredIdentity(authConfiguration);
|
|
37832
|
+
const getAccessToken = createRefreshingAccessTokenProvider({ dependencies, paths, snapshot });
|
|
37703
37833
|
try {
|
|
37704
|
-
|
|
37705
|
-
await withCredentialState({
|
|
37706
|
-
stateDirectory: dirname6(paths.tokenPath),
|
|
37707
|
-
action: async () => {
|
|
37708
|
-
await recoverCredentialPromotionWhileLocked({
|
|
37709
|
-
stateDirectory: dirname6(paths.tokenPath),
|
|
37710
|
-
configPath: paths.configPath,
|
|
37711
|
-
tokenPath: paths.tokenPath,
|
|
37712
|
-
...dependencies.credentialStateFilesystem
|
|
37713
|
-
});
|
|
37714
|
-
await assertCredentialSnapshotCurrent({ paths, snapshot });
|
|
37715
|
-
const candidateGeneration = await readFile5(candidateTokenPath, "utf8");
|
|
37716
|
-
if (candidateGeneration !== snapshot.tokenGeneration) {
|
|
37717
|
-
await replaceCredentialTokenWhileLocked({
|
|
37718
|
-
sourcePath: candidateTokenPath,
|
|
37719
|
-
tokenPath: paths.tokenPath,
|
|
37720
|
-
renameFile: dependencies.credentialStateFilesystem?.renameFile,
|
|
37721
|
-
syncDirectory: dependencies.credentialStateFilesystem?.syncDirectory
|
|
37722
|
-
});
|
|
37723
|
-
}
|
|
37724
|
-
}
|
|
37725
|
-
});
|
|
37834
|
+
await getAccessToken(dependencies.abortSignal);
|
|
37726
37835
|
const client = createCodingAgentClient({
|
|
37727
37836
|
apiUrl: snapshot.configuration.apiUrl,
|
|
37728
|
-
getAccessToken
|
|
37837
|
+
getAccessToken
|
|
37729
37838
|
});
|
|
37730
37839
|
const remoteIdentity = await materializeRemoteCurrentUser({ client, signal: dependencies.abortSignal });
|
|
37731
37840
|
return {
|
|
37732
|
-
authConfiguration
|
|
37841
|
+
authConfiguration,
|
|
37733
37842
|
client,
|
|
37734
37843
|
identity,
|
|
37735
37844
|
authenticatedEmail: remoteIdentity.email
|
|
@@ -37755,6 +37864,85 @@ async function createAuthenticatedClient(dependencies) {
|
|
|
37755
37864
|
});
|
|
37756
37865
|
}
|
|
37757
37866
|
throw error93;
|
|
37867
|
+
}
|
|
37868
|
+
}
|
|
37869
|
+
function createRefreshingAccessTokenProvider({
|
|
37870
|
+
dependencies,
|
|
37871
|
+
paths,
|
|
37872
|
+
snapshot
|
|
37873
|
+
}) {
|
|
37874
|
+
let tokenGeneration = snapshot.tokenGeneration;
|
|
37875
|
+
let pendingAccessToken;
|
|
37876
|
+
return async (signal) => {
|
|
37877
|
+
if (!pendingAccessToken) {
|
|
37878
|
+
const expectedTokenGeneration = tokenGeneration;
|
|
37879
|
+
pendingAccessToken = refreshAccessToken({ dependencies, paths, configuration: snapshot.configuration, expectedTokenGeneration, signal }).then(({ accessToken: accessToken2, tokenGeneration: refreshedTokenGeneration }) => {
|
|
37880
|
+
tokenGeneration = refreshedTokenGeneration;
|
|
37881
|
+
return accessToken2;
|
|
37882
|
+
});
|
|
37883
|
+
}
|
|
37884
|
+
const accessToken = pendingAccessToken;
|
|
37885
|
+
try {
|
|
37886
|
+
return await accessToken;
|
|
37887
|
+
} finally {
|
|
37888
|
+
if (pendingAccessToken === accessToken)
|
|
37889
|
+
pendingAccessToken = undefined;
|
|
37890
|
+
}
|
|
37891
|
+
};
|
|
37892
|
+
}
|
|
37893
|
+
async function refreshAccessToken({
|
|
37894
|
+
dependencies,
|
|
37895
|
+
paths,
|
|
37896
|
+
configuration,
|
|
37897
|
+
expectedTokenGeneration,
|
|
37898
|
+
signal
|
|
37899
|
+
}) {
|
|
37900
|
+
const candidateTokenPath = await createCandidateTokenPath(paths.tokenPath);
|
|
37901
|
+
try {
|
|
37902
|
+
await withCredentialState({
|
|
37903
|
+
stateDirectory: dirname6(paths.tokenPath),
|
|
37904
|
+
action: async () => {
|
|
37905
|
+
await recoverCredentialPromotionWhileLocked({
|
|
37906
|
+
stateDirectory: dirname6(paths.tokenPath),
|
|
37907
|
+
configPath: paths.configPath,
|
|
37908
|
+
tokenPath: paths.tokenPath,
|
|
37909
|
+
...dependencies.credentialStateFilesystem
|
|
37910
|
+
});
|
|
37911
|
+
await assertCredentialSnapshotCurrent({
|
|
37912
|
+
paths,
|
|
37913
|
+
snapshot: { configuration, tokenGeneration: expectedTokenGeneration }
|
|
37914
|
+
});
|
|
37915
|
+
if (expectedTokenGeneration !== undefined)
|
|
37916
|
+
await writeFile4(candidateTokenPath, expectedTokenGeneration, { mode: 384 });
|
|
37917
|
+
}
|
|
37918
|
+
});
|
|
37919
|
+
const candidateAuthConfiguration = toAuthConfiguration({ configuration, tokenPath: candidateTokenPath });
|
|
37920
|
+
const accessToken = await requireAccessToken(candidateAuthConfiguration, signal);
|
|
37921
|
+
const candidateTokenGeneration = await readFile5(candidateTokenPath, "utf8");
|
|
37922
|
+
await withCredentialState({
|
|
37923
|
+
stateDirectory: dirname6(paths.tokenPath),
|
|
37924
|
+
action: async () => {
|
|
37925
|
+
await recoverCredentialPromotionWhileLocked({
|
|
37926
|
+
stateDirectory: dirname6(paths.tokenPath),
|
|
37927
|
+
configPath: paths.configPath,
|
|
37928
|
+
tokenPath: paths.tokenPath,
|
|
37929
|
+
...dependencies.credentialStateFilesystem
|
|
37930
|
+
});
|
|
37931
|
+
await assertCredentialSnapshotCurrent({
|
|
37932
|
+
paths,
|
|
37933
|
+
snapshot: { configuration, tokenGeneration: expectedTokenGeneration }
|
|
37934
|
+
});
|
|
37935
|
+
if (candidateTokenGeneration !== expectedTokenGeneration) {
|
|
37936
|
+
await replaceCredentialTokenWhileLocked({
|
|
37937
|
+
sourcePath: candidateTokenPath,
|
|
37938
|
+
tokenPath: paths.tokenPath,
|
|
37939
|
+
renameFile: dependencies.credentialStateFilesystem?.renameFile,
|
|
37940
|
+
syncDirectory: dependencies.credentialStateFilesystem?.syncDirectory
|
|
37941
|
+
});
|
|
37942
|
+
}
|
|
37943
|
+
}
|
|
37944
|
+
});
|
|
37945
|
+
return { accessToken, tokenGeneration: candidateTokenGeneration };
|
|
37758
37946
|
} finally {
|
|
37759
37947
|
await removeCandidateToken({ path: candidateTokenPath, removeTokenFile: unlink3 });
|
|
37760
37948
|
}
|
|
@@ -37847,6 +38035,8 @@ async function dashboard({
|
|
|
37847
38035
|
return 0;
|
|
37848
38036
|
if (action.kind === "interrupt")
|
|
37849
38037
|
throw new CliInterruptedError("SIGINT");
|
|
38038
|
+
if (action.kind === "logout")
|
|
38039
|
+
return await logout({ dependencies, flags: {} });
|
|
37850
38040
|
holdTransitionScreen();
|
|
37851
38041
|
if (action.kind === "session")
|
|
37852
38042
|
return await attachSession({ dependencies, sessionId: action.sessionId, noTui: false, json: false });
|
|
@@ -38354,6 +38544,13 @@ async function runAttachedSession({
|
|
|
38354
38544
|
bootstrap: await prepareDashboardBootstrap({ dependencies, operations })
|
|
38355
38545
|
});
|
|
38356
38546
|
}
|
|
38547
|
+
if (action.kind === "logout") {
|
|
38548
|
+
stop();
|
|
38549
|
+
await startPromise;
|
|
38550
|
+
if (dependencies.abortSignal?.aborted)
|
|
38551
|
+
throw dependencies.abortSignal.reason ?? new Error("interrupted");
|
|
38552
|
+
return await logout({ dependencies, flags: {} });
|
|
38553
|
+
}
|
|
38357
38554
|
holdTransitionScreen();
|
|
38358
38555
|
const availableRepositories = await collectAllRepositories({ listRepositories: operations.listRepositories });
|
|
38359
38556
|
let draft = await openTuiNewSessionWizard({ dependencies, operations, repositories: availableRepositories });
|