@workflow-code/cli 0.2.4-20261010001.codex-cli-web → 0.2.4-20261010002.codex-cli-web
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/dist/index.js +1 -1
- package/dist/web/theme.css +8 -6
- package/dist/web/workflow-ui.css +262 -1
- package/dist/web/workflow-ui.js +372 -71
- package/dist/{web-U23BFLG4.js → web-KTQSEVQT.js} +95 -10
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -24,7 +24,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
24
24
|
const prepared = prepareCliInvocation(argv);
|
|
25
25
|
argv = prepared.argv;
|
|
26
26
|
if (argv[0] === "web") {
|
|
27
|
-
const webCli = await import("./web-
|
|
27
|
+
const webCli = await import("./web-KTQSEVQT.js");
|
|
28
28
|
return webCli.main(argv.slice(1));
|
|
29
29
|
}
|
|
30
30
|
if (argv[0] === "workspace") {
|
package/dist/web/theme.css
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
--vscode-errorForeground: #c72e2e;
|
|
26
26
|
--vscode-button-background: #0078d4;
|
|
27
27
|
--vscode-button-foreground: #ffffff;
|
|
28
|
+
--vscode-button-hoverBackground: #006ab1;
|
|
28
29
|
--vscode-button-secondaryBackground: #eeeeee;
|
|
29
30
|
--vscode-button-secondaryForeground: #333333;
|
|
30
31
|
--vscode-editorWidget-background: #ffffff;
|
|
@@ -33,6 +34,9 @@
|
|
|
33
34
|
--vscode-menu-foreground: #333333;
|
|
34
35
|
--vscode-menu-selectionBackground: #e8e8e8;
|
|
35
36
|
--vscode-menu-selectionForeground: #333333;
|
|
37
|
+
--vscode-testing-iconPassed: #16825d;
|
|
38
|
+
--vscode-scrollbarSlider-background: #79797955;
|
|
39
|
+
--vscode-scrollbarSlider-hoverBackground: #64646488;
|
|
36
40
|
}
|
|
37
41
|
:root[data-theme=dark] {
|
|
38
42
|
color-scheme: dark;
|
|
@@ -61,12 +65,10 @@
|
|
|
61
65
|
--vscode-menu-foreground: #cccccc;
|
|
62
66
|
--vscode-menu-selectionBackground: #3c3c3c;
|
|
63
67
|
--vscode-menu-selectionForeground: #ffffff;
|
|
68
|
+
--vscode-testing-iconPassed: #89d185;
|
|
69
|
+
--vscode-scrollbarSlider-background: #79797966;
|
|
70
|
+
--vscode-scrollbarSlider-hoverBackground: #646464b3;
|
|
64
71
|
}
|
|
72
|
+
* { scrollbar-width: thin; scrollbar-color: var(--vscode-scrollbarSlider-background) transparent; }
|
|
65
73
|
#chats { flex: 1; }
|
|
66
|
-
.web-account { display: flex; align-items: center; gap: 10px; flex-shrink: 0; margin: auto 8px 8px; padding: 10px; min-height: 44px; border: none; border-top: 1px solid var(--vscode-panel-border); background: transparent; text-align: left; }
|
|
67
|
-
.web-account > span:last-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
68
|
-
.web-account:hover { background: var(--vscode-toolbar-hoverBackground); }
|
|
69
|
-
.web-avatar { display: grid; place-items: center; width: 24px; height: 24px; flex-shrink: 0; position: relative; }
|
|
70
|
-
.web-avatar img { position: absolute; width: 24px; height: 24px; object-fit: cover; border-radius: 50%; }
|
|
71
74
|
.web-preview { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; font: 12px/1.6 var(--vscode-editor-font-family); }
|
|
72
|
-
@media (max-width: 680px) { .web-account { margin: 0 8px; min-height: 36px; padding: 6px 10px; } }
|
package/dist/web/workflow-ui.css
CHANGED
|
@@ -1335,7 +1335,13 @@ main {
|
|
|
1335
1335
|
}
|
|
1336
1336
|
.composer-area {
|
|
1337
1337
|
flex-shrink: 0;
|
|
1338
|
-
|
|
1338
|
+
min-width: 0;
|
|
1339
|
+
padding: 8px 28px 14px;
|
|
1340
|
+
}
|
|
1341
|
+
.composer-area > * {
|
|
1342
|
+
width: 100%;
|
|
1343
|
+
max-width: 820px;
|
|
1344
|
+
margin-inline: auto;
|
|
1339
1345
|
}
|
|
1340
1346
|
.parameter-login {
|
|
1341
1347
|
display: flex;
|
|
@@ -1477,9 +1483,19 @@ textarea::placeholder {
|
|
|
1477
1483
|
.parameter-popover-heading strong {
|
|
1478
1484
|
font-weight: 500;
|
|
1479
1485
|
}
|
|
1486
|
+
.parameter-popover[data-kind=auxiliary] {
|
|
1487
|
+
display: flex;
|
|
1488
|
+
flex-direction: column;
|
|
1489
|
+
overflow: hidden;
|
|
1490
|
+
}
|
|
1491
|
+
.parameter-popover-heading {
|
|
1492
|
+
flex-shrink: 0;
|
|
1493
|
+
}
|
|
1480
1494
|
.parameter-popover-fields {
|
|
1481
1495
|
display: grid;
|
|
1482
1496
|
gap: 18px;
|
|
1497
|
+
min-height: 0;
|
|
1498
|
+
overflow: auto;
|
|
1483
1499
|
padding: 16px;
|
|
1484
1500
|
}
|
|
1485
1501
|
.parameter-popover[data-kind=quick] {
|
|
@@ -1708,3 +1724,248 @@ textarea::placeholder {
|
|
|
1708
1724
|
max-width: 190px;
|
|
1709
1725
|
}
|
|
1710
1726
|
}
|
|
1727
|
+
|
|
1728
|
+
/* src/web/ui/account.css */
|
|
1729
|
+
.web-account {
|
|
1730
|
+
display: flex;
|
|
1731
|
+
align-items: center;
|
|
1732
|
+
gap: 10px;
|
|
1733
|
+
width: calc(100% - 16px);
|
|
1734
|
+
min-width: 0;
|
|
1735
|
+
flex-shrink: 0;
|
|
1736
|
+
margin: auto 8px 8px;
|
|
1737
|
+
padding: 10px;
|
|
1738
|
+
min-height: 44px;
|
|
1739
|
+
border: none;
|
|
1740
|
+
border-top: 1px solid var(--border);
|
|
1741
|
+
border-radius: 4px;
|
|
1742
|
+
background: transparent;
|
|
1743
|
+
text-align: left;
|
|
1744
|
+
}
|
|
1745
|
+
.web-account-name {
|
|
1746
|
+
min-width: 0;
|
|
1747
|
+
overflow: hidden;
|
|
1748
|
+
text-overflow: ellipsis;
|
|
1749
|
+
white-space: nowrap;
|
|
1750
|
+
}
|
|
1751
|
+
.web-account:hover {
|
|
1752
|
+
background: var(--vscode-toolbar-hoverBackground);
|
|
1753
|
+
}
|
|
1754
|
+
.web-avatar,
|
|
1755
|
+
.account-avatar {
|
|
1756
|
+
display: grid;
|
|
1757
|
+
place-items: center;
|
|
1758
|
+
width: 24px;
|
|
1759
|
+
height: 24px;
|
|
1760
|
+
flex-shrink: 0;
|
|
1761
|
+
position: relative;
|
|
1762
|
+
overflow: hidden;
|
|
1763
|
+
border-radius: 50%;
|
|
1764
|
+
background: var(--vscode-button-secondaryBackground);
|
|
1765
|
+
color: var(--vscode-button-secondaryForeground);
|
|
1766
|
+
}
|
|
1767
|
+
.web-avatar img,
|
|
1768
|
+
.account-avatar img {
|
|
1769
|
+
position: absolute;
|
|
1770
|
+
inset: 0;
|
|
1771
|
+
width: 100%;
|
|
1772
|
+
height: 100%;
|
|
1773
|
+
object-fit: cover;
|
|
1774
|
+
}
|
|
1775
|
+
.account-dialog {
|
|
1776
|
+
width: min(460px, calc(100vw - 32px));
|
|
1777
|
+
}
|
|
1778
|
+
.account-body {
|
|
1779
|
+
padding: 24px;
|
|
1780
|
+
font-size: 13px;
|
|
1781
|
+
line-height: 1.6;
|
|
1782
|
+
}
|
|
1783
|
+
.account-body #account-status {
|
|
1784
|
+
margin-bottom: 24px;
|
|
1785
|
+
color: var(--muted);
|
|
1786
|
+
}
|
|
1787
|
+
.account-body #account-status.connected {
|
|
1788
|
+
color: var(--vscode-testing-iconPassed, #16825d);
|
|
1789
|
+
}
|
|
1790
|
+
.account-body form {
|
|
1791
|
+
display: grid;
|
|
1792
|
+
gap: 8px;
|
|
1793
|
+
}
|
|
1794
|
+
.account-body form > label:not(:first-child) {
|
|
1795
|
+
margin-top: 12px;
|
|
1796
|
+
}
|
|
1797
|
+
.account-body input {
|
|
1798
|
+
width: 100%;
|
|
1799
|
+
height: 36px;
|
|
1800
|
+
padding: 6px 10px;
|
|
1801
|
+
}
|
|
1802
|
+
.account-password {
|
|
1803
|
+
position: relative;
|
|
1804
|
+
}
|
|
1805
|
+
.account-password input {
|
|
1806
|
+
padding-right: 40px;
|
|
1807
|
+
}
|
|
1808
|
+
.account-password .icon-button {
|
|
1809
|
+
position: absolute;
|
|
1810
|
+
right: 2px;
|
|
1811
|
+
top: 2px;
|
|
1812
|
+
width: 32px;
|
|
1813
|
+
height: 32px;
|
|
1814
|
+
}
|
|
1815
|
+
.account-actions {
|
|
1816
|
+
display: flex;
|
|
1817
|
+
flex-wrap: wrap;
|
|
1818
|
+
align-items: center;
|
|
1819
|
+
gap: 10px;
|
|
1820
|
+
margin-top: 16px;
|
|
1821
|
+
}
|
|
1822
|
+
.account-primary,
|
|
1823
|
+
.account-secondary {
|
|
1824
|
+
display: inline-flex;
|
|
1825
|
+
align-items: center;
|
|
1826
|
+
justify-content: center;
|
|
1827
|
+
gap: 8px;
|
|
1828
|
+
min-height: 34px;
|
|
1829
|
+
padding: 6px 12px;
|
|
1830
|
+
border: none;
|
|
1831
|
+
border-radius: 4px;
|
|
1832
|
+
}
|
|
1833
|
+
.account-primary {
|
|
1834
|
+
background: var(--vscode-button-background);
|
|
1835
|
+
color: var(--vscode-button-foreground);
|
|
1836
|
+
}
|
|
1837
|
+
.account-primary:hover:not(:disabled) {
|
|
1838
|
+
background: var(--vscode-button-hoverBackground, var(--vscode-button-background));
|
|
1839
|
+
}
|
|
1840
|
+
.account-secondary {
|
|
1841
|
+
background: var(--vscode-button-secondaryBackground);
|
|
1842
|
+
color: var(--vscode-button-secondaryForeground);
|
|
1843
|
+
}
|
|
1844
|
+
.account-secondary:hover:not(:disabled) {
|
|
1845
|
+
background: var(--vscode-button-secondaryHoverBackground, var(--vscode-toolbar-hoverBackground));
|
|
1846
|
+
}
|
|
1847
|
+
.account-identity {
|
|
1848
|
+
display: flex;
|
|
1849
|
+
align-items: center;
|
|
1850
|
+
gap: 16px;
|
|
1851
|
+
padding-bottom: 24px;
|
|
1852
|
+
border-bottom: 1px solid var(--border);
|
|
1853
|
+
}
|
|
1854
|
+
.account-identity > div {
|
|
1855
|
+
min-width: 0;
|
|
1856
|
+
}
|
|
1857
|
+
.account-avatar {
|
|
1858
|
+
width: 48px;
|
|
1859
|
+
height: 48px;
|
|
1860
|
+
}
|
|
1861
|
+
.account-avatar > .codicon {
|
|
1862
|
+
font-size: 28px;
|
|
1863
|
+
}
|
|
1864
|
+
.account-identity h3 {
|
|
1865
|
+
margin: 0;
|
|
1866
|
+
font-size: 15px;
|
|
1867
|
+
font-weight: 500;
|
|
1868
|
+
overflow-wrap: anywhere;
|
|
1869
|
+
}
|
|
1870
|
+
.account-muted {
|
|
1871
|
+
color: var(--muted);
|
|
1872
|
+
overflow-wrap: anywhere;
|
|
1873
|
+
}
|
|
1874
|
+
.account-body dl {
|
|
1875
|
+
display: grid;
|
|
1876
|
+
grid-template-columns: auto minmax(0, 1fr);
|
|
1877
|
+
gap: 12px 16px;
|
|
1878
|
+
margin: 24px 0;
|
|
1879
|
+
}
|
|
1880
|
+
.account-body dt {
|
|
1881
|
+
color: var(--muted);
|
|
1882
|
+
}
|
|
1883
|
+
.account-body dd {
|
|
1884
|
+
margin: 0;
|
|
1885
|
+
overflow-wrap: anywhere;
|
|
1886
|
+
}
|
|
1887
|
+
.account-body #account-error {
|
|
1888
|
+
margin-top: 18px;
|
|
1889
|
+
color: var(--vscode-errorForeground);
|
|
1890
|
+
overflow-wrap: anywhere;
|
|
1891
|
+
}
|
|
1892
|
+
@media (max-width: 680px) {
|
|
1893
|
+
.web-account {
|
|
1894
|
+
margin: 0 8px 4px;
|
|
1895
|
+
min-height: 36px;
|
|
1896
|
+
padding: 6px 10px;
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
@media (max-width: 420px) {
|
|
1900
|
+
.account-body {
|
|
1901
|
+
padding: 18px;
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
/* src/web/ui/connection.css */
|
|
1906
|
+
.connection-banner {
|
|
1907
|
+
display: flex;
|
|
1908
|
+
align-items: center;
|
|
1909
|
+
gap: 10px;
|
|
1910
|
+
flex-shrink: 0;
|
|
1911
|
+
min-width: 0;
|
|
1912
|
+
padding: 12px 20px;
|
|
1913
|
+
border-bottom: 1px solid var(--border);
|
|
1914
|
+
background: var(--vscode-sideBar-background);
|
|
1915
|
+
color: var(--muted);
|
|
1916
|
+
font-size: 12px;
|
|
1917
|
+
line-height: 1.6;
|
|
1918
|
+
}
|
|
1919
|
+
.connection-icon {
|
|
1920
|
+
display: grid;
|
|
1921
|
+
place-items: center;
|
|
1922
|
+
width: 20px;
|
|
1923
|
+
height: 20px;
|
|
1924
|
+
flex-shrink: 0;
|
|
1925
|
+
}
|
|
1926
|
+
.connection-banner[data-status=connecting] .connection-icon .codicon,
|
|
1927
|
+
.connection-banner[data-status=reconnecting] .connection-icon .codicon {
|
|
1928
|
+
animation: spin 1.2s linear infinite;
|
|
1929
|
+
}
|
|
1930
|
+
.connection-copy {
|
|
1931
|
+
display: flex;
|
|
1932
|
+
flex: 1;
|
|
1933
|
+
flex-wrap: wrap;
|
|
1934
|
+
align-items: baseline;
|
|
1935
|
+
gap: 2px 10px;
|
|
1936
|
+
min-width: 0;
|
|
1937
|
+
overflow-wrap: anywhere;
|
|
1938
|
+
}
|
|
1939
|
+
.connection-copy strong {
|
|
1940
|
+
font-weight: 500;
|
|
1941
|
+
color: var(--vscode-editor-foreground);
|
|
1942
|
+
}
|
|
1943
|
+
.connection-retry {
|
|
1944
|
+
display: inline-flex;
|
|
1945
|
+
align-items: center;
|
|
1946
|
+
gap: 6px;
|
|
1947
|
+
flex-shrink: 0;
|
|
1948
|
+
min-height: 30px;
|
|
1949
|
+
padding: 4px 8px;
|
|
1950
|
+
border: none;
|
|
1951
|
+
border-radius: 4px;
|
|
1952
|
+
background: transparent;
|
|
1953
|
+
color: var(--vscode-editor-foreground);
|
|
1954
|
+
font-size: 12px;
|
|
1955
|
+
}
|
|
1956
|
+
.connection-retry:hover:not(:disabled) {
|
|
1957
|
+
background: var(--vscode-toolbar-hoverBackground);
|
|
1958
|
+
}
|
|
1959
|
+
@media (max-width: 680px) {
|
|
1960
|
+
.connection-banner {
|
|
1961
|
+
padding: 10px 12px;
|
|
1962
|
+
align-items: flex-start;
|
|
1963
|
+
}
|
|
1964
|
+
.connection-copy {
|
|
1965
|
+
flex-direction: column;
|
|
1966
|
+
align-items: flex-start;
|
|
1967
|
+
}
|
|
1968
|
+
.connection-retry {
|
|
1969
|
+
margin-top: -3px;
|
|
1970
|
+
}
|
|
1971
|
+
}
|
package/dist/web/workflow-ui.js
CHANGED
|
@@ -3917,6 +3917,342 @@ ${JSON.stringify(output.details, null, 2)}` : "");
|
|
|
3917
3917
|
}
|
|
3918
3918
|
};
|
|
3919
3919
|
|
|
3920
|
+
// src/web/ui/account.ts
|
|
3921
|
+
var AccountPanel = class {
|
|
3922
|
+
constructor(menu, request) {
|
|
3923
|
+
this.request = request;
|
|
3924
|
+
this.button.id = "web-account";
|
|
3925
|
+
this.button.className = "web-account";
|
|
3926
|
+
this.button.type = "button";
|
|
3927
|
+
this.button.hidden = true;
|
|
3928
|
+
this.button.setAttribute("aria-haspopup", "dialog");
|
|
3929
|
+
this.button.setAttribute("aria-controls", "account-dialog");
|
|
3930
|
+
this.button.innerHTML = '<span class="web-avatar"></span><span class="web-account-name"></span>';
|
|
3931
|
+
this.button.addEventListener("click", () => this.open());
|
|
3932
|
+
menu.append(this.button);
|
|
3933
|
+
this.dialog.id = "account-dialog";
|
|
3934
|
+
this.dialog.className = "conversation-dialog account-dialog";
|
|
3935
|
+
this.dialog.setAttribute("aria-labelledby", "account-title");
|
|
3936
|
+
this.dialog.innerHTML = `
|
|
3937
|
+
<header><span class="codicon codicon-account" aria-hidden="true"></span><h2 id="account-title">Workflow \u8D26\u53F7</h2><button id="close-account" type="button" class="icon-button" title="\u5173\u95ED\u8D26\u53F7" aria-label="\u5173\u95ED\u8D26\u53F7">${iconHtml("close")}</button></header>
|
|
3938
|
+
<div class="dialog-body account-body">
|
|
3939
|
+
<div id="account-status" role="status"></div>
|
|
3940
|
+
<form id="login-form">
|
|
3941
|
+
<label for="server-url">\u670D\u52A1\u5668</label><input id="server-url" type="url" required spellcheck="false" autocomplete="url">
|
|
3942
|
+
<label for="login-email">\u90AE\u7BB1</label><input id="login-email" type="email" required autocomplete="username">
|
|
3943
|
+
<label for="login-password">\u5BC6\u7801</label><div class="account-password"><input id="login-password" type="password" required autocomplete="current-password"><button id="show-password" type="button" class="icon-button" title="\u663E\u793A\u5BC6\u7801" aria-label="\u663E\u793A\u5BC6\u7801">${iconHtml("eye")}</button></div>
|
|
3944
|
+
<div class="account-actions"><button id="login-submit" type="submit" class="account-primary">${iconHtml("sign-in")}<span>\u767B\u5F55</span></button><button id="browser-login" type="button" class="account-secondary">${iconHtml("link-external")}<span>\u6D4F\u89C8\u5668\u767B\u5F55</span></button></div>
|
|
3945
|
+
</form>
|
|
3946
|
+
<section id="account-profile" hidden>
|
|
3947
|
+
<div class="account-identity"><span class="account-avatar"></span><div><h3 id="account-display-name"></h3><div id="account-email" class="account-muted"></div></div></div>
|
|
3948
|
+
<dl><dt>\u670D\u52A1\u5668</dt><dd id="account-server"></dd><dt>\u6709\u6548\u671F</dt><dd id="account-expiry"></dd></dl>
|
|
3949
|
+
<div class="account-actions"><button id="verify-login" type="button" class="account-secondary">${iconHtml("verified")}<span>\u9A8C\u8BC1\u767B\u5F55</span></button><button id="account-logout" type="button" class="account-secondary">${iconHtml("sign-out")}<span>\u9000\u51FA\u767B\u5F55</span></button></div>
|
|
3950
|
+
</section>
|
|
3951
|
+
<div id="account-error" role="alert" hidden></div>
|
|
3952
|
+
</div>`;
|
|
3953
|
+
document.body.append(this.dialog);
|
|
3954
|
+
this.element("#close-account").addEventListener("click", () => this.dialog.close());
|
|
3955
|
+
this.dialog.addEventListener("close", () => {
|
|
3956
|
+
this.element("#login-password").value = "";
|
|
3957
|
+
this.button.focus();
|
|
3958
|
+
});
|
|
3959
|
+
this.element("#login-form").addEventListener("submit", (event) => {
|
|
3960
|
+
event.preventDefault();
|
|
3961
|
+
if (this.pending || !this.element("#login-form").reportValidity()) {
|
|
3962
|
+
return;
|
|
3963
|
+
}
|
|
3964
|
+
const password = this.element("#login-password");
|
|
3965
|
+
const action = { type: "login", serverUrl: this.element("#server-url").value, email: this.element("#login-email").value, password: password.value };
|
|
3966
|
+
password.value = "";
|
|
3967
|
+
void this.perform(action);
|
|
3968
|
+
});
|
|
3969
|
+
this.element("#browser-login").addEventListener("click", () => {
|
|
3970
|
+
const server = this.element("#server-url");
|
|
3971
|
+
if (!this.pending && server.reportValidity()) {
|
|
3972
|
+
void this.perform({ type: "login", serverUrl: server.value });
|
|
3973
|
+
}
|
|
3974
|
+
});
|
|
3975
|
+
this.element("#show-password").addEventListener("click", (event) => {
|
|
3976
|
+
const password = this.element("#login-password");
|
|
3977
|
+
const visible = password.type === "password";
|
|
3978
|
+
password.type = visible ? "text" : "password";
|
|
3979
|
+
const button = event.currentTarget;
|
|
3980
|
+
button.innerHTML = iconHtml(visible ? "eye-closed" : "eye");
|
|
3981
|
+
button.title = button.ariaLabel = visible ? "\u9690\u85CF\u5BC6\u7801" : "\u663E\u793A\u5BC6\u7801";
|
|
3982
|
+
});
|
|
3983
|
+
this.element("#verify-login").addEventListener("click", () => {
|
|
3984
|
+
if (!this.pending) {
|
|
3985
|
+
void this.perform({ type: "verify" });
|
|
3986
|
+
}
|
|
3987
|
+
});
|
|
3988
|
+
this.element("#account-logout").addEventListener("click", () => {
|
|
3989
|
+
if (!this.pending) {
|
|
3990
|
+
void this.perform({ type: "logout" });
|
|
3991
|
+
}
|
|
3992
|
+
});
|
|
3993
|
+
}
|
|
3994
|
+
request;
|
|
3995
|
+
button = document.createElement("button");
|
|
3996
|
+
dialog = document.createElement("dialog");
|
|
3997
|
+
state;
|
|
3998
|
+
pending = false;
|
|
3999
|
+
avatarKey = "";
|
|
4000
|
+
element(selector) {
|
|
4001
|
+
return this.dialog.querySelector(selector);
|
|
4002
|
+
}
|
|
4003
|
+
open() {
|
|
4004
|
+
this.render();
|
|
4005
|
+
if (!this.dialog.open) {
|
|
4006
|
+
this.dialog.showModal();
|
|
4007
|
+
}
|
|
4008
|
+
}
|
|
4009
|
+
update(state) {
|
|
4010
|
+
this.state = state;
|
|
4011
|
+
this.button.hidden = false;
|
|
4012
|
+
this.pending = state.status === "checking";
|
|
4013
|
+
this.render();
|
|
4014
|
+
}
|
|
4015
|
+
render() {
|
|
4016
|
+
const state = this.state;
|
|
4017
|
+
if (!state) {
|
|
4018
|
+
return;
|
|
4019
|
+
}
|
|
4020
|
+
const signedIn = !!state.user && ["authenticated", "checking", "error"].includes(state.status);
|
|
4021
|
+
const name = signedIn ? state.user.displayName || state.user.email || "Workflow" : this.pending ? "\u767B\u5F55\u4E2D..." : "\u767B\u5F55 Workflow";
|
|
4022
|
+
this.button.querySelector(".web-account-name").textContent = name;
|
|
4023
|
+
this.button.title = this.button.ariaLabel = state.error || state.user?.email || name;
|
|
4024
|
+
const avatarKey = `${state.serverUrl}
|
|
4025
|
+
${state.user?.avatarUrl ?? ""}`;
|
|
4026
|
+
if (avatarKey !== this.avatarKey) {
|
|
4027
|
+
this.avatarKey = avatarKey;
|
|
4028
|
+
for (const avatar of [this.button.querySelector(".web-avatar"), this.element(".account-avatar")]) {
|
|
4029
|
+
avatar.innerHTML = iconHtml("account");
|
|
4030
|
+
if (!state.user?.avatarUrl) {
|
|
4031
|
+
continue;
|
|
4032
|
+
}
|
|
4033
|
+
let url;
|
|
4034
|
+
try {
|
|
4035
|
+
url = new URL(state.user.avatarUrl, `${state.serverUrl}/`);
|
|
4036
|
+
} catch {
|
|
4037
|
+
continue;
|
|
4038
|
+
}
|
|
4039
|
+
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password) {
|
|
4040
|
+
continue;
|
|
4041
|
+
}
|
|
4042
|
+
const image = document.createElement("img");
|
|
4043
|
+
image.src = url.toString();
|
|
4044
|
+
image.alt = "";
|
|
4045
|
+
image.hidden = true;
|
|
4046
|
+
image.referrerPolicy = "no-referrer";
|
|
4047
|
+
image.addEventListener("load", () => {
|
|
4048
|
+
image.hidden = false;
|
|
4049
|
+
avatar.firstElementChild.hidden = true;
|
|
4050
|
+
});
|
|
4051
|
+
image.addEventListener("error", () => {
|
|
4052
|
+
image.hidden = true;
|
|
4053
|
+
avatar.firstElementChild.hidden = false;
|
|
4054
|
+
});
|
|
4055
|
+
avatar.append(image);
|
|
4056
|
+
}
|
|
4057
|
+
}
|
|
4058
|
+
this.element("#login-form").hidden = signedIn;
|
|
4059
|
+
this.element("#account-profile").hidden = !signedIn;
|
|
4060
|
+
const server = this.element("#server-url");
|
|
4061
|
+
if (!server.value || signedIn && document.activeElement !== server) {
|
|
4062
|
+
server.value = state.serverUrl;
|
|
4063
|
+
}
|
|
4064
|
+
this.dialog.querySelectorAll("input, #login-submit, #browser-login, #verify-login, #account-logout").forEach((element) => {
|
|
4065
|
+
element.disabled = this.pending;
|
|
4066
|
+
});
|
|
4067
|
+
this.element("#login-submit > span:last-child").textContent = this.pending ? "\u767B\u5F55\u4E2D..." : "\u767B\u5F55";
|
|
4068
|
+
const status = this.element("#account-status");
|
|
4069
|
+
status.textContent = state.status === "authenticated" ? "\u5DF2\u767B\u5F55\uFF0C\u8D26\u53F7\u9A8C\u8BC1\u901A\u8FC7" : this.pending ? "\u6B63\u5728\u9A8C\u8BC1\u8D26\u53F7..." : state.status === "expired" ? "\u767B\u5F55\u5DF2\u5931\u6548" : state.status === "error" && state.user ? "\u8FDE\u63A5\u672A\u9A8C\u8BC1" : "\u672A\u767B\u5F55";
|
|
4070
|
+
status.classList.toggle("connected", state.status === "authenticated");
|
|
4071
|
+
this.showError(state.error);
|
|
4072
|
+
if (state.user) {
|
|
4073
|
+
this.element("#account-display-name").textContent = state.user.displayName || state.user.email;
|
|
4074
|
+
this.element("#account-email").textContent = state.user.email;
|
|
4075
|
+
this.element("#account-server").textContent = state.serverUrl;
|
|
4076
|
+
this.element("#account-expiry").textContent = state.expiresAt ? new Date(state.expiresAt).toLocaleString("zh-CN") : "\u7531\u670D\u52A1\u5668\u7BA1\u7406";
|
|
4077
|
+
}
|
|
4078
|
+
}
|
|
4079
|
+
showError(message) {
|
|
4080
|
+
const error = this.element("#account-error");
|
|
4081
|
+
error.textContent = message ?? "";
|
|
4082
|
+
error.hidden = !message;
|
|
4083
|
+
}
|
|
4084
|
+
async perform(action) {
|
|
4085
|
+
this.pending = true;
|
|
4086
|
+
this.render();
|
|
4087
|
+
this.showError();
|
|
4088
|
+
try {
|
|
4089
|
+
await this.request(action);
|
|
4090
|
+
} catch (error) {
|
|
4091
|
+
this.pending = false;
|
|
4092
|
+
this.render();
|
|
4093
|
+
this.showError(error.message);
|
|
4094
|
+
}
|
|
4095
|
+
}
|
|
4096
|
+
};
|
|
4097
|
+
|
|
4098
|
+
// src/web/ui/connection.ts
|
|
4099
|
+
var ConnectionError = class extends Error {
|
|
4100
|
+
};
|
|
4101
|
+
var BrowserConnection = class {
|
|
4102
|
+
constructor(emit) {
|
|
4103
|
+
this.emit = emit;
|
|
4104
|
+
if (this.token) {
|
|
4105
|
+
history.replaceState(null, "", `${location.pathname}${location.search}`);
|
|
4106
|
+
}
|
|
4107
|
+
queueMicrotask(() => {
|
|
4108
|
+
this.banner = document.createElement("div");
|
|
4109
|
+
this.banner.id = "connection-status";
|
|
4110
|
+
this.banner.className = "connection-banner";
|
|
4111
|
+
this.banner.setAttribute("role", "status");
|
|
4112
|
+
this.banner.setAttribute("aria-live", "polite");
|
|
4113
|
+
this.banner.innerHTML = `<span class="connection-icon" aria-hidden="true"></span><div class="connection-copy"><strong></strong><span></span></div><button type="button" class="connection-retry">${iconHtml("refresh")}<span>\u91CD\u65B0\u8FDE\u63A5</span></button>`;
|
|
4114
|
+
this.banner.querySelector("button").addEventListener("click", () => {
|
|
4115
|
+
void this.connect();
|
|
4116
|
+
});
|
|
4117
|
+
document.querySelector("main").prepend(this.banner);
|
|
4118
|
+
void this.connect();
|
|
4119
|
+
});
|
|
4120
|
+
addEventListener("offline", () => {
|
|
4121
|
+
this.source?.close();
|
|
4122
|
+
clearTimeout(this.retry);
|
|
4123
|
+
this.update("offline");
|
|
4124
|
+
});
|
|
4125
|
+
addEventListener("online", () => {
|
|
4126
|
+
void this.connect();
|
|
4127
|
+
});
|
|
4128
|
+
addEventListener("pagehide", () => {
|
|
4129
|
+
this.closed = true;
|
|
4130
|
+
clearTimeout(this.retry);
|
|
4131
|
+
this.source?.close();
|
|
4132
|
+
}, { once: true });
|
|
4133
|
+
}
|
|
4134
|
+
emit;
|
|
4135
|
+
source;
|
|
4136
|
+
banner;
|
|
4137
|
+
connecting;
|
|
4138
|
+
retry;
|
|
4139
|
+
status = "connecting";
|
|
4140
|
+
attempts = 0;
|
|
4141
|
+
closed = false;
|
|
4142
|
+
token = new URLSearchParams(location.hash.slice(1)).get("session");
|
|
4143
|
+
update(status) {
|
|
4144
|
+
this.status = status;
|
|
4145
|
+
this.emit({ type: "connection", status });
|
|
4146
|
+
if (!this.banner) {
|
|
4147
|
+
return;
|
|
4148
|
+
}
|
|
4149
|
+
this.banner.hidden = status === "connected";
|
|
4150
|
+
this.banner.dataset.status = status;
|
|
4151
|
+
const retrying = status === "connecting" || status === "reconnecting";
|
|
4152
|
+
this.banner.querySelector(".connection-icon").innerHTML = iconHtml(retrying ? "loading" : status === "expired" ? "warning" : "debug-disconnect");
|
|
4153
|
+
this.banner.querySelector("strong").textContent = status === "connecting" ? "\u6B63\u5728\u8FDE\u63A5\u9879\u76EE" : status === "expired" ? "\u9879\u76EE\u8FDE\u63A5\u5DF2\u5931\u6548" : "\u8FDE\u63A5\u5DF2\u4E2D\u65AD";
|
|
4154
|
+
this.banner.querySelector(".connection-copy > span").textContent = status === "reconnecting" ? "\u6B63\u5728\u91CD\u65B0\u8FDE\u63A5..." : status === "offline" ? "\u7F51\u7EDC\u6062\u590D\u540E\u5C06\u81EA\u52A8\u91CD\u8FDE\u3002" : status === "expired" ? "\u8BF7\u4F7F\u7528\u9879\u76EE\u8FD0\u884C\u5730\u5740\u91CD\u65B0\u6253\u5F00\u3002" : "";
|
|
4155
|
+
this.banner.querySelector("button").disabled = status === "offline" || !!this.connecting;
|
|
4156
|
+
}
|
|
4157
|
+
interrupted() {
|
|
4158
|
+
this.source?.close();
|
|
4159
|
+
clearTimeout(this.retry);
|
|
4160
|
+
this.update(navigator.onLine ? "reconnecting" : "offline");
|
|
4161
|
+
if (!this.closed && navigator.onLine) {
|
|
4162
|
+
this.retry = setTimeout(() => {
|
|
4163
|
+
void this.connect();
|
|
4164
|
+
}, Math.min(2e3 * 2 ** this.attempts++, 3e4));
|
|
4165
|
+
}
|
|
4166
|
+
}
|
|
4167
|
+
connect() {
|
|
4168
|
+
if (this.connecting) {
|
|
4169
|
+
return this.connecting;
|
|
4170
|
+
}
|
|
4171
|
+
clearTimeout(this.retry);
|
|
4172
|
+
if (this.closed) {
|
|
4173
|
+
return Promise.resolve();
|
|
4174
|
+
}
|
|
4175
|
+
if (!navigator.onLine) {
|
|
4176
|
+
this.update("offline");
|
|
4177
|
+
return Promise.resolve();
|
|
4178
|
+
}
|
|
4179
|
+
this.source?.close();
|
|
4180
|
+
this.update(this.attempts ? "reconnecting" : "connecting");
|
|
4181
|
+
this.connecting = (async () => {
|
|
4182
|
+
try {
|
|
4183
|
+
if (this.token) {
|
|
4184
|
+
const session2 = await fetch("/api/session", { method: "POST", headers: { Authorization: `Bearer ${this.token}` }, signal: AbortSignal.timeout(1e4) });
|
|
4185
|
+
if (!session2.ok) {
|
|
4186
|
+
this.update("expired");
|
|
4187
|
+
return;
|
|
4188
|
+
}
|
|
4189
|
+
this.token = null;
|
|
4190
|
+
}
|
|
4191
|
+
const session = await fetch("/api/session", { signal: AbortSignal.timeout(1e4) });
|
|
4192
|
+
if (session.status === 401) {
|
|
4193
|
+
this.update("expired");
|
|
4194
|
+
return;
|
|
4195
|
+
}
|
|
4196
|
+
if (!session.ok) {
|
|
4197
|
+
throw new Error("Cannot connect to the project.");
|
|
4198
|
+
}
|
|
4199
|
+
const source = this.source = new EventSource("/api/events");
|
|
4200
|
+
source.onopen = () => {
|
|
4201
|
+
this.attempts = 0;
|
|
4202
|
+
this.update("connected");
|
|
4203
|
+
};
|
|
4204
|
+
source.onmessage = (event) => {
|
|
4205
|
+
this.emit(JSON.parse(event.data));
|
|
4206
|
+
};
|
|
4207
|
+
source.onerror = () => {
|
|
4208
|
+
if (this.source === source && !this.closed) {
|
|
4209
|
+
this.interrupted();
|
|
4210
|
+
}
|
|
4211
|
+
};
|
|
4212
|
+
} catch {
|
|
4213
|
+
if (!this.closed) {
|
|
4214
|
+
this.interrupted();
|
|
4215
|
+
}
|
|
4216
|
+
}
|
|
4217
|
+
})().finally(() => {
|
|
4218
|
+
this.connecting = void 0;
|
|
4219
|
+
if (this.banner) {
|
|
4220
|
+
this.banner.querySelector("button").disabled = this.status === "offline";
|
|
4221
|
+
}
|
|
4222
|
+
});
|
|
4223
|
+
return this.connecting;
|
|
4224
|
+
}
|
|
4225
|
+
async request(message) {
|
|
4226
|
+
await this.connecting;
|
|
4227
|
+
if (this.status !== "connected") {
|
|
4228
|
+
throw new ConnectionError("\u8FDE\u63A5\u5DF2\u4E2D\u65AD\u3002");
|
|
4229
|
+
}
|
|
4230
|
+
let response;
|
|
4231
|
+
try {
|
|
4232
|
+
response = await fetch("/api/message", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(message) });
|
|
4233
|
+
} catch {
|
|
4234
|
+
this.interrupted();
|
|
4235
|
+
throw new ConnectionError("\u8FDE\u63A5\u5DF2\u4E2D\u65AD\u3002");
|
|
4236
|
+
}
|
|
4237
|
+
if (response.status === 401) {
|
|
4238
|
+
this.source?.close();
|
|
4239
|
+
this.update("expired");
|
|
4240
|
+
throw new ConnectionError("\u9879\u76EE\u8FDE\u63A5\u5DF2\u5931\u6548\u3002");
|
|
4241
|
+
}
|
|
4242
|
+
let reply;
|
|
4243
|
+
try {
|
|
4244
|
+
reply = await response.json();
|
|
4245
|
+
} catch {
|
|
4246
|
+
this.interrupted();
|
|
4247
|
+
throw new ConnectionError("\u8FDE\u63A5\u5DF2\u4E2D\u65AD\u3002");
|
|
4248
|
+
}
|
|
4249
|
+
if (!response.ok) {
|
|
4250
|
+
throw new Error(reply.message || "Request failed.");
|
|
4251
|
+
}
|
|
4252
|
+
return reply.messages ?? [];
|
|
4253
|
+
}
|
|
4254
|
+
};
|
|
4255
|
+
|
|
3920
4256
|
// src/web/ui/host.ts
|
|
3921
4257
|
function acquireWorkflowHost() {
|
|
3922
4258
|
const embedded = globalThis.acquireVsCodeApi;
|
|
@@ -3933,38 +4269,14 @@ ${JSON.stringify(output.details, null, 2)}` : "");
|
|
|
3933
4269
|
}
|
|
3934
4270
|
const storageKey = `workflow-web:${document.querySelector("meta[name=workflow-project]")?.content ?? location.pathname}`;
|
|
3935
4271
|
const emit = (message) => window.dispatchEvent(new MessageEvent("message", { data: message }));
|
|
3936
|
-
const
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
}
|
|
3940
|
-
let source;
|
|
3941
|
-
const connected = (async () => {
|
|
3942
|
-
if (token) {
|
|
3943
|
-
const response = await fetch("/api/session", { method: "POST", headers: { Authorization: `Bearer ${token}` } });
|
|
3944
|
-
if (!response.ok) {
|
|
3945
|
-
throw new Error("Web session is unavailable. Open the URL printed by workflow-code web.");
|
|
3946
|
-
}
|
|
4272
|
+
const connection = new BrowserConnection((message) => {
|
|
4273
|
+
if (message.type === "state" && message.account) {
|
|
4274
|
+
accountPanel.update(message.account);
|
|
3947
4275
|
}
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
const message = JSON.parse(event.data);
|
|
3951
|
-
if (message.type === "state") {
|
|
3952
|
-
updateAccount(message);
|
|
3953
|
-
}
|
|
3954
|
-
emit(message);
|
|
3955
|
-
};
|
|
3956
|
-
source.onerror = () => emit({ type: "error", message: "\u8FDE\u63A5\u5DF2\u4E2D\u65AD\uFF0C\u6B63\u5728\u91CD\u65B0\u8FDE\u63A5..." });
|
|
3957
|
-
addEventListener("pagehide", () => source.close(), { once: true });
|
|
3958
|
-
})();
|
|
3959
|
-
connected.catch((error) => emit({ type: "error", message: String(error.message) }));
|
|
4276
|
+
emit(message);
|
|
4277
|
+
});
|
|
3960
4278
|
const request = async (message) => {
|
|
3961
|
-
await
|
|
3962
|
-
const response = await fetch("/api/message", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(message) });
|
|
3963
|
-
const reply = await response.json();
|
|
3964
|
-
if (!response.ok) {
|
|
3965
|
-
throw new Error(reply.message || "Request failed.");
|
|
3966
|
-
}
|
|
3967
|
-
for (const entry of reply.messages ?? []) {
|
|
4279
|
+
for (const entry of await connection.request(message)) {
|
|
3968
4280
|
handleReply(entry);
|
|
3969
4281
|
}
|
|
3970
4282
|
};
|
|
@@ -4024,44 +4336,15 @@ ${JSON.stringify(output.details, null, 2)}` : "");
|
|
|
4024
4336
|
picker.addEventListener("cancel", () => emit({ type: "attachment-cancelled", sessionId: message.sessionId }));
|
|
4025
4337
|
picker.click();
|
|
4026
4338
|
};
|
|
4027
|
-
let
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
if (!button) {
|
|
4032
|
-
button = document.createElement("button");
|
|
4033
|
-
button.id = "web-account";
|
|
4034
|
-
button.className = "web-account";
|
|
4035
|
-
document.querySelector("#project-menu").append(button);
|
|
4036
|
-
button.onclick = () => {
|
|
4037
|
-
if (account?.status === "authenticated") {
|
|
4038
|
-
if (confirm("\u9000\u51FA Workflow \u767B\u5F55\uFF1F")) {
|
|
4039
|
-
void request({ type: "logout" }).catch((error) => emit({ type: "error", message: error.message }));
|
|
4040
|
-
}
|
|
4041
|
-
} else {
|
|
4042
|
-
const serverUrl = prompt("Workflow Server", account?.serverUrl || "http://localhost:7126");
|
|
4043
|
-
if (serverUrl) {
|
|
4044
|
-
void request({ type: "login", serverUrl }).catch((error) => emit({ type: "error", message: error.message }));
|
|
4045
|
-
}
|
|
4046
|
-
}
|
|
4047
|
-
};
|
|
4048
|
-
}
|
|
4049
|
-
button.replaceChildren();
|
|
4050
|
-
const avatar = document.createElement("span");
|
|
4051
|
-
avatar.className = "web-avatar codicon codicon-account";
|
|
4052
|
-
if (account?.user?.avatarUrl && /^https?:\/\//.test(account.user.avatarUrl)) {
|
|
4053
|
-
const image = document.createElement("img");
|
|
4054
|
-
image.src = account.user.avatarUrl;
|
|
4055
|
-
image.alt = "";
|
|
4056
|
-
avatar.append(image);
|
|
4057
|
-
}
|
|
4058
|
-
const name = document.createElement("span");
|
|
4059
|
-
name.textContent = account?.status === "authenticated" ? account.user?.displayName || account.user?.email || "Workflow" : account?.status === "checking" ? "\u767B\u5F55\u4E2D..." : "\u767B\u5F55 Workflow";
|
|
4060
|
-
button.title = account?.error || account?.user?.email || name.textContent;
|
|
4061
|
-
button.append(avatar, name);
|
|
4062
|
-
};
|
|
4339
|
+
let accountPanel;
|
|
4340
|
+
queueMicrotask(() => {
|
|
4341
|
+
accountPanel = new AccountPanel(document.querySelector("#project-menu"), request);
|
|
4342
|
+
});
|
|
4063
4343
|
return {
|
|
4064
4344
|
postMessage: (message) => {
|
|
4345
|
+
if (message.type === "ready") {
|
|
4346
|
+
return;
|
|
4347
|
+
}
|
|
4065
4348
|
if (message.type === "copy") {
|
|
4066
4349
|
void navigator.clipboard.writeText(message.text).catch((error) => emit({ type: "error", message: error.message }));
|
|
4067
4350
|
return;
|
|
@@ -4085,10 +4368,10 @@ ${JSON.stringify(output.details, null, 2)}` : "");
|
|
|
4085
4368
|
return;
|
|
4086
4369
|
}
|
|
4087
4370
|
if (message.type === "login") {
|
|
4088
|
-
|
|
4371
|
+
accountPanel.open();
|
|
4089
4372
|
return;
|
|
4090
4373
|
}
|
|
4091
|
-
void request(message).catch((error) => emit({ type: "error", requestType: message.type, sessionId: message.sessionId, messageId: message.messageId, requestId: message.requestId, message: error.message }));
|
|
4374
|
+
void request(message).catch((error) => emit({ type: "error", silent: error instanceof ConnectionError, requestType: message.type, sessionId: message.sessionId, messageId: message.messageId, requestId: message.requestId, message: error.message }));
|
|
4092
4375
|
},
|
|
4093
4376
|
getState: () => {
|
|
4094
4377
|
try {
|
|
@@ -4140,6 +4423,7 @@ ${JSON.stringify(output.details, null, 2)}` : "");
|
|
|
4140
4423
|
let formKey = "";
|
|
4141
4424
|
let paramStates = {};
|
|
4142
4425
|
let resolving = false;
|
|
4426
|
+
let connectionAvailable = true;
|
|
4143
4427
|
let submitting = false;
|
|
4144
4428
|
let requestId = 0;
|
|
4145
4429
|
let paramsNeedInit = false;
|
|
@@ -4226,7 +4510,7 @@ ${JSON.stringify(output.details, null, 2)}` : "");
|
|
|
4226
4510
|
}
|
|
4227
4511
|
function requestParams(trigger) {
|
|
4228
4512
|
clearTimeout(inputTimer);
|
|
4229
|
-
if (!state || busy() || !fields.size) {
|
|
4513
|
+
if (!state || !connectionAvailable || busy() || !fields.size) {
|
|
4230
4514
|
return;
|
|
4231
4515
|
}
|
|
4232
4516
|
paramsNeedInit = false;
|
|
@@ -4457,7 +4741,7 @@ ${JSON.stringify(output.details, null, 2)}` : "");
|
|
|
4457
4741
|
sendButton.innerHTML = iconHtml(stop ? "debug-stop" : submitting || uploading ? "loading" : conversation ? "arrow-up" : "play");
|
|
4458
4742
|
sendButton.title = sendButton.ariaLabel = stop ? "\u505C\u6B62" : editingQueueId ? "\u4FDD\u5B58\u5F85\u53D1\u6D88\u606F" : conversation ? "\u53D1\u9001" : "\u8FD0\u884C";
|
|
4459
4743
|
sendButton.classList.toggle("stop", stop);
|
|
4460
|
-
sendButton.disabled = !stop && (submitting || resolving || uploading > 0 || conversation && state.project.conversation?.defaultInput?.enabled !== false && !hasInput);
|
|
4744
|
+
sendButton.disabled = !connectionAvailable || !stop && (submitting || resolving || uploading > 0 || conversation && state.project.conversation?.defaultInput?.enabled !== false && !hasInput);
|
|
4461
4745
|
textarea.disabled = busy();
|
|
4462
4746
|
entrypoint.disabled = busy() || resolving;
|
|
4463
4747
|
document.querySelector("#new").disabled = false;
|
|
@@ -5223,6 +5507,21 @@ ${JSON.stringify(output.details, null, 2)}` : "");
|
|
|
5223
5507
|
let renderScheduled = false;
|
|
5224
5508
|
window.addEventListener("message", (event) => {
|
|
5225
5509
|
const message = event.data;
|
|
5510
|
+
if (message.type === "connection") {
|
|
5511
|
+
const restored = !connectionAvailable && message.status === "connected";
|
|
5512
|
+
connectionAvailable = message.status === "connected";
|
|
5513
|
+
if (!connectionAvailable) {
|
|
5514
|
+
resolving = false;
|
|
5515
|
+
requestId++;
|
|
5516
|
+
}
|
|
5517
|
+
sendButton.disabled = !connectionAvailable || sendButton.disabled;
|
|
5518
|
+
updateControls();
|
|
5519
|
+
if (restored && state) {
|
|
5520
|
+
paramsNeedInit = true;
|
|
5521
|
+
requestParams({ kind: "init" });
|
|
5522
|
+
}
|
|
5523
|
+
return;
|
|
5524
|
+
}
|
|
5226
5525
|
if (message.type === "assistant-command") {
|
|
5227
5526
|
document.querySelector(message.command === "new" ? "#new" : "#history-toggle").click();
|
|
5228
5527
|
return;
|
|
@@ -5323,7 +5622,9 @@ ${JSON.stringify(output.details, null, 2)}` : "");
|
|
|
5323
5622
|
messages.get(message.messageId)?.prompt?.setBusy(false, message.message);
|
|
5324
5623
|
}
|
|
5325
5624
|
updateControls();
|
|
5326
|
-
|
|
5625
|
+
if (!message.silent) {
|
|
5626
|
+
showError(message.message);
|
|
5627
|
+
}
|
|
5327
5628
|
}
|
|
5328
5629
|
});
|
|
5329
5630
|
vscode.postMessage({ type: "ready" });
|
|
@@ -6,7 +6,8 @@ import "./chunk-J2JMC4MZ.js";
|
|
|
6
6
|
import "./chunk-G7PZDTYW.js";
|
|
7
7
|
import {
|
|
8
8
|
clearCliAuthConfig,
|
|
9
|
-
readCliAuthConfig
|
|
9
|
+
readCliAuthConfig,
|
|
10
|
+
writeCliAuthConfig
|
|
10
11
|
} from "./chunk-PGM3IPWA.js";
|
|
11
12
|
import "./chunk-BP2VDZQV.js";
|
|
12
13
|
import {
|
|
@@ -915,6 +916,52 @@ function conversationResourceCards(items, projectId, conversationId) {
|
|
|
915
916
|
return [...cards.values()];
|
|
916
917
|
}
|
|
917
918
|
|
|
919
|
+
// src/web/account-auth.ts
|
|
920
|
+
async function loginWebAccount(serverUrl, email, password) {
|
|
921
|
+
if (!email.trim() || !password) {
|
|
922
|
+
throw new Error("\u8BF7\u8F93\u5165\u90AE\u7BB1\u548C\u5BC6\u7801\u3002");
|
|
923
|
+
}
|
|
924
|
+
let token = "";
|
|
925
|
+
let cookie = "";
|
|
926
|
+
let apiKeyId = "";
|
|
927
|
+
let saved = false;
|
|
928
|
+
const request = async (endpoint, options = {}) => {
|
|
929
|
+
const response = await fetch(`${serverUrl}${endpoint}`, { ...options, redirect: "error", signal: AbortSignal.timeout(15e3) });
|
|
930
|
+
const result = await response.json();
|
|
931
|
+
if (!response.ok || result.errCode !== 0 || !result.data) {
|
|
932
|
+
const message = response.status === 401 ? "\u90AE\u7BB1\u6216\u5BC6\u7801\u4E0D\u6B63\u786E\uFF0C\u6216\u767B\u5F55\u5DF2\u5931\u6548\u3002" : result.errMessage || "Workflow Server login failed.";
|
|
933
|
+
throw new Error(String(message).replaceAll(password, "[redacted]").replaceAll(token || "\0", "[redacted]"));
|
|
934
|
+
}
|
|
935
|
+
return { response, data: result.data };
|
|
936
|
+
};
|
|
937
|
+
try {
|
|
938
|
+
const result = await request("/api/auth/login", {
|
|
939
|
+
method: "POST",
|
|
940
|
+
headers: { "Content-Type": "application/json" },
|
|
941
|
+
body: JSON.stringify({ email: email.trim(), password, source: "cli", clientName: "Workflow CLI Web" })
|
|
942
|
+
});
|
|
943
|
+
cookie = result.response.headers.getSetCookie().map((value) => value.split(";", 1)[0]).join("; ");
|
|
944
|
+
token = typeof result.data.accessToken === "string" ? result.data.accessToken : "";
|
|
945
|
+
apiKeyId = typeof result.data.apiKey?.id === "string" ? result.data.apiKey.id : "";
|
|
946
|
+
if (!result.data.authenticated || !token || !apiKeyId) {
|
|
947
|
+
throw new Error("Workflow Server did not return CLI login credentials.");
|
|
948
|
+
}
|
|
949
|
+
const { data: me } = await request("/api/auth/me", { headers: { Authorization: `Bearer ${token}` } });
|
|
950
|
+
if (typeof me.profile?.userId !== "string" || !me.profile.userId || typeof me.profile.email !== "string" || typeof me.profile.displayName !== "string") {
|
|
951
|
+
throw new Error("Workflow Server did not return an account profile.");
|
|
952
|
+
}
|
|
953
|
+
await writeCliAuthConfig({ serverUrl, apiKey: token, expiresAt: me.principal?.expiresAt ?? result.data.expiresAt, user: me.profile, source: "password" });
|
|
954
|
+
saved = true;
|
|
955
|
+
} finally {
|
|
956
|
+
if (cookie) {
|
|
957
|
+
if (!saved && apiKeyId) {
|
|
958
|
+
await request(`/api/auth/api-keys/${encodeURIComponent(apiKeyId)}`, { method: "DELETE", headers: { Cookie: cookie } }).catch(() => void 0);
|
|
959
|
+
}
|
|
960
|
+
await request("/api/auth/logout", { method: "POST", headers: { Cookie: cookie } }).catch(() => void 0);
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
|
|
918
965
|
// src/web/server.ts
|
|
919
966
|
async function startWebServer(options) {
|
|
920
967
|
const projectDir = await realpath(path2.resolve(options.projectDir));
|
|
@@ -927,6 +974,7 @@ async function startWebServer(options) {
|
|
|
927
974
|
let signedOut = false;
|
|
928
975
|
let authIdentity = "";
|
|
929
976
|
let login;
|
|
977
|
+
let passwordLogin = false;
|
|
930
978
|
const runtime = options.runtime ?? createWebRuntime(projectDir, storageDir, () => connection);
|
|
931
979
|
let inspected;
|
|
932
980
|
try {
|
|
@@ -1006,23 +1054,29 @@ async function startWebServer(options) {
|
|
|
1006
1054
|
return pendingSave;
|
|
1007
1055
|
}
|
|
1008
1056
|
});
|
|
1009
|
-
const refreshAuth = async () => {
|
|
1057
|
+
const refreshAuth = async (force = false) => {
|
|
1058
|
+
if (passwordLogin || login) {
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
1010
1061
|
const config = await readCliAuthConfig();
|
|
1011
1062
|
const serverUrl = process.env.WORKFLOW_SERVER_URL ?? config.serverUrl;
|
|
1012
1063
|
const token = process.env.WORKFLOW_SERVER_ADMIN_KEY ?? config.apiKey;
|
|
1013
1064
|
const identity = signedOut ? "" : `${serverUrl}
|
|
1014
1065
|
${token}`;
|
|
1015
|
-
if (identity === authIdentity && account.status !== "checking") {
|
|
1066
|
+
if (!force && identity === authIdentity && account.status !== "checking") {
|
|
1016
1067
|
return;
|
|
1017
1068
|
}
|
|
1069
|
+
const changed = identity !== authIdentity;
|
|
1018
1070
|
authIdentity = identity;
|
|
1019
1071
|
connection = !signedOut && serverUrl && token ? { serverUrl: serverUrl.replace(/\/+$/, ""), accessToken: token } : void 0;
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
controller.
|
|
1072
|
+
if (changed) {
|
|
1073
|
+
authRevision++;
|
|
1074
|
+
parameterStates.clear();
|
|
1075
|
+
for (const session of controller.sessions) {
|
|
1076
|
+
controller.stop(session);
|
|
1077
|
+
}
|
|
1024
1078
|
}
|
|
1025
|
-
account = { status: connection ? "checking" : "signed-out", serverUrl: serverUrl || "http://localhost:7126", revision:
|
|
1079
|
+
account = { status: connection ? "checking" : "signed-out", serverUrl: serverUrl || "http://localhost:7126", user: connection ? config.user ?? account.user : void 0, revision: account.revision + 1 };
|
|
1026
1080
|
if (connection) {
|
|
1027
1081
|
try {
|
|
1028
1082
|
const response = await fetch(`${connection.serverUrl}/api/auth/me`, { headers: { Authorization: `Bearer ${connection.accessToken}` }, signal: AbortSignal.timeout(1e4), redirect: "error" });
|
|
@@ -1033,6 +1087,13 @@ ${token}`;
|
|
|
1033
1087
|
account = { ...account, status: "authenticated", user: result.data.profile, expiresAt: result.data.principal?.expiresAt ?? config.expiresAt };
|
|
1034
1088
|
} catch (error) {
|
|
1035
1089
|
connection = void 0;
|
|
1090
|
+
if (!changed) {
|
|
1091
|
+
authRevision++;
|
|
1092
|
+
parameterStates.clear();
|
|
1093
|
+
for (const session of controller.sessions) {
|
|
1094
|
+
controller.stop(session);
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1036
1097
|
account = { ...account, status: "error", error: String(error.message) };
|
|
1037
1098
|
}
|
|
1038
1099
|
}
|
|
@@ -1281,7 +1342,7 @@ ${token}`;
|
|
|
1281
1342
|
if (process.env.WORKFLOW_SERVER_ADMIN_KEY !== void 0) {
|
|
1282
1343
|
throw new Error("This session is supplied by the host. Sign in through the host.");
|
|
1283
1344
|
}
|
|
1284
|
-
if (login) {
|
|
1345
|
+
if (login || passwordLogin) {
|
|
1285
1346
|
break;
|
|
1286
1347
|
}
|
|
1287
1348
|
const url = new URL(String(message.serverUrl));
|
|
@@ -1291,6 +1352,23 @@ ${token}`;
|
|
|
1291
1352
|
signedOut = false;
|
|
1292
1353
|
account = { ...account, status: "checking" };
|
|
1293
1354
|
broadcast();
|
|
1355
|
+
if (message.email !== void 0 || message.password !== void 0) {
|
|
1356
|
+
passwordLogin = true;
|
|
1357
|
+
try {
|
|
1358
|
+
if (typeof message.email !== "string" || typeof message.password !== "string") {
|
|
1359
|
+
throw new Error("Invalid login credentials.");
|
|
1360
|
+
}
|
|
1361
|
+
await loginWebAccount(url.toString().replace(/\/+$/, ""), message.email, message.password);
|
|
1362
|
+
} catch (error) {
|
|
1363
|
+
account = { ...account, status: "error", error: String(error.message) };
|
|
1364
|
+
broadcast();
|
|
1365
|
+
throw error;
|
|
1366
|
+
} finally {
|
|
1367
|
+
passwordLogin = false;
|
|
1368
|
+
}
|
|
1369
|
+
await refreshAuth(true);
|
|
1370
|
+
break;
|
|
1371
|
+
}
|
|
1294
1372
|
login = spawn2(process.execPath, [path2.join(cliRoot(), "dist/index.js"), "workspace", "login", "--server", url.toString().replace(/\/$/, "")], { shell: false, stdio: ["ignore", "ignore", "pipe"] });
|
|
1295
1373
|
login.stderr?.on("data", (chunk) => process.stderr.write(chunk));
|
|
1296
1374
|
login.once("error", (error) => {
|
|
@@ -1305,6 +1383,9 @@ ${token}`;
|
|
|
1305
1383
|
});
|
|
1306
1384
|
break;
|
|
1307
1385
|
}
|
|
1386
|
+
case "verify":
|
|
1387
|
+
await refreshAuth(true);
|
|
1388
|
+
break;
|
|
1308
1389
|
case "logout":
|
|
1309
1390
|
signedOut = true;
|
|
1310
1391
|
login?.kill();
|
|
@@ -1344,6 +1425,10 @@ ${token}`;
|
|
|
1344
1425
|
json(response, 401, { message: "Open the URL printed by workflow-code web." });
|
|
1345
1426
|
return;
|
|
1346
1427
|
}
|
|
1428
|
+
if (url.pathname === "/api/session" && request.method === "GET") {
|
|
1429
|
+
json(response, 200, { connected: true });
|
|
1430
|
+
return;
|
|
1431
|
+
}
|
|
1347
1432
|
if (url.pathname === "/api/events" && request.method === "GET") {
|
|
1348
1433
|
response.writeHead(200, { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", Connection: "keep-alive" });
|
|
1349
1434
|
response.write(`data: ${JSON.stringify(state())}
|
|
@@ -1393,7 +1478,7 @@ ${token}`;
|
|
|
1393
1478
|
}
|
|
1394
1479
|
if (url.pathname === "/") {
|
|
1395
1480
|
response.writeHead(200, { "Content-Type": "text/html; charset=utf-8", "Cache-Control": "no-store", "Content-Security-Policy": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https: http:; font-src 'self'; connect-src 'self'; media-src 'self' blob:; object-src 'none'; base-uri 'none'; frame-ancestors 'self' vscode-webview:; form-action 'self'" });
|
|
1396
|
-
response.end(`<!doctype html><html lang="zh-CN" data-theme="${options.theme ?? "auto"}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="workflow-project" content="${key}"><title>Workflow</title><link rel="stylesheet" href="/assets/codicons/codicon.css"><link rel="stylesheet" href="/assets/
|
|
1481
|
+
response.end(`<!doctype html><html lang="zh-CN" data-theme="${options.theme ?? "auto"}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="workflow-project" content="${key}"><title>Workflow</title><link rel="stylesheet" href="/assets/codicons/codicon.css"><link rel="stylesheet" href="/assets/workflow-ui.css"><link rel="stylesheet" href="/assets/theme.css"></head><body data-surface="editor"><div id="app"></div><script src="/assets/workflow-ui.js" defer></script></body></html>`);
|
|
1397
1482
|
return;
|
|
1398
1483
|
}
|
|
1399
1484
|
const assets = { "/assets/workflow-ui.js": "application/javascript", "/assets/workflow-ui.css": "text/css", "/assets/theme.css": "text/css", "/assets/codicons/codicon.css": "text/css", "/assets/codicons/codicon.ttf": "font/ttf" };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@workflow-code/cli",
|
|
3
|
-
"version": "0.2.4-
|
|
3
|
+
"version": "0.2.4-20261010002.codex-cli-web",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"workflow-code": "./dist/index.js"
|
|
@@ -37,14 +37,14 @@
|
|
|
37
37
|
"workflow-code": "0.2.4-20260929001",
|
|
38
38
|
"@workflow-code/auth-config": "0.1.0",
|
|
39
39
|
"@workflow-code/execution-targets": "0.1.0",
|
|
40
|
+
"@workflow-code/i18n": "0.1.0",
|
|
40
41
|
"@workflow-code/openai-responses": "0.1.0",
|
|
41
42
|
"@workflow-code/project-dependencies": "0.1.0",
|
|
42
|
-
"@workflow-code/i18n": "0.1.0",
|
|
43
43
|
"@workflow-code/project-info": "0.1.0",
|
|
44
44
|
"@workflow-code/project-upload": "0.1.0",
|
|
45
|
+
"@workflow-code/runtime-compatibility": "0.1.0",
|
|
45
46
|
"@workflow-code/workflow-ids": "0.1.0",
|
|
46
|
-
"@workflow-code/workflow-mcp": "0.1.0"
|
|
47
|
-
"@workflow-code/runtime-compatibility": "0.1.0"
|
|
47
|
+
"@workflow-code/workflow-mcp": "0.1.0"
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
50
|
"bootstrap": "node scripts/bootstrap.mjs",
|