@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 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-U23BFLG4.js");
27
+ const webCli = await import("./web-KTQSEVQT.js");
28
28
  return webCli.main(argv.slice(1));
29
29
  }
30
30
  if (argv[0] === "workspace") {
@@ -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; } }
@@ -1335,7 +1335,13 @@ main {
1335
1335
  }
1336
1336
  .composer-area {
1337
1337
  flex-shrink: 0;
1338
- padding: 8px max(28px, calc((100% - 820px) / 2)) 14px;
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
+ }
@@ -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 token = new URLSearchParams(location.hash.slice(1)).get("session");
3937
- if (token) {
3938
- history.replaceState(null, "", `${location.pathname}${location.search}`);
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
- source = new EventSource("/api/events");
3949
- source.onmessage = (event) => {
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 connected;
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 account;
4028
- const updateAccount = (state) => {
4029
- account = state.account;
4030
- let button = document.querySelector("#web-account");
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
- document.querySelector("#web-account")?.click();
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
- showError(message.message);
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
- authRevision++;
1021
- parameterStates.clear();
1022
- for (const session of controller.sessions) {
1023
- controller.stop(session);
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: authRevision };
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/theme.css"><link rel="stylesheet" href="/assets/workflow-ui.css"></head><body data-surface="editor"><div id="app"></div><script src="/assets/workflow-ui.js" defer></script></body></html>`);
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-20261010001.codex-cli-web",
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",