@meistrari/remy-cli 1.1.0 → 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 +315 -77
- 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";
|
|
@@ -35110,6 +35198,8 @@ var defaultNewSessionAgent = {
|
|
|
35110
35198
|
};
|
|
35111
35199
|
|
|
35112
35200
|
// src/tui/new-session-wizard.ts
|
|
35201
|
+
var suggestionSpinnerFrames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
35202
|
+
var suggestionSpinnerIntervalMs = 80;
|
|
35113
35203
|
async function createNewSessionWizard({
|
|
35114
35204
|
repositories,
|
|
35115
35205
|
reloadRepositories,
|
|
@@ -35126,6 +35216,7 @@ async function createNewSessionWizard({
|
|
|
35126
35216
|
let inputHandler;
|
|
35127
35217
|
let keyHandler;
|
|
35128
35218
|
let resizeHandler;
|
|
35219
|
+
let suggestionSpinnerTimer;
|
|
35129
35220
|
try {
|
|
35130
35221
|
let selectedInstallationId = function() {
|
|
35131
35222
|
return installationIds[installationIndex];
|
|
@@ -35169,6 +35260,7 @@ async function createNewSessionWizard({
|
|
|
35169
35260
|
}
|
|
35170
35261
|
contentScrollBox.flexGrow = repositoryListVisible ? 0 : 1;
|
|
35171
35262
|
contentScrollBox.maxHeight = repositoryListVisible ? "45%" : undefined;
|
|
35263
|
+
contentScrollBox.height = repositoryListVisible ? repositoryHeaderHeight ?? "auto" : 0;
|
|
35172
35264
|
repositoryScrollBox.flexGrow = repositoryListVisible ? 1 : 0;
|
|
35173
35265
|
repositoryScrollBox.visible = repositoryListVisible;
|
|
35174
35266
|
repositoryRows.content = "";
|
|
@@ -35219,8 +35311,8 @@ async function createNewSessionWizard({
|
|
|
35219
35311
|
} else if (step === "loadingSuggestions") {
|
|
35220
35312
|
content.content = orient(joinStyled([
|
|
35221
35313
|
new StyledText4([stepHeader("Select repositories")]),
|
|
35222
|
-
|
|
35223
|
-
|
|
35314
|
+
new StyledText4([fg5(PALETTE.progress)(`${suggestionSpinnerFrames[suggestionSpinnerFrame]} Remy is matching your prompt to repositories\u2026`)]),
|
|
35315
|
+
new StyledText4([dim3(fg5(PALETTE.dimText)("This can take a few seconds."))]),
|
|
35224
35316
|
stringToStyledText4("s select repositories yourself \xB7 esc back")
|
|
35225
35317
|
], `
|
|
35226
35318
|
|
|
@@ -35293,23 +35385,52 @@ async function createNewSessionWizard({
|
|
|
35293
35385
|
if (composerMounted)
|
|
35294
35386
|
composer.render();
|
|
35295
35387
|
if (repositoryListVisible) {
|
|
35296
|
-
renderer.once(
|
|
35388
|
+
renderer.once(CliRenderEvents3.FRAME, () => {
|
|
35297
35389
|
if (destroyed || step !== "repositories" && step !== "repositorySearch")
|
|
35298
35390
|
return;
|
|
35391
|
+
const nextRepositoryHeaderHeight = Math.min(content.virtualLineCount, Math.floor(renderer.height * 0.45));
|
|
35392
|
+
if (nextRepositoryHeaderHeight !== repositoryHeaderHeight) {
|
|
35393
|
+
repositoryHeaderHeight = nextRepositoryHeaderHeight;
|
|
35394
|
+
contentScrollBox.height = nextRepositoryHeaderHeight;
|
|
35395
|
+
}
|
|
35299
35396
|
repositoryScrollBox.scrollTo({ x: 0, y: repositoryIndex });
|
|
35300
35397
|
renderer.requestRender();
|
|
35301
35398
|
});
|
|
35399
|
+
} else {
|
|
35400
|
+
repositoryHeaderHeight = undefined;
|
|
35302
35401
|
}
|
|
35402
|
+
syncSuggestionLoadingIndicator();
|
|
35303
35403
|
renderer.requestRender();
|
|
35404
|
+
}, syncSuggestionLoadingIndicator = function() {
|
|
35405
|
+
if (destroyed || settled || step !== "loadingSuggestions") {
|
|
35406
|
+
if (suggestionSpinnerTimer) {
|
|
35407
|
+
clearInterval(suggestionSpinnerTimer);
|
|
35408
|
+
suggestionSpinnerTimer = undefined;
|
|
35409
|
+
}
|
|
35410
|
+
suggestionSpinnerFrame = 0;
|
|
35411
|
+
return;
|
|
35412
|
+
}
|
|
35413
|
+
if (suggestionSpinnerTimer)
|
|
35414
|
+
return;
|
|
35415
|
+
suggestionSpinnerTimer = setInterval(() => {
|
|
35416
|
+
if (destroyed || settled || step !== "loadingSuggestions") {
|
|
35417
|
+
syncSuggestionLoadingIndicator();
|
|
35418
|
+
return;
|
|
35419
|
+
}
|
|
35420
|
+
suggestionSpinnerFrame = (suggestionSpinnerFrame + 1) % suggestionSpinnerFrames.length;
|
|
35421
|
+
render();
|
|
35422
|
+
}, suggestionSpinnerIntervalMs);
|
|
35304
35423
|
}, finish = function(value) {
|
|
35305
35424
|
if (settled)
|
|
35306
35425
|
return;
|
|
35307
35426
|
settled = true;
|
|
35427
|
+
syncSuggestionLoadingIndicator();
|
|
35308
35428
|
resolveDraft(value);
|
|
35309
35429
|
}, fail = function(error93) {
|
|
35310
35430
|
if (settled)
|
|
35311
35431
|
return;
|
|
35312
35432
|
settled = true;
|
|
35433
|
+
syncSuggestionLoadingIndicator();
|
|
35313
35434
|
rejectDraft(error93);
|
|
35314
35435
|
}, returnToPromptFromRepositoryLoading = function() {
|
|
35315
35436
|
step = "prompt";
|
|
@@ -35493,6 +35614,24 @@ async function createNewSessionWizard({
|
|
|
35493
35614
|
if (step !== "repositories")
|
|
35494
35615
|
return;
|
|
35495
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;
|
|
35496
35635
|
if (key.name === "up" || key.name === "down" || key.name === "j" || key.name === "k") {
|
|
35497
35636
|
key.preventDefault();
|
|
35498
35637
|
if (choices.length > 0)
|
|
@@ -35560,10 +35699,13 @@ async function createNewSessionWizard({
|
|
|
35560
35699
|
let repositoryIndex = 0;
|
|
35561
35700
|
let repositoryQuery = "";
|
|
35562
35701
|
let recomputeInstruction = "";
|
|
35702
|
+
let suggestionSpinnerFrame = 0;
|
|
35563
35703
|
let attachments = [...initialDraft?.attachments ?? []];
|
|
35564
35704
|
let pathCompletions = [];
|
|
35565
35705
|
let completionIndex = 0;
|
|
35566
35706
|
let pathCompletionMenuOpen = false;
|
|
35707
|
+
let repositoryHeaderHeight;
|
|
35708
|
+
let awaitingVimGoToTop = false;
|
|
35567
35709
|
const manualSelections = new Map;
|
|
35568
35710
|
let searchSelections;
|
|
35569
35711
|
let suggestedIds = new Set;
|
|
@@ -35787,7 +35929,7 @@ async function createNewSessionWizard({
|
|
|
35787
35929
|
};
|
|
35788
35930
|
renderer.addInputHandler(inputHandler);
|
|
35789
35931
|
renderer.keyInput.on("keypress", keyHandler);
|
|
35790
|
-
renderer.on(
|
|
35932
|
+
renderer.on(CliRenderEvents3.RENDER_ERROR, (event) => fail(event.error));
|
|
35791
35933
|
resizeHandler = () => {
|
|
35792
35934
|
if (destroyed)
|
|
35793
35935
|
return;
|
|
@@ -35796,7 +35938,7 @@ async function createNewSessionWizard({
|
|
|
35796
35938
|
repositoryIndex = Math.min(repositoryIndex, choices.length - 1);
|
|
35797
35939
|
render();
|
|
35798
35940
|
};
|
|
35799
|
-
renderer.on(
|
|
35941
|
+
renderer.on(CliRenderEvents3.RESIZE, resizeHandler);
|
|
35800
35942
|
render();
|
|
35801
35943
|
if (!Array.isArray(repositories))
|
|
35802
35944
|
requestRepositoryLoad({ retry: false });
|
|
@@ -35811,7 +35953,11 @@ async function createNewSessionWizard({
|
|
|
35811
35953
|
if (keyHandler)
|
|
35812
35954
|
renderer.keyInput.off("keypress", keyHandler);
|
|
35813
35955
|
if (resizeHandler)
|
|
35814
|
-
renderer.off(
|
|
35956
|
+
renderer.off(CliRenderEvents3.RESIZE, resizeHandler);
|
|
35957
|
+
if (suggestionSpinnerTimer) {
|
|
35958
|
+
clearInterval(suggestionSpinnerTimer);
|
|
35959
|
+
suggestionSpinnerTimer = undefined;
|
|
35960
|
+
}
|
|
35815
35961
|
composer.destroy();
|
|
35816
35962
|
rendererDestroyed2 = true;
|
|
35817
35963
|
renderer.destroy();
|
|
@@ -35821,12 +35967,14 @@ async function createNewSessionWizard({
|
|
|
35821
35967
|
}
|
|
35822
35968
|
};
|
|
35823
35969
|
} catch (error93) {
|
|
35970
|
+
if (suggestionSpinnerTimer)
|
|
35971
|
+
clearInterval(suggestionSpinnerTimer);
|
|
35824
35972
|
if (inputHandler)
|
|
35825
35973
|
renderer.removeInputHandler(inputHandler);
|
|
35826
35974
|
if (keyHandler)
|
|
35827
35975
|
renderer.keyInput.off("keypress", keyHandler);
|
|
35828
35976
|
if (resizeHandler)
|
|
35829
|
-
renderer.off(
|
|
35977
|
+
renderer.off(CliRenderEvents3.RESIZE, resizeHandler);
|
|
35830
35978
|
if (!rendererDestroyed2) {
|
|
35831
35979
|
rendererDestroyed2 = true;
|
|
35832
35980
|
renderer.destroy();
|
|
@@ -35909,11 +36057,12 @@ async function createDefaultRenderer2() {
|
|
|
35909
36057
|
}
|
|
35910
36058
|
|
|
35911
36059
|
// src/tui/session-view.ts
|
|
35912
|
-
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";
|
|
35913
36061
|
var composerSlashCommands = [
|
|
35914
36062
|
{ value: "/sessions", description: "Back to the session list" },
|
|
35915
36063
|
{ value: "/complete", description: "Finish this session" },
|
|
35916
36064
|
{ value: "/new", description: "Start a new session" },
|
|
36065
|
+
{ value: "/logout", description: "Sign out of Remy" },
|
|
35917
36066
|
{ value: "/help", description: "Show what you can do here" },
|
|
35918
36067
|
{ value: "/exit", description: "Leave Remy" }
|
|
35919
36068
|
];
|
|
@@ -36139,6 +36288,7 @@ async function createSessionTui({
|
|
|
36139
36288
|
let stopState = { kind: "idle" };
|
|
36140
36289
|
let completeState = { kind: "idle" };
|
|
36141
36290
|
let helpVisible = false;
|
|
36291
|
+
let logoutConfirmationOpen = false;
|
|
36142
36292
|
let latestState = controller.getState();
|
|
36143
36293
|
let spinnerFrameIndex = 0;
|
|
36144
36294
|
let renderedTranscript;
|
|
@@ -36195,6 +36345,17 @@ async function createSessionTui({
|
|
|
36195
36345
|
return true;
|
|
36196
36346
|
};
|
|
36197
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
|
+
}
|
|
36198
36359
|
if (key.ctrl && key.name === "c") {
|
|
36199
36360
|
key.preventDefault();
|
|
36200
36361
|
finish({ kind: "exit", signal: "SIGINT" });
|
|
@@ -36270,6 +36431,10 @@ async function createSessionTui({
|
|
|
36270
36431
|
}
|
|
36271
36432
|
}
|
|
36272
36433
|
composer.onKeyDown = (key) => {
|
|
36434
|
+
if (logoutConfirmationOpen) {
|
|
36435
|
+
key.preventDefault();
|
|
36436
|
+
return;
|
|
36437
|
+
}
|
|
36273
36438
|
if (slashCompletions.length > 0 && (key.name === "up" || key.name === "down")) {
|
|
36274
36439
|
key.preventDefault();
|
|
36275
36440
|
slashCompletionIndex = (slashCompletionIndex + (key.name === "up" ? -1 : 1) + slashCompletions.length) % slashCompletions.length;
|
|
@@ -36423,6 +36588,14 @@ async function createSessionTui({
|
|
|
36423
36588
|
await handleCompleteRequest();
|
|
36424
36589
|
return true;
|
|
36425
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
|
+
}
|
|
36426
36599
|
if (command === "/help") {
|
|
36427
36600
|
composer.setText("");
|
|
36428
36601
|
composerDraft = { ...composerDraft, text: "" };
|
|
@@ -36549,12 +36722,12 @@ async function createSessionTui({
|
|
|
36549
36722
|
renderer.addInputHandler(inputHandler);
|
|
36550
36723
|
renderer.keyInput.on("keypress", keyHandler);
|
|
36551
36724
|
composer.setText(composerDraft.text);
|
|
36552
|
-
renderer.on(
|
|
36725
|
+
renderer.on(CliRenderEvents4.RENDER_ERROR, (event) => finish(undefined, event.error));
|
|
36553
36726
|
resizeHandler = () => {
|
|
36554
36727
|
if (!destroyed)
|
|
36555
36728
|
render();
|
|
36556
36729
|
};
|
|
36557
|
-
renderer.on(
|
|
36730
|
+
renderer.on(CliRenderEvents4.RESIZE, resizeHandler);
|
|
36558
36731
|
render();
|
|
36559
36732
|
if (latestState.aggregateStatus === "open")
|
|
36560
36733
|
composer.focus();
|
|
@@ -36574,7 +36747,7 @@ async function createSessionTui({
|
|
|
36574
36747
|
if (keyHandler)
|
|
36575
36748
|
renderer.keyInput.off("keypress", keyHandler);
|
|
36576
36749
|
if (resizeHandler)
|
|
36577
|
-
renderer.off(
|
|
36750
|
+
renderer.off(CliRenderEvents4.RESIZE, resizeHandler);
|
|
36578
36751
|
composerLayout.destroy();
|
|
36579
36752
|
rendererDestroyed2 = true;
|
|
36580
36753
|
renderer.destroy();
|
|
@@ -36591,7 +36764,7 @@ async function createSessionTui({
|
|
|
36591
36764
|
if (keyHandler)
|
|
36592
36765
|
renderer.keyInput.off("keypress", keyHandler);
|
|
36593
36766
|
if (resizeHandler)
|
|
36594
|
-
renderer.off(
|
|
36767
|
+
renderer.off(CliRenderEvents4.RESIZE, resizeHandler);
|
|
36595
36768
|
if (!rendererDestroyed2) {
|
|
36596
36769
|
rendererDestroyed2 = true;
|
|
36597
36770
|
renderer.destroy();
|
|
@@ -36619,11 +36792,11 @@ var helpEntries = [
|
|
|
36619
36792
|
{ group: "command", token: "/new", description: "start a new session" },
|
|
36620
36793
|
{ group: "command", token: "/sessions", description: "back to the session list" },
|
|
36621
36794
|
{ group: "command", token: "/complete", description: "finish this session" },
|
|
36795
|
+
{ group: "command", token: "/logout", description: "sign out of Remy" },
|
|
36622
36796
|
{ group: "command", token: "/exit", description: "leave Remy" },
|
|
36623
36797
|
{ group: "key", token: "esc", description: "stop Remy\u2019s current turn" },
|
|
36624
36798
|
{ group: "key", token: "ctrl+o", description: "expand the activity trail" },
|
|
36625
|
-
{ group: "key", token: "ctrl+r", description: "retry the oldest failed message" }
|
|
36626
|
-
{ group: "key", token: "ctrl+c", description: "quit" }
|
|
36799
|
+
{ group: "key", token: "ctrl+r", description: "retry the oldest failed message" }
|
|
36627
36800
|
];
|
|
36628
36801
|
function renderHelpPanel() {
|
|
36629
36802
|
const width = helpEntries.reduce((max, entry) => Math.max(max, entry.token.length), 0) + 2;
|
|
@@ -36683,7 +36856,7 @@ function renderTerminalSessionBand({ aggregateStatus }) {
|
|
|
36683
36856
|
}
|
|
36684
36857
|
function renderActionBar({ state, working, stopState, completeState }) {
|
|
36685
36858
|
if (completeState.kind === "completing")
|
|
36686
|
-
return new StyledText5([dim4(fg6(PALETTE.dimText)("completing the session\u2026
|
|
36859
|
+
return new StyledText5([dim4(fg6(PALETTE.dimText)("completing the session\u2026"))]);
|
|
36687
36860
|
if (state.aggregateStatus !== "open")
|
|
36688
36861
|
return new StyledText5([]);
|
|
36689
36862
|
const stopToken = working && stopState.kind === "failed" ? ["esc retry stop"] : [];
|
|
@@ -37001,7 +37174,7 @@ var compactMarkRows = 9;
|
|
|
37001
37174
|
var compactMinWidth = 48;
|
|
37002
37175
|
var compactMinHeight = 20;
|
|
37003
37176
|
var markBrightnessGain = 4.2;
|
|
37004
|
-
var remyCliVersion = "1.
|
|
37177
|
+
var remyCliVersion = "1.2.0";
|
|
37005
37178
|
async function showRemySplash({
|
|
37006
37179
|
createRenderer = createRemyRenderer,
|
|
37007
37180
|
durationMs = splashDurationMs,
|
|
@@ -37183,7 +37356,7 @@ Verification URL: `), bold5(fg7(PALETTE.humanAccent)(state.verificationUrl))] :
|
|
|
37183
37356
|
fg7(PALETTE.bodyText)("Continue authentication in your browser."),
|
|
37184
37357
|
...url3,
|
|
37185
37358
|
fg7(PALETTE.dimText)(`
|
|
37186
|
-
${spinner} Waiting for approval \xB7 q / esc
|
|
37359
|
+
${spinner} Waiting for approval \xB7 q / esc cancel`)
|
|
37187
37360
|
]);
|
|
37188
37361
|
}
|
|
37189
37362
|
async function waitForSplashAbort(signal) {
|
|
@@ -37476,7 +37649,7 @@ ${approvalUrl}
|
|
|
37476
37649
|
`);
|
|
37477
37650
|
});
|
|
37478
37651
|
if (!splashPresented)
|
|
37479
|
-
dependencies.output.writeStderr(`Waiting for approval\u2026
|
|
37652
|
+
dependencies.output.writeStderr(`Waiting for approval\u2026
|
|
37480
37653
|
`);
|
|
37481
37654
|
});
|
|
37482
37655
|
let journalPrepared = false;
|
|
@@ -37640,7 +37813,6 @@ function parseOptionalStringFlag(value, name) {
|
|
|
37640
37813
|
}
|
|
37641
37814
|
async function createAuthenticatedClient(dependencies) {
|
|
37642
37815
|
const paths = resolveCliPaths(dependencies.environment);
|
|
37643
|
-
const candidateTokenPath = await createCandidateTokenPath(paths.tokenPath);
|
|
37644
37816
|
const snapshot = await withCredentialState({
|
|
37645
37817
|
stateDirectory: dirname6(paths.tokenPath),
|
|
37646
37818
|
action: async () => {
|
|
@@ -37652,43 +37824,21 @@ async function createAuthenticatedClient(dependencies) {
|
|
|
37652
37824
|
});
|
|
37653
37825
|
const configuration = await readCliConfiguration(paths.configPath);
|
|
37654
37826
|
const tokenGeneration = await readOptionalTokenFile(paths.tokenPath);
|
|
37655
|
-
if (tokenGeneration !== undefined)
|
|
37656
|
-
await writeFile4(candidateTokenPath, tokenGeneration, { mode: 384 });
|
|
37657
37827
|
return { configuration, tokenGeneration };
|
|
37658
37828
|
}
|
|
37659
37829
|
});
|
|
37660
|
-
const
|
|
37661
|
-
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 });
|
|
37662
37833
|
try {
|
|
37663
|
-
|
|
37664
|
-
await withCredentialState({
|
|
37665
|
-
stateDirectory: dirname6(paths.tokenPath),
|
|
37666
|
-
action: async () => {
|
|
37667
|
-
await recoverCredentialPromotionWhileLocked({
|
|
37668
|
-
stateDirectory: dirname6(paths.tokenPath),
|
|
37669
|
-
configPath: paths.configPath,
|
|
37670
|
-
tokenPath: paths.tokenPath,
|
|
37671
|
-
...dependencies.credentialStateFilesystem
|
|
37672
|
-
});
|
|
37673
|
-
await assertCredentialSnapshotCurrent({ paths, snapshot });
|
|
37674
|
-
const candidateGeneration = await readFile5(candidateTokenPath, "utf8");
|
|
37675
|
-
if (candidateGeneration !== snapshot.tokenGeneration) {
|
|
37676
|
-
await replaceCredentialTokenWhileLocked({
|
|
37677
|
-
sourcePath: candidateTokenPath,
|
|
37678
|
-
tokenPath: paths.tokenPath,
|
|
37679
|
-
renameFile: dependencies.credentialStateFilesystem?.renameFile,
|
|
37680
|
-
syncDirectory: dependencies.credentialStateFilesystem?.syncDirectory
|
|
37681
|
-
});
|
|
37682
|
-
}
|
|
37683
|
-
}
|
|
37684
|
-
});
|
|
37834
|
+
await getAccessToken(dependencies.abortSignal);
|
|
37685
37835
|
const client = createCodingAgentClient({
|
|
37686
37836
|
apiUrl: snapshot.configuration.apiUrl,
|
|
37687
|
-
getAccessToken
|
|
37837
|
+
getAccessToken
|
|
37688
37838
|
});
|
|
37689
37839
|
const remoteIdentity = await materializeRemoteCurrentUser({ client, signal: dependencies.abortSignal });
|
|
37690
37840
|
return {
|
|
37691
|
-
authConfiguration
|
|
37841
|
+
authConfiguration,
|
|
37692
37842
|
client,
|
|
37693
37843
|
identity,
|
|
37694
37844
|
authenticatedEmail: remoteIdentity.email
|
|
@@ -37714,6 +37864,85 @@ async function createAuthenticatedClient(dependencies) {
|
|
|
37714
37864
|
});
|
|
37715
37865
|
}
|
|
37716
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 };
|
|
37717
37946
|
} finally {
|
|
37718
37947
|
await removeCandidateToken({ path: candidateTokenPath, removeTokenFile: unlink3 });
|
|
37719
37948
|
}
|
|
@@ -37806,6 +38035,8 @@ async function dashboard({
|
|
|
37806
38035
|
return 0;
|
|
37807
38036
|
if (action.kind === "interrupt")
|
|
37808
38037
|
throw new CliInterruptedError("SIGINT");
|
|
38038
|
+
if (action.kind === "logout")
|
|
38039
|
+
return await logout({ dependencies, flags: {} });
|
|
37809
38040
|
holdTransitionScreen();
|
|
37810
38041
|
if (action.kind === "session")
|
|
37811
38042
|
return await attachSession({ dependencies, sessionId: action.sessionId, noTui: false, json: false });
|
|
@@ -38313,6 +38544,13 @@ async function runAttachedSession({
|
|
|
38313
38544
|
bootstrap: await prepareDashboardBootstrap({ dependencies, operations })
|
|
38314
38545
|
});
|
|
38315
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
|
+
}
|
|
38316
38554
|
holdTransitionScreen();
|
|
38317
38555
|
const availableRepositories = await collectAllRepositories({ listRepositories: operations.listRepositories });
|
|
38318
38556
|
let draft = await openTuiNewSessionWizard({ dependencies, operations, repositories: availableRepositories });
|