@songsid/agend 2.3.5-alpha.2 → 2.3.5-alpha.4
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/agend-home.d.ts +13 -0
- package/dist/agend-home.js +42 -0
- package/dist/agend-home.js.map +1 -1
- package/dist/agent-runtime-api.d.ts +4 -0
- package/dist/agent-runtime-api.js +59 -0
- package/dist/agent-runtime-api.js.map +1 -0
- package/dist/agent-runtime-change.d.ts +74 -0
- package/dist/agent-runtime-change.js +102 -0
- package/dist/agent-runtime-change.js.map +1 -0
- package/dist/auth-api.d.ts +21 -0
- package/dist/auth-api.js +95 -4
- package/dist/auth-api.js.map +1 -1
- package/dist/authority/web-authority.d.ts +12 -2
- package/dist/authority/web-authority.js +25 -4
- package/dist/authority/web-authority.js.map +1 -1
- package/dist/backend/claude-code.js +16 -1
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/codex-footer-hints.d.ts +4 -0
- package/dist/backend/codex-footer-hints.js +27 -0
- package/dist/backend/codex-footer-hints.js.map +1 -0
- package/dist/backend/codex.js +19 -11
- package/dist/backend/codex.js.map +1 -1
- package/dist/backend/kiro-identity.d.ts +2 -0
- package/dist/backend/kiro-identity.js +13 -6
- package/dist/backend/kiro-identity.js.map +1 -1
- package/dist/backend/kiro.js +18 -3
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend-availability.d.ts +6 -0
- package/dist/backend-availability.js +13 -5
- package/dist/backend-availability.js.map +1 -1
- package/dist/channel/mcp-tools.js +1 -1
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/cli.js +6 -9
- package/dist/cli.js.map +1 -1
- package/dist/config-validator.js +12 -1
- package/dist/config-validator.js.map +1 -1
- package/dist/daemon.d.ts +5 -0
- package/dist/daemon.js +107 -30
- package/dist/daemon.js.map +1 -1
- package/dist/fleet-manager.d.ts +146 -8
- package/dist/fleet-manager.js +1030 -166
- package/dist/fleet-manager.js.map +1 -1
- package/dist/fleet-windows-runtime.d.ts +6 -1
- package/dist/fleet-windows-runtime.js +77 -1
- package/dist/fleet-windows-runtime.js.map +1 -1
- package/dist/fleet-yaml-patch.d.ts +3 -0
- package/dist/fleet-yaml-patch.js +103 -0
- package/dist/fleet-yaml-patch.js.map +1 -0
- package/dist/general-binding.d.ts +85 -0
- package/dist/general-binding.js +298 -0
- package/dist/general-binding.js.map +1 -0
- package/dist/general-host-owner.d.ts +8 -0
- package/dist/general-host-owner.js +26 -0
- package/dist/general-host-owner.js.map +1 -0
- package/dist/general-instance.d.ts +7 -0
- package/dist/general-instance.js +20 -3
- package/dist/general-instance.js.map +1 -1
- package/dist/general-knowledge/skills/model-discovery/SKILL.md +2 -0
- package/dist/general-role.d.ts +5 -0
- package/dist/general-role.js +26 -0
- package/dist/general-role.js.map +1 -0
- package/dist/instance-lifecycle.d.ts +1 -0
- package/dist/instance-lifecycle.js +26 -4
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/instructions.d.ts +2 -0
- package/dist/instructions.js +6 -0
- package/dist/instructions.js.map +1 -1
- package/dist/kiro-resume-summary.d.ts +37 -0
- package/dist/kiro-resume-summary.js +85 -0
- package/dist/kiro-resume-summary.js.map +1 -0
- package/dist/locale.js +59 -0
- package/dist/locale.js.map +1 -1
- package/dist/login-controller.js +9 -1
- package/dist/login-controller.js.map +1 -1
- package/dist/native-pane-lifecycle.js +4 -1
- package/dist/native-pane-lifecycle.js.map +1 -1
- package/dist/native-pane-manager.d.ts +1 -0
- package/dist/native-pane-manager.js +23 -2
- package/dist/native-pane-manager.js.map +1 -1
- package/dist/needs-you-hub.js +1 -1
- package/dist/needs-you-hub.js.map +1 -1
- package/dist/needs-you.d.ts +8 -1
- package/dist/needs-you.js +5 -2
- package/dist/needs-you.js.map +1 -1
- package/dist/office-share-page.js +1 -1
- package/dist/office-share-page.js.map +1 -1
- package/dist/package-preimage.js +27 -2
- package/dist/package-preimage.js.map +1 -1
- package/dist/pane-port.d.ts +11 -0
- package/dist/public-web-gateway.js +9 -0
- package/dist/public-web-gateway.js.map +1 -1
- package/dist/public-web-link.d.ts +11 -0
- package/dist/public-web-link.js +13 -0
- package/dist/public-web-link.js.map +1 -1
- package/dist/quickstart-api.js +9 -5
- package/dist/quickstart-api.js.map +1 -1
- package/dist/quickstart.d.ts +1 -0
- package/dist/quickstart.js +4 -1
- package/dist/quickstart.js.map +1 -1
- package/dist/reply-buttons-controller.js +10 -0
- package/dist/reply-buttons-controller.js.map +1 -1
- package/dist/runtime-api.js +84 -9
- package/dist/runtime-api.js.map +1 -1
- package/dist/runtime-build-info.d.ts +9 -0
- package/dist/runtime-build-info.js +57 -0
- package/dist/runtime-build-info.js.map +1 -0
- package/dist/runtime-bundle.d.ts +3 -1
- package/dist/runtime-bundle.js +17 -8
- package/dist/runtime-bundle.js.map +1 -1
- package/dist/runtime-cli.d.ts +6 -1
- package/dist/runtime-cli.js +46 -11
- package/dist/runtime-cli.js.map +1 -1
- package/dist/runtime-client-store.d.ts +20 -1
- package/dist/runtime-client-store.js +56 -4
- package/dist/runtime-client-store.js.map +1 -1
- package/dist/runtime-client.d.ts +12 -0
- package/dist/runtime-client.js +43 -7
- package/dist/runtime-client.js.map +1 -1
- package/dist/runtime-connection.d.ts +22 -0
- package/dist/runtime-connection.js +93 -0
- package/dist/runtime-connection.js.map +1 -0
- package/dist/runtime-coordinator.d.ts +9 -0
- package/dist/runtime-coordinator.js +81 -34
- package/dist/runtime-coordinator.js.map +1 -1
- package/dist/runtime-general-protocol.d.ts +22 -0
- package/dist/runtime-general-protocol.js +57 -0
- package/dist/runtime-general-protocol.js.map +1 -0
- package/dist/runtime-general-workspace.d.ts +17 -0
- package/dist/runtime-general-workspace.js +213 -0
- package/dist/runtime-general-workspace.js.map +1 -0
- package/dist/runtime-pane-assets.d.ts +1 -1
- package/dist/runtime-pane-assets.js +1 -1
- package/dist/runtime-pane-assets.js.map +1 -1
- package/dist/runtime-pane-boot.d.ts +7 -0
- package/dist/runtime-pane-boot.js +23 -0
- package/dist/runtime-pane-boot.js.map +1 -1
- package/dist/runtime-pane-host.d.ts +1 -0
- package/dist/runtime-pane-host.js +22 -1
- package/dist/runtime-pane-host.js.map +1 -1
- package/dist/runtime-pane-mcp.d.ts +8 -0
- package/dist/runtime-pane-mcp.js +15 -2
- package/dist/runtime-pane-mcp.js.map +1 -1
- package/dist/runtime-pane-port.d.ts +6 -1
- package/dist/runtime-pane-port.js +21 -0
- package/dist/runtime-pane-port.js.map +1 -1
- package/dist/runtime-pane-protocol.d.ts +4 -1
- package/dist/runtime-pane-protocol.js +7 -1
- package/dist/runtime-pane-protocol.js.map +1 -1
- package/dist/runtime-protocol.d.ts +13 -0
- package/dist/runtime-protocol.js +18 -0
- package/dist/runtime-protocol.js.map +1 -1
- package/dist/settings-api.d.ts +5 -0
- package/dist/settings-api.js +37 -1
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-baseline.d.ts +1 -0
- package/dist/settings-baseline.js +1 -1
- package/dist/settings-baseline.js.map +1 -1
- package/dist/settings-confirmation.d.ts +1 -0
- package/dist/settings-confirmation.js +1 -0
- package/dist/settings-confirmation.js.map +1 -1
- package/dist/settings-effect.d.ts +4 -0
- package/dist/settings-effect.js +36 -1
- package/dist/settings-effect.js.map +1 -1
- package/dist/settings-http-confirmation.d.ts +4 -0
- package/dist/settings-http-confirmation.js +20 -1
- package/dist/settings-http-confirmation.js.map +1 -1
- package/dist/settings-request-capability.d.ts +2 -0
- package/dist/settings-request-capability.js +4 -0
- package/dist/settings-request-capability.js.map +1 -1
- package/dist/settings-route-lifetime.d.ts +14 -0
- package/dist/settings-route-lifetime.js +48 -0
- package/dist/settings-route-lifetime.js.map +1 -0
- package/dist/settings-transaction.d.ts +4 -0
- package/dist/settings-transaction.js +9 -0
- package/dist/settings-transaction.js.map +1 -1
- package/dist/setup-form.js +6 -3
- package/dist/setup-form.js.map +1 -1
- package/dist/setup-i18n.js +3 -0
- package/dist/setup-i18n.js.map +1 -1
- package/dist/stall-reporter.d.ts +21 -0
- package/dist/stall-reporter.js +82 -0
- package/dist/stall-reporter.js.map +1 -0
- package/dist/tmux-control.d.ts +2 -2
- package/dist/tmux-control.js +4 -2
- package/dist/tmux-control.js.map +1 -1
- package/dist/tmux-exec-helper.d.ts +6 -3
- package/dist/tmux-exec-helper.js +115 -53
- package/dist/tmux-exec-helper.js.map +1 -1
- package/dist/tool-permissions.js +2 -1
- package/dist/tool-permissions.js.map +1 -1
- package/dist/tunnel/cloudflared-install.d.ts +4 -0
- package/dist/tunnel/cloudflared-install.js +20 -1
- package/dist/tunnel/cloudflared-install.js.map +1 -1
- package/dist/tunnel/cloudflared.d.ts +8 -1
- package/dist/tunnel/cloudflared.js +34 -6
- package/dist/tunnel/cloudflared.js.map +1 -1
- package/dist/tunnel/lease.d.ts +3 -3
- package/dist/tunnel/lease.js +23 -19
- package/dist/tunnel/lease.js.map +1 -1
- package/dist/tunnel/manager.js +6 -7
- package/dist/tunnel/manager.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/ui/agent-proposals.js +2 -2
- package/dist/ui/agent-runtime-settings.js +67 -0
- package/dist/ui/agent-runtime.js +20 -2
- package/dist/ui/backend-signin.js +36 -21
- package/dist/ui/general-location.js +71 -0
- package/dist/ui/native-instance-settings.js +5 -8
- package/dist/ui/native-pane-recovery.js +1 -1
- package/dist/ui/panel-chat.js +5 -2
- package/dist/ui/panel-fleet.js +4 -2
- package/dist/ui/panel-needs.js +2 -0
- package/dist/ui/panel-runtimes.js +136 -33
- package/dist/ui/panel-settings.js +227 -138
- package/dist/ui/panel-windows-cli.js +77 -41
- package/dist/ui/settings-agent-filters.js +94 -0
- package/dist/ui/settings-appearance.js +122 -23
- package/dist/ui/settings-dialogs.js +13 -7
- package/dist/ui/settings-guests.js +25 -11
- package/dist/ui/settings-layout.js +245 -0
- package/dist/ui/settings-model.js +10 -1
- package/dist/ui/settings-outside.js +176 -0
- package/dist/ui/settings-registry.js +6 -3
- package/dist/ui/settings-shortcuts.js +65 -0
- package/dist/ui/settings-strings.js +95 -0
- package/dist/ui/settings-web-authority.js +14 -11
- package/dist/ui/shared/app-i18n.js +2 -2
- package/dist/ui/shared/app-shell.js +17 -7
- package/dist/ui/shared/app.css +1038 -13
- package/dist/ui/shared/instance-nav.js +16 -5
- package/dist/ui/shared/machine-name.js +13 -0
- package/dist/ui/shared/panel-view.js +266 -17
- package/dist/ui/shared/qrcode.module.js +2237 -0
- package/dist/ui/shared/settings-width.js +31 -0
- package/dist/ui/shared/signin.js +39 -5
- package/dist/ui/shared/tokens.css +1 -0
- package/dist/ui/shared/ui-dialog.js +2 -2
- package/dist/ui/shared/ui-menu.js +3 -3
- package/dist/ui/shared/vendor/qrcode-generator.LICENSE +27 -0
- package/dist/ui/shared/vendor/vendor.json +19 -2
- package/dist/ui/shared/view-strings.js +28 -0
- package/dist/ui/windows-cli-catalog.js +41 -12
- package/dist/unattended-install.d.ts +1 -1
- package/dist/unattended-install.js +19 -5
- package/dist/unattended-install.js.map +1 -1
- package/dist/update-check.d.ts +2 -8
- package/dist/update-check.js +2 -5
- package/dist/update-check.js.map +1 -1
- package/dist/view-api.d.ts +15 -0
- package/dist/view-api.js +94 -33
- package/dist/view-api.js.map +1 -1
- package/dist/web-api.d.ts +13 -0
- package/dist/web-api.js +80 -0
- package/dist/web-api.js.map +1 -1
- package/dist/web-login.d.ts +4 -3
- package/dist/web-login.js +23 -14
- package/dist/web-login.js.map +1 -1
- package/dist/web-only-fleet.d.ts +20 -0
- package/dist/web-only-fleet.js +21 -0
- package/dist/web-only-fleet.js.map +1 -1
- package/dist/web-remote-access.d.ts +152 -0
- package/dist/web-remote-access.js +368 -0
- package/dist/web-remote-access.js.map +1 -0
- package/dist/web-session.d.ts +12 -0
- package/dist/web-session.js +22 -8
- package/dist/web-session.js.map +1 -1
- package/dist/windows-agents.d.ts +15 -1
- package/dist/windows-agents.js +37 -10
- package/dist/windows-agents.js.map +1 -1
- package/dist/windows-cli-api.js +2 -1
- package/dist/windows-cli-api.js.map +1 -1
- package/dist/windows-cli-worker.d.ts +1 -0
- package/dist/windows-cli-worker.js +2 -2
- package/dist/windows-cli-worker.js.map +1 -1
- package/dist/windows-cli.d.ts +1 -0
- package/dist/windows-cli.js +7 -2
- package/dist/windows-cli.js.map +1 -1
- package/launcher/agend-install.cjs +16 -2
- package/package.json +1 -1
package/dist/tunnel/manager.js
CHANGED
|
@@ -44,8 +44,7 @@ export class ManagedTunnel {
|
|
|
44
44
|
async reap() {
|
|
45
45
|
const outcome = await reapStaleTunnel(this.opts.dataDir, this.opts);
|
|
46
46
|
if (outcome.kind === "manual") {
|
|
47
|
-
this.
|
|
48
|
-
this.log(this.blocked);
|
|
47
|
+
this.log(manualCleanupMessage(outcome));
|
|
49
48
|
}
|
|
50
49
|
return outcome;
|
|
51
50
|
}
|
|
@@ -82,7 +81,7 @@ export class ManagedTunnel {
|
|
|
82
81
|
// Reserved before the spawn, not after: between these two writes a child
|
|
83
82
|
// may come into existence, and a crash in that window must leave evidence
|
|
84
83
|
// rather than silence.
|
|
85
|
-
writeLease(this.opts.dataDir, {
|
|
84
|
+
await writeLease(this.opts.dataDir, {
|
|
86
85
|
sid: ctx.sid,
|
|
87
86
|
provider: provider.name,
|
|
88
87
|
originPort: Number(ctx.origin.port),
|
|
@@ -105,7 +104,7 @@ export class ManagedTunnel {
|
|
|
105
104
|
// Best effort, and never allowed to throw past the block below: a full disk here must not turn
|
|
106
105
|
// "a child we cannot prove dead" into a rejected start with no block and no announcement.
|
|
107
106
|
try {
|
|
108
|
-
writeLease(this.opts.dataDir, {
|
|
107
|
+
await writeLease(this.opts.dataDir, {
|
|
109
108
|
sid: ctx.sid,
|
|
110
109
|
provider: provider.name,
|
|
111
110
|
originPort: Number(ctx.origin.port),
|
|
@@ -132,7 +131,7 @@ export class ManagedTunnel {
|
|
|
132
131
|
}
|
|
133
132
|
// The provider proved its child is gone (or never made one), so the lease
|
|
134
133
|
// describes nothing and may go.
|
|
135
|
-
clearLease(this.opts.dataDir);
|
|
134
|
+
await clearLease(this.opts.dataDir);
|
|
136
135
|
return {
|
|
137
136
|
ok: false,
|
|
138
137
|
errorKind: startError?.errorKind ?? "spawn-failed",
|
|
@@ -145,7 +144,7 @@ export class ManagedTunnel {
|
|
|
145
144
|
// tunnel is stopped on proof — and if that cannot be proven the failure says so and blocks the next start.
|
|
146
145
|
this.active = handle;
|
|
147
146
|
try {
|
|
148
|
-
writeLease(this.opts.dataDir, {
|
|
147
|
+
await writeLease(this.opts.dataDir, {
|
|
149
148
|
sid: ctx.sid,
|
|
150
149
|
provider: provider.name,
|
|
151
150
|
originPort: Number(ctx.origin.port),
|
|
@@ -186,7 +185,7 @@ export class ManagedTunnel {
|
|
|
186
185
|
const result = await handle.stop(reason);
|
|
187
186
|
this.active = null;
|
|
188
187
|
if (result.confirmed) {
|
|
189
|
-
clearLease(this.opts.dataDir);
|
|
188
|
+
await clearLease(this.opts.dataDir);
|
|
190
189
|
return result;
|
|
191
190
|
}
|
|
192
191
|
this.unconfirmed = { reason: result.reason, pid: result.pid, identity: result.identity };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/tunnel/manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,eAAe,EAAE,UAAU,EAAsC,MAAM,YAAY,CAAC;AAC/H,OAAO,EACL,gBAAgB,GAKjB,MAAM,YAAY,CAAC;AAWpB;;;;;;GAMG;AACH,MAAM,OAAO,aAAa;IAYK,IAAI;IAXzB,QAAQ,GAAuC,IAAI,CAAC;IACpD,MAAM,GAAwB,IAAI,CAAC;IAC3C,qFAAqF;IAC7E,OAAO,GAAkB,IAAI,CAAC;IACtC;;;;OAIG;IACK,WAAW,GAA2E,IAAI,CAAC;IAEnG,YAA6B,IAA0B;oBAA1B,IAAI;IAAyB,CAAC;IAEnD,GAAG,CAAC,OAAe,IAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAE9E,8EAA8E;IACtE,aAAa;QACnB,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,YAAY,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC7D,OAAO,KAAK,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7D,CAAC;IAED,IAAI,MAAM,KAA0B,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAEzD,gFAAgF;IAChF,KAAK,CAAC,IAAI;QACR,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACpE,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC9B,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/tunnel/manager.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -102,6 +102,8 @@ export interface InstanceConfig {
|
|
|
102
102
|
native_agent_id?: string;
|
|
103
103
|
/** Persistent terminal opt-in. Unset/exec preserves the per-turn adapter and rollback. */
|
|
104
104
|
native_adapter?: "exec" | "pane";
|
|
105
|
+
/** Internal administrator-created General binding; runner computes its dedicated workspace. */
|
|
106
|
+
native_general_binding?: string;
|
|
105
107
|
/** Native CLI permission selection; independent of Linux terminal permissions. */
|
|
106
108
|
native_sandbox?: "read-only" | "workspace-write";
|
|
107
109
|
/** Proactive interrupted-work continuation; defaults true, inherited from fleet defaults. */
|
|
@@ -49,11 +49,11 @@ export function refreshProposals() {
|
|
|
49
49
|
* Once per page: read once, then again on the stream's `agent_proposals` ping (live) and whenever a status frame shows a
|
|
50
50
|
* revision the list was not read at yet (#2997 review: a page that polls /ui/poll, one that reconnected, and a tab from
|
|
51
51
|
* before the upgrade — whose stream knows no `agent_proposals` event — all still apply status frames; r2: until a read
|
|
52
|
-
* at that revision succeeds).
|
|
52
|
+
* at that revision succeeds). #3008: on the public link too — this computer's admin signed in from outside decides there;
|
|
53
|
+
* anyone else over it reads an empty list (the server answers the owner only).
|
|
53
54
|
*/
|
|
54
55
|
export function installProposals() {
|
|
55
56
|
if (installed) return;
|
|
56
|
-
if (typeof document !== "undefined" && document.body && document.body.dataset.publicLink === "1") return;
|
|
57
57
|
installed = true;
|
|
58
58
|
const on = appStore.get().streamOn;
|
|
59
59
|
if (typeof on === "function") {
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import {html,useEffect,useRef,useState} from "/assets/app-html.js";
|
|
2
|
+
import {useLease} from "/assets/app-ctx.js";
|
|
3
|
+
import {appStore,useStore} from "/assets/app-store.js";
|
|
4
|
+
import {register,t} from "/assets/app-i18n.js";
|
|
5
|
+
import {sectionPath} from "/assets/app-route.js";
|
|
6
|
+
import {Dialog} from "/assets/ui-dialog.js";
|
|
7
|
+
import {CliPicker,CliProductMark,cliLabel} from "./cli-picker.js";
|
|
8
|
+
import {RuntimePicker,tr} from "./agent-runtime.js";
|
|
9
|
+
import {useRuntimeCatalog,runtimeCliChoices} from "./runtime-catalog.js";
|
|
10
|
+
import {useWindowsCliCatalog} from "./windows-cli-catalog.js";
|
|
11
|
+
import {useBackendRows} from "./settings-catalog.js";
|
|
12
|
+
import {ModelSelect} from "./settings-dialogs.js";
|
|
13
|
+
import {machineLabel} from "./settings-agent-filters.js";
|
|
14
|
+
import {removedBackendReplacement} from "./settings-model.js";
|
|
15
|
+
register("runtimeChange",{
|
|
16
|
+
"zh-TW":{removed:"{0} 已從 AgEnD 移除,無法繼續使用。請改選其他 CLI,建議使用 {1}。",title:"切換 Runtime 與 CLI",change:"切換 Runtime/CLI",current:"執行環境與 CLI",lead:"保留同一個 Agent 與 AgEnD 聊天歷史,在選擇的電腦開始新的 CLI 對話。",configure:"設定新環境",next:"下一步",back:"上一步",cancel:"取消",apply:"儲存切換",loading:"正在讀取 Agent 與 Runtime 狀態…",busy:"正在準備新工作區並關閉舊 CLI…",fresh:"我了解切換後會開始新的 CLI 對話",freshHint:"原有 CLI 對話與檔案會留在原電腦;不會自動複製到新環境。",stopped:"切換後 Agent 會暫停,請從 Agent 列表啟動。",directoryHint:"更換電腦時請填目標電腦的路徑;留空會建立獨立工作區。",dirty:"請先儲存或取消其他編輯,再切換 Runtime。",unknown:"請重新讀取 Agent 狀態再操作,避免重複切換。",reload:"重新讀取狀態",failed:"無法切換 Runtime",noCli:"這台電腦尚無可用的 CLI。請先到 Runtime 偵測或更新清單。",generalMove:"General 的位置不在這裡切換。請到「Agent 預設值 → General 位置」,那裡會暫停投遞並確認舊程式已結束。",generalGo:"前往 General 位置"},
|
|
17
|
+
en:{removed:"{0} was removed from AgEnD and can no longer run. Choose another CLI; {1} is its replacement.",title:"Change runtime and CLI",change:"Change runtime / CLI",current:"Runtime and CLI",lead:"Keep this Agent and its AgEnD chat history. Start a fresh CLI conversation on the selected computer.",configure:"Configure the new environment",next:"Next",back:"Back",cancel:"Cancel",apply:"Save runtime change",loading:"Loading Agent and runtime state…",busy:"Preparing the new workspace and closing the old CLI…",fresh:"I understand this starts a new CLI conversation",freshHint:"Existing CLI conversations and files stay on the original computer. They are not copied to the new environment.",stopped:"The Agent will be paused after switching. Start it from the Agent list.",directoryHint:"Use a path on the target computer. Leave empty for a separate managed workspace.",dirty:"Save or cancel other edits before changing runtime.",unknown:"Read the Agent state again before another operation to avoid switching twice.",reload:"Read state again",failed:"Could not change runtime",noCli:"No runnable CLI is available on this computer. Detect or refresh its inventory in Runtime settings.",generalMove:"General is not moved from here. Use Agent defaults → General location, which holds deliveries and confirms the old process has exited.",generalGo:"Go to General location"}
|
|
18
|
+
});
|
|
19
|
+
const text=(key,...values)=>t(`runtimeChange.${key}`,...values);
|
|
20
|
+
export function AgentRuntimeSettings({name,inst,disabled=false,inherited=null,onChanged}){
|
|
21
|
+
const [open,setOpen]=useState(false),general=inst.general_topic===true||name==="general";
|
|
22
|
+
// The computer's name as the Runtime page shows it; a computer that is revoked or gone says so (with the start of its id), never the whole id.
|
|
23
|
+
const where=inst.runtime==="windows"?(inst.machine_id?machineLabel(inst.machine_id,inst.machine_name,inst.machine_status,inst.machine_missing!==true):null):null;
|
|
24
|
+
const place=inst.runtime==="windows"?(where?where.label:tr("windows")):tr("wsl");
|
|
25
|
+
return html`<section class="agent-runtime-settings"><div><span class="note">${text("current")}</span><p><${CliProductMark} id=${inst.backend} size=${20}/><strong class="agent-runtime-place" title=${where?.title||place}>${place}</strong><span class=${removedBackendReplacement(inst.backend)?"agent-runtime-removed":undefined}>· ${removedBackendReplacement(inst.backend)?t("settings.backendUnavailable",inst.backend):cliLabel(inst.backend)}</span></p>${inherited}</div>${general?html`<p class="note agent-runtime-general">${text("generalMove")} <a href=${`${sectionPath("agent-defaults")}#general-location`}>${text("generalGo")}</a></p>`:html`<button type="button" class="btn btn-sm" disabled=${disabled} title=${disabled?text("dirty"):undefined} onClick=${()=>setOpen(true)}>${text("change")}</button>`}${open&&!general?html`<${AgentRuntimeChangeDialog} name=${name} inst=${inst} onClose=${()=>setOpen(false)} onChanged=${onChanged}/>`:null}</section>`;
|
|
26
|
+
}
|
|
27
|
+
export function AgentRuntimeChangeDialog({name,inst,onClose,onChanged}){
|
|
28
|
+
const lease=useLease(`runtime-change:${name}`),claim=useRef(false);
|
|
29
|
+
const {windowsCli}=useStore(appStore),machinesState=useRuntimeCatalog(),rows=useBackendRows();
|
|
30
|
+
const [source,setSource]=useState(null),[step,setStep]=useState(1),[error,setError]=useState(""),[busy,setBusy]=useState(false),[uncertain,setUncertain]=useState(false);
|
|
31
|
+
const [f,setForm]=useState({runtime:inst.runtime==="windows"?"windows":"wsl",machineId:inst.machine_id||"",backend:inst.backend||"",directory:inst.working_directory||"",model:inst.model||"",access:inst.native_sandbox||"read-only",adapter:inst.native_adapter||"exec",fresh:false});
|
|
32
|
+
const local=useWindowsCliCatalog(windowsCli===true&&f.runtime==="windows"&&!f.machineId),machines=machinesState.data?.machines||[],machine=machines.find(row=>row.id===f.machineId);
|
|
33
|
+
const windows=f.runtime==="windows",inventory=f.machineId?machine?.inventory:local.data;
|
|
34
|
+
const native=windows?(f.machineId?runtimeCliChoices(machine):inventory?.clis||[]).filter(cli=>cli.path&&cli.supports?.execution!==false&&cli.supports?.resume!==false):[];
|
|
35
|
+
const choices=windows?native.map(cli=>({value:cli.id,label:cliLabel(cli.id),disabled:!cli.runnable})): (rows||[]).filter(row=>row.installed!==false).map(row=>({value:row.name,label:cliLabel(row.name),disabled:["not_installed","not_signed_in","too_old"].includes(source?.runtime==="wsl"&&source.backend===row.name?(source.backendState||row.state):row.state)}));
|
|
36
|
+
const selected=native.find(cli=>cli.id===f.backend),targetReady=!!choices.find(choice=>choice.value===f.backend&&!choice.disabled)&&(!f.machineId||machine?.status==="online");
|
|
37
|
+
const allowedRead=!windows||selected?.readOnlySupport!==false;
|
|
38
|
+
const set=(key,value)=>setForm(old=>({...old,[key]:value}));
|
|
39
|
+
const load=async()=>{
|
|
40
|
+
if(claim.current)return;setSource(null);setError("");
|
|
41
|
+
try{const response=await lease.fetch(`/ui/instances/${encodeURIComponent(name)}/runtime`),value=await response.json();if(!lease.current())return;if(!response.ok)throw Error(value.error||text("failed"));
|
|
42
|
+
const {sandbox:access}=value;setSource(value);setUncertain(false);setStep(1);setForm({runtime:value.runtime,machineId:value.machineId||"",backend:value.backend||inst.backend||"",directory:value.directory||"",model:value.model||"",access,adapter:value.adapter,fresh:false});
|
|
43
|
+
}catch(e){if(lease.current())setError(e.message);}
|
|
44
|
+
};
|
|
45
|
+
useEffect(()=>{void load();},[lease]);
|
|
46
|
+
useEffect(()=>{void machinesState.refresh();},[]);
|
|
47
|
+
const choose=(runtime,machineId="")=>setForm(old=>({...old,runtime,machineId,backend:"",model:"",directory:runtime===source?.runtime&&machineId===(source?.machineId||"")?source.directory:"",access:"read-only",adapter:"exec",fresh:false}));
|
|
48
|
+
useEffect(()=>{if(!f.backend&&choices.some(choice=>!choice.disabled))set("backend",choices.find(choice=>!choice.disabled).value);},[f.runtime,f.machineId,f.backend,choices.map(choice=>`${choice.value}:${choice.disabled}`).join(",")]);
|
|
49
|
+
const submit=async()=>{
|
|
50
|
+
if(claim.current||uncertain||!source||!f.fresh||!targetReady||(!allowedRead&&f.access==="read-only"))return;
|
|
51
|
+
claim.current=true;setBusy(true);setError("");
|
|
52
|
+
const body={requestId:crypto.randomUUID(),expectedRevision:source.revision,runtime:f.runtime,...(f.machineId?{machineId:f.machineId}:{}),backend:f.backend,model:f.model.trim(),directory:f.directory.trim(),sandbox:f.access,adapter:windows?f.adapter:"exec",startNewConversation:true};
|
|
53
|
+
try{const response=await lease.fetch(`/ui/instances/${encodeURIComponent(name)}/runtime`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(body)}),value=await response.json();if(!lease.current())return;
|
|
54
|
+
if(!response.ok||value?.ok!==true)throw Error(value.error||text("failed"));onChanged?.();onClose();
|
|
55
|
+
}catch(e){if(lease.current()){setError(e.message);setUncertain(true);}}
|
|
56
|
+
finally{claim.current=false;if(lease.current())setBusy(false);}
|
|
57
|
+
};
|
|
58
|
+
const close=()=>{if(!claim.current)onClose();};
|
|
59
|
+
return html`<${Dialog} title=${text("title")} cls="agent-runtime-change-dialog" busy=${busy} onClose=${close} wide=${true} actions=${html`<button type="button" class="btn" disabled=${busy} onClick=${close}>${text("cancel")}</button>${step===2?html`<button type="button" class="btn" disabled=${busy||uncertain} onClick=${()=>setStep(1)}>${text("back")}</button>`:null}<button type="button" class="btn btn-primary" disabled=${busy||uncertain||!source||!targetReady||(step===2&&(!f.fresh||!allowedRead&&f.access==="read-only"))} onClick=${step===1?()=>setStep(2):submit}>${text(step===1?"next":"apply")}</button>`}>
|
|
60
|
+
<div class="form agent-runtime-change" aria-busy=${busy}><p>${text("lead")}</p>${!source?html`<p role="status">${text("loading")}</p>`:null}
|
|
61
|
+
${source&&step===1?html`<${RuntimePicker} value=${f.runtime} onChange=${runtime=>choose(runtime)} windowsEnabled=${windowsCli===true} machineId=${f.machineId} machines=${machines} onMachineChange=${id=>choose("windows",id)} onRefresh=${machinesState.refresh} loaded=${!!machinesState.data && !machinesState.error}/><div class="field"><label>${tr("backend")}</label><${CliPicker} value=${f.backend} choices=${choices} label=${tr("backend")} onChange=${backend=>setForm(old=>({...old,backend,model:"",fresh:false}))}/></div>${removedBackendReplacement(f.backend)?html`<p class="note" role="status">${text("removed",f.backend,removedBackendReplacement(f.backend))}</p>`:null}${!choices.length?html`<p class="note">${text("noCli")}</p>`:null}`:null}
|
|
62
|
+
${source&&step===2?html`<h3>${text("configure")}</h3><p><strong>${machine?.name||(windows?tr("windows"):tr("wsl"))}</strong> · ${cliLabel(f.backend)}</p><label class="field"><span>${tr("directory")}</span><input value=${f.directory} disabled=${busy||uncertain} onInput=${event=>set("directory",event.target.value)}/></label><p class="note">${text("directoryHint")}</p><div class="field"><label for="runtime-change-model">${t("settings.model")}</label><${ModelSelect} id="runtime-change-model" disabled=${busy||uncertain} value=${f.model} onChange=${value=>set("model",value)} backend=${f.backend} instance=${!windows&&source.runtime==="wsl"&&source.backend===f.backend?name:undefined} runtime=${f.runtime} machineId=${f.machineId} inherit=${t("settings.modelCliDefault")}/></div>
|
|
63
|
+
${windows?html`<label class="field"><span>${tr("access")}</span><select value=${f.access} disabled=${busy||uncertain} onChange=${event=>set("access",event.target.value)}><option value="read-only" disabled=${!allowedRead}>${tr("read")}</option><option value="workspace-write">${tr("write")}</option></select></label>${!allowedRead?html`<p class="note" role="status">${tr("noReadOnly")}</p>`:null}<label class="field"><span>${tr("adapter")}</span><select value=${f.adapter} disabled=${busy||uncertain} onChange=${event=>set("adapter",event.target.value)}><option value="exec">${tr("adapterExec")}</option><option value="pane" disabled=${!["codex","claude-code"].includes(f.backend)}>${tr("adapterPane")}</option></select></label>`:null}
|
|
64
|
+
<p class="note">${text("freshHint")}</p><p class="note">${text("stopped")}</p><label class="check"><input type="checkbox" checked=${f.fresh} disabled=${busy||uncertain} onChange=${event=>set("fresh",event.target.checked)}/><span>${text("fresh")}</span></label>`:null}
|
|
65
|
+
${busy?html`<p role="status"><span class="out-spin" aria-hidden="true"></span> ${text("busy")}</p>`:null}${error?html`<p class="feedback error" role="alert">${error}</p>`:null}${uncertain?html`<p class="note">${text("unknown")}</p><button type="button" class="btn" onClick=${load}>${text("reload")}</button>`:null}</div>
|
|
66
|
+
</${Dialog}>`;
|
|
67
|
+
}
|
package/dist/ui/agent-runtime.js
CHANGED
|
@@ -2,6 +2,7 @@ import { html, useEffect, useRef, useState } from "/assets/app-html.js";
|
|
|
2
2
|
import { register, t } from "/assets/app-i18n.js";
|
|
3
3
|
import { useLease } from "/assets/app-ctx.js";
|
|
4
4
|
import { Icon } from "/assets/ui-icons.js";
|
|
5
|
+
import { runtimeCliChoices } from "./runtime-catalog.js";
|
|
5
6
|
|
|
6
7
|
register("runtime", {
|
|
7
8
|
"zh-TW": { title:"新增 Agent", choose:"1. 選擇 Runtime", wsl:"WSL / Linux", windows:"Windows 本機", wslHint:"使用 Linux CLI 與工作目錄", windowsHint:"使用 Windows 工具與本機登入", disabled:"需要先啟用 Windows CLI", configure:"2. 設定 Agent", backend:"CLI", directory:"工作目錄(選填)", nativeHint:"建立 Windows Codex Agent,保留獨立對話。Claude Code 目前僅支援偵測。", model:"使用 Windows Codex 的預設模型", access:"檔案存取", read:"唯讀", write:"工作目錄可寫入", required:"請填入 Agent 名稱,工作目錄可以留空。", created:"Windows Agent 已建立", assist:"讓 AI 幫我填寫", assistHint:"由已登入的 Windows Codex 產生草稿,填回下方後可編輯,再按「建立」。", noAi:"需要先有可用的 AI:請在設定 → Windows CLI 確認 Codex 已登入。", goal:"描述想建立的 Agent", placeholder:"例如:建立一個協助整理資料、回答問題的助手", generate:"產生設定草稿", generating:"AI 正在產生草稿…", cancel:"取消產生", draftError:"AI 沒有回傳有效的設定草稿,請調整描述後再試。", nativeBackend:"Codex — Windows", nativeClaude:"Claude Code — Windows(僅偵測)" },
|
|
@@ -25,6 +26,10 @@ register("runtime", {
|
|
|
25
26
|
en: { computers:"Connected computers", online:"Online", offline:"Offline", pending:"Awaiting approval", revoked:"Revoked", machineLoading:"Loading connected computers…", machineFailed:"Could not update connected computers. Local choices are still available.", machineRetry:"Refresh computers", noRemoteCli:"This computer has not reported a task-capable CLI.", remoteDirectory:"Working directory on {0} (optional)", remoteDraft:"AI-assisted setup is currently available for the local runtime. You can enter this remote agent's settings below.", machine:"Computer", inventoryAt:"CLI inventory updated: {0}", remotePolicy:"Files and CLI permissions belong to the selected computer. Sign-ins remain on that computer.", manage:"Connect or manage computers in Settings → Runtime", noAvailableMachine:"Connect and approve a computer in Settings → Runtime to use its CLIs." },
|
|
26
27
|
"zh-TW": { computers:"已連接的電腦", online:"在線", offline:"離線", pending:"等待核准", revoked:"已撤銷", machineLoading:"正在載入已連接的電腦…", machineFailed:"無法更新已連接的電腦,仍可使用本機選項。", machineRetry:"更新機器清單", noRemoteCli:"這台電腦尚未回報可派工的 CLI。", remoteDirectory:"{0} 上的工作目錄(選填)", remoteDraft:"AI 協助設定目前適用於本機 Runtime,你可以直接填寫下方遠端 Agent 設定。", machine:"電腦", inventoryAt:"CLI 清單更新:{0}", remotePolicy:"檔案與 CLI 權限屬於所選電腦,登入也保留在該電腦上。", manage:"前往設定 → Runtime 連接或管理電腦", noAvailableMachine:"在設定 → Runtime 連接並核准電腦後,即可使用它的 CLI。" },
|
|
27
28
|
});
|
|
29
|
+
register("runtime", {
|
|
30
|
+
en: { machineOffline:"Offline: run connect-agend.bat on that computer.", machinePending:"Awaiting approval: choose Approve computer in Settings → Runtime.", machineNoInventory:"CLIs not detected yet: choose Refresh CLI inventory for it in Settings → Runtime.", machineNoCli:"No task-capable CLI reported: install one, then choose Refresh CLI inventory in Settings → Runtime.", machineNotReady:"No CLI is signed in and ready: sign in on that computer, then choose Refresh CLI inventory in Settings → Runtime." },
|
|
31
|
+
"zh-TW": { machineOffline:"離線:請在那台電腦執行 connect-agend.bat。", machinePending:"等待核准:請到設定 → Runtime 按「核准電腦」。", machineNoInventory:"尚未偵測 CLI:到設定 → Runtime 按該電腦的「更新 CLI 清單」。", machineNoCli:"沒有可派工的 CLI:請先安裝,再到設定 → Runtime 按「更新 CLI 清單」。", machineNotReady:"CLI 尚未登入或只能偵測:請在那台電腦登入,再到設定 → Runtime 按「更新 CLI 清單」。" },
|
|
32
|
+
});
|
|
28
33
|
register("runtime", {
|
|
29
34
|
"zh-TW": {
|
|
30
35
|
agendQueries:"AgEnD 工具:唯讀模式可查詢 Agent、任務與派送狀態。",
|
|
@@ -44,9 +49,22 @@ export function NativeAgendToolsHint({ cli, sandbox }) {
|
|
|
44
49
|
return html`<p class="note native-agend-tools">${tr(!supported ? "agendUnavailable" : sandbox === "read-only" ? "agendQueries" : "agendActions")}</p>`;
|
|
45
50
|
}
|
|
46
51
|
/** #2953 story 3 (S-35): with this PC's Windows CLIs switched off, "Native Windows" is not offered at all (paired PCs still are). */
|
|
47
|
-
|
|
52
|
+
/** Why a connected computer cannot take a new agent yet ("" when it can). Offline, awaiting approval and no usable CLI are deliberate blocks; the picker says which. */
|
|
53
|
+
export function machineBlockReason(machine) {
|
|
54
|
+
if (machine.status === "pending") return "machinePending";
|
|
55
|
+
if (machine.status !== "online") return "machineOffline";
|
|
56
|
+
if (!machine.inventory) return "machineNoInventory";
|
|
57
|
+
const choices = runtimeCliChoices(machine);
|
|
58
|
+
// Same bar as the dialogs' "Next": a computer is usable once one of its task-capable CLIs is runnable.
|
|
59
|
+
return !choices.length ? "machineNoCli" : choices.some(cli => cli.runnable === true) ? "" : "machineNotReady";
|
|
60
|
+
}
|
|
61
|
+
export function RuntimePicker({ value, onChange, windowsEnabled, disabled, machineId = "", machines = [], onMachineChange, onRefresh, loaded = false, legend = tr("choose") }) {
|
|
62
|
+
const [refreshing, setRefreshing] = useState(false);
|
|
63
|
+
// Only a click on the button disables it, never the 5 s background poll (that would drop keyboard focus).
|
|
64
|
+
const refresh = async () => { setRefreshing(true); try { await onRefresh(); } finally { setRefreshing(false); } };
|
|
65
|
+
const listed = machines.filter(machine => machine.status !== "revoked");
|
|
48
66
|
return html`<fieldset class="agent-runtime-picker" disabled=${disabled}><legend>${legend}</legend><div>${(windowsEnabled ? ["wsl","windows"] : ["wsl"]).map(runtime => html`<label key=${runtime} class=${`agent-runtime-choice${!machineId && value === runtime ? " selected" : ""}`}><input type="radio" name="agent-runtime" value=${runtime} checked=${!machineId && value === runtime} disabled=${runtime === "windows" && !windowsEnabled} onChange=${() => onChange(runtime)} /><${Icon} name=${runtime === "windows" ? "view" : "code"} size=${22} /><span><strong>${tr(runtime)}</strong><small>${tr(runtime === "windows" && !windowsEnabled ? "disabled" : `${runtime}Hint`)}</small></span></label>`)}</div>
|
|
49
|
-
${
|
|
67
|
+
${listed.length || onRefresh ? html`<section class="runtime-remote-targets" aria-label=${tr("computers")}><div class="runtime-remote-heading"><h4>${tr("computers")}</h4>${onRefresh ? html`<button type="button" class="btn btn-ghost btn-sm" disabled=${refreshing} onClick=${refresh}>${tr("machineRetry")}</button>` : null}</div>${listed.map(machine => { const reason = machineBlockReason(machine), status = ["online","offline","pending"].includes(machine.status) ? machine.status : "offline"; return html`<label key=${machine.id} class=${`agent-runtime-choice${machineId === machine.id ? " selected" : ""}${reason ? " blocked" : ""}`}><input type="radio" name="agent-runtime" value=${`machine:${machine.id}`} checked=${machineId === machine.id} disabled=${!!reason} aria-describedby=${reason ? `machine-reason-${machine.id}` : undefined} onChange=${() => onMachineChange?.(machine.id)} /><${Icon} name="view" size=${22} /><span><strong>${machine.name}</strong><small><span class="runtime-machine-platform">${machine.platform || "Windows"} · </span><span class=${`runtime-machine-status is-${status}`}>${tr(status)}</span></small>${reason ? html`<small class="runtime-machine-reason" id=${`machine-reason-${machine.id}`}>${tr(reason)}</small>` : null}</span></label>`; })}${loaded && !listed.length ? html`<p class="note">${tr("noAvailableMachine")}</p>` : null}</section>` : null}
|
|
50
68
|
</fieldset>`;
|
|
51
69
|
}
|
|
52
70
|
export function parseAgentDraft(text, runtime) {
|
|
@@ -12,6 +12,7 @@ import { closeDialog, shellStore, showDialog } from "/assets/app-shell.js";
|
|
|
12
12
|
import { Dialog } from "/assets/ui-dialog.js";
|
|
13
13
|
import { toast } from "/assets/ui-toast.js";
|
|
14
14
|
import { mountStructuredSignIns } from "./structured-signin.js";
|
|
15
|
+
import { CliProductMark, cliLabel } from "./cli-picker.js";
|
|
15
16
|
|
|
16
17
|
register("signin", {
|
|
17
18
|
en: {
|
|
@@ -231,11 +232,12 @@ export function BrowserSignInDialog({ backend, backends, onClose }) {
|
|
|
231
232
|
if (!result.ok) { toast(tn("failed")); return; }
|
|
232
233
|
showDialog(SignInDialog, { backend: chosen });
|
|
233
234
|
};
|
|
234
|
-
|
|
235
|
-
<div ref=${root}></div>
|
|
235
|
+
const actions = html`<div class="signin-browser-actions">
|
|
236
236
|
${backends ? backends.filter(b => b !== "antigravity").map(b => html`<button type="button" class="btn" onClick=${() => fallback(b)}>${b} · ${tn("consoleFallback")}</button>`) : null}
|
|
237
|
-
|
|
238
|
-
<button type="button" class="btn" onClick=${close}>${tn("close")}</button></div
|
|
237
|
+
${backend ? html`<button type="button" class="btn" onClick=${() => fallback(backend)}>${tn("consoleFallback")}</button>` : null}
|
|
238
|
+
<button type="button" class="btn" onClick=${close}>${tn("close")}</button></div>`;
|
|
239
|
+
return html`<${Dialog} title=${backends ? tn("listTitle") : tn("title", cliLabel(backend))} onClose=${close} actions=${actions}>
|
|
240
|
+
<div class="signin-browser" ref=${root}></div>
|
|
239
241
|
</${Dialog}>`;
|
|
240
242
|
}
|
|
241
243
|
|
|
@@ -327,28 +329,41 @@ export function SignInLine() {
|
|
|
327
329
|
|
|
328
330
|
const STATE_KEY = { usable: "stUsable", not_signed_in: "stNotSignedIn", not_installed: "stNotInstalled", too_old: "stTooOld", unknown: "stUnknown" };
|
|
329
331
|
|
|
330
|
-
|
|
332
|
+
register("signin", {
|
|
333
|
+
en: {
|
|
334
|
+
hostTitle: "CLI sign-ins", hostHint: "Accounts used by CLIs on the host running AgEnD.",
|
|
335
|
+
hostBadge: "AgEnD host", hostProfile: "Credential profile: {0}", hostLoading: "Reading CLI sign-ins…", hostEmpty: "No CLI sign-ins were reported on this host.",
|
|
336
|
+
installHelp: "Installation instructions", updateHelp: "Update instructions", hostHelp: "Sign-in instructions",
|
|
337
|
+
commandHint: "Run these instructions in a terminal on the host running AgEnD.",
|
|
338
|
+
},
|
|
339
|
+
"zh-TW": {
|
|
340
|
+
hostTitle: "CLI 登入", hostHint: "執行 AgEnD 的主機上,各個 CLI 使用的帳號。",
|
|
341
|
+
hostBadge: "AgEnD 主機", hostProfile: "憑證設定檔:{0}", hostLoading: "正在讀取 CLI 登入狀態…", hostEmpty: "這台主機尚未回報 CLI 登入項目。",
|
|
342
|
+
installHelp: "安裝說明", updateHelp: "更新說明", hostHelp: "登入說明",
|
|
343
|
+
commandHint: "請在執行 AgEnD 的主機終端機中操作。",
|
|
344
|
+
},
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
/** Settings → Agents: existing host sign-in actions, with commands disclosed only when needed. */
|
|
331
348
|
export function SignInList() {
|
|
332
349
|
const { targets, loaded, login, structured } = useStore(signInStore);
|
|
333
|
-
//
|
|
334
|
-
// serve this read (it was a 404 in the console there, found by smoke-1939-authorize-defaults).
|
|
350
|
+
// Public-link authority remains unchanged: this page neither reads nor offers host sign-in there.
|
|
335
351
|
const offered = signInOffered();
|
|
336
352
|
useEffect(() => { if (offered) void refreshSignIn(); }, []);
|
|
337
|
-
if (!offered
|
|
353
|
+
if (!offered) return null;
|
|
338
354
|
const active = structured?.login;
|
|
339
355
|
const running = (login && login.phase === "running") || (active && active.phase !== "done" && !(active.phase === "failed" && active.reason));
|
|
340
|
-
return html`<section class="s-group signin-list" aria-label=${tn("
|
|
341
|
-
<
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
</div>`)}
|
|
356
|
+
return html`<section class="s-group signin-list s-auth-section" aria-label=${tn("hostTitle")}>
|
|
357
|
+
<header class="s-auth-section-head"><div><h3>${tn("hostTitle")}</h3><p>${tn("hostHint")}</p></div><span class="s-auth-host-badge">${tn("hostBadge")}</span></header>
|
|
358
|
+
${!loaded ? html`<p class="note" role="status">${tn("hostLoading")}</p>` : !targets.length ? html`<p class="note">${tn("hostEmpty")}</p>` : html`<div class="s-host-cli-list">${targets.map((x) => {
|
|
359
|
+
const commands = [["install", x.install], ["update", x.update], ["onHost", x.hostCommand]].filter(([, value]) => value);
|
|
360
|
+
return html`<div key=${`${x.backend}:${x.profile || ""}`} class="s-row s-host-cli-row" data-backend=${x.backend}>
|
|
361
|
+
<div class="s-host-cli-identity"><${CliProductMark} id=${x.backend} size=${24} /><div><strong class="s-name">${cliLabel(x.backend)}</strong>${x.profile ? html`<small>${tn("hostProfile", x.profile)}</small>` : null}</div></div>
|
|
362
|
+
<span class=${`tag s-auth-state is-${x.state || "unknown"}`}>${tn(STATE_KEY[x.state] || "stUnknown")}</span>
|
|
363
|
+
${x.signIn ? html`<span class="s-actions"><button type="button" class=${x.state === "not_signed_in" ? "btn btn-sm btn-primary" : "btn btn-sm"} disabled=${!!running}
|
|
364
|
+
onClick=${() => openSignIn(x)}>${x.state === "usable" ? tn("signInAgain") : tn("signIn")}</button></span>` : null}
|
|
365
|
+
${commands.length ? html`<details class="s-host-cli-help" data-inline-help=${x.signIn ? "false" : "true"}><summary>${tn(x.install ? "installHelp" : x.update ? "updateHelp" : "hostHelp")}</summary><div><p class="note">${tn("commandHint")}</p>${commands.map(([kind, command]) => html`<p class="s-host-cli-command"><code>${tn(kind, command)}</code></p>`)}</div></details>` : null}
|
|
366
|
+
</div>`;
|
|
367
|
+
})}</div>`}
|
|
353
368
|
</section>`;
|
|
354
369
|
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// The location is a separate revision-bound Settings operation, never an agent-config patch.
|
|
2
|
+
import { html, useEffect, useRef, useState } from "/assets/app-html.js";
|
|
3
|
+
import { t, register } from "/assets/app-i18n.js";
|
|
4
|
+
import { appStore, useStore, guestPage } from "/assets/app-store.js";
|
|
5
|
+
import { useLease } from "/assets/app-ctx.js";
|
|
6
|
+
import { toast } from "/assets/ui-toast.js";
|
|
7
|
+
import { confirmedWrite, newKey } from "./settings-confirm.js";
|
|
8
|
+
import { ModelSelect } from "./settings-dialogs.js";
|
|
9
|
+
register("generalLocation", {
|
|
10
|
+
en: {
|
|
11
|
+
label: "Run General on", host: "This AgEnD host", windows: "Paired Windows PC",
|
|
12
|
+
offline: "General’s Windows PC is offline. AgEnD is still running here. Reconnect that PC, or switch General back to this host in Settings. Pending or uncertain requests will not be sent again automatically.",
|
|
13
|
+
exit: "General was not switched: the previous process has not been confirmed stopped. Reconnect its PC or use the existing stopped-process recovery, then try again. No replacement was started.",
|
|
14
|
+
start: "General’s location was saved, but it could not start. Check Needs you and choose Start or switch back. Its previous conversation is kept; no input was replayed.",
|
|
15
|
+
history: "Host and Windows conversations stay separate. Switching does not copy or merge their history.",
|
|
16
|
+
},
|
|
17
|
+
"zh-TW": {
|
|
18
|
+
label: "General 執行位置", host: "這台 AgEnD 主機", windows: "已配對的 Windows 電腦",
|
|
19
|
+
offline: "General 所在的 Windows 電腦已離線。AgEnD 仍在這台主機運作。請重新連線那台電腦,或到設定把 General 切回這台主機。待處理或結果不明的請求不會自動重送。",
|
|
20
|
+
exit: "General 尚未切換:還無法確認前一個程序已停止。請重新連線那台電腦,或完成現有的程序停止復原,再重試。沒有啟動替代程序。",
|
|
21
|
+
start: "General 的執行位置已儲存,但未能啟動。請查看「等你處理」,選擇啟動或切回原位置。先前的對話已保留,沒有重送輸入。",
|
|
22
|
+
history: "主機與 Windows 各自保留對話,切換時不會複製或合併對話紀錄。",
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
const tr = key => t(`generalLocation.${key}`);
|
|
26
|
+
export function GeneralLocation({ name, attentionOnly = false }) {
|
|
27
|
+
const app = useStore(appStore);
|
|
28
|
+
const attentionKey = JSON.stringify((app.needs || []).filter(item => item.generalLocation && (!name || item.instance === name)).map(item => item.id));
|
|
29
|
+
const lease = useLease("general-location"), [data, setData] = useState(null), [error, setError] = useState(null);
|
|
30
|
+
const read = async () => {
|
|
31
|
+
try { const response = await lease.fetch("/api/settings/general-location"); const result = await response.json();
|
|
32
|
+
if (lease.current()) { if (response.ok) { setData(result); setError(null); } else setError(result.error); }
|
|
33
|
+
} catch (err) { if (lease.current()) setError(err.message); }
|
|
34
|
+
};
|
|
35
|
+
useEffect(() => { if (!guestPage()) void read(); }, [name, attentionKey]);
|
|
36
|
+
if (guestPage()) return null;
|
|
37
|
+
return html`<section id="general-location" class="card general-location"><h2>${tr("label")}</h2>
|
|
38
|
+
${error ? html`<p role="alert">${error}</p>` : null}
|
|
39
|
+
${(data?.generals || []).filter(row => !name || row.name === name).map(row => html`<${LocationRow} key=${row.name + row.revision} row=${row} machines=${data.machines || []} attentionOnly=${attentionOnly} refresh=${read} />`)}
|
|
40
|
+
${!attentionOnly ? html`<button type="button" class="btn btn-sm" onClick=${read}>${t("settings.refresh")}</button>` : null}
|
|
41
|
+
</section>`;
|
|
42
|
+
}
|
|
43
|
+
function LocationRow({ row, machines, attentionOnly, refresh }) {
|
|
44
|
+
const [target, setTarget] = useState("host"), [cli, setCli] = useState("claude-code"), [model, setModel] = useState("");
|
|
45
|
+
const [busy, setBusy] = useState(false), flight = useRef(false);
|
|
46
|
+
const lease = useLease("general-location-row");
|
|
47
|
+
const machine = machines.find(item => item.id === target);
|
|
48
|
+
// The rendered revision stays with this click. Never re-read and silently retry a stale proposal.
|
|
49
|
+
const propose = async next => {
|
|
50
|
+
if (flight.current || !lease.current()) return; flight.current = true; setBusy(true);
|
|
51
|
+
try { const result = await confirmedWrite("/api/settings/general-location", { body: { name: row.name, revision: row.revision, target: next }, key: newKey("general"), label: tr("label") });
|
|
52
|
+
if (lease.current()) { toast(result.body?.message || result.body?.error || t(result.ok ? "app.saved" : "app.failed"), result.ok); await refresh(); }
|
|
53
|
+
} catch (err) { if (lease.current()) toast(err.message, false); }
|
|
54
|
+
finally { flight.current = false; if (lease.current()) setBusy(false); }
|
|
55
|
+
};
|
|
56
|
+
if (attentionOnly && !row.offline && !row.hold) return null;
|
|
57
|
+
return html`<div class="general-location-row" data-general=${row.name}>
|
|
58
|
+
<strong>${row.displayName}</strong><p>${row.location === "host" ? tr("host") : `${machines.find(item => item.id === row.machineId)?.name || tr("windows")} · ${row.cli}`}</p>
|
|
59
|
+
${row.offline || row.hold ? html`<p role="alert">${tr(row.hold || "offline")}</p>` : null}
|
|
60
|
+
<p class="note">${tr("history")}</p>
|
|
61
|
+
${row.canSwitchBack ? html`<button type="button" class="btn" disabled=${busy || row.switching} onClick=${() => propose({ location: "host" })}>${tr("host")}</button>` : null}
|
|
62
|
+
${row.hold === "start" ? html`<button type="button" class="btn" disabled=${busy || row.switching} onClick=${() => propose(row.retryTarget || (row.location === "host" ? { location: "host" } : { location: "windows", machineId: row.machineId, cli: row.cli, model: row.model }))}>${t("chat.start")}</button>` : null}
|
|
63
|
+
${!attentionOnly ? html`<label>${tr("label")}<select value=${target} disabled=${busy || row.switching} onChange=${e => { setTarget(e.target.value); setModel(""); }}>
|
|
64
|
+
<option value="host">${tr("host")}</option>${machines.map(item => html`<option value=${item.id} disabled=${!item.online}>${item.name}</option>`)}
|
|
65
|
+
</select></label>
|
|
66
|
+
${target !== "host" ? html`<label>${t("settings.backend")}<select value=${cli} onChange=${e => { setCli(e.target.value); setModel(""); }}>
|
|
67
|
+
${["claude-code", "codex"].map(id => html`<option value=${id} disabled=${!machine?.clis.some(item => item.id === id)}>${id}</option>`)}
|
|
68
|
+
</select></label><${ModelSelect} value=${model} onChange=${setModel} backend=${cli} runtime="windows" machineId=${target} instance=${row.name} />` : null}
|
|
69
|
+
<button type="button" class="btn btn-primary" disabled=${busy || row.switching || target === "host" && !row.canSwitchBack || target !== "host" && (!machine?.online || !machine.clis.some(item => item.id === cli))} onClick=${() => propose(target === "host" ? { location: "host" } : { location: "windows", machineId: target, cli, model })}>${t("settings.save")}</button>` : null}
|
|
70
|
+
</div>`;
|
|
71
|
+
}
|
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
// Windows uses the same staged instance settings flow, with its runtime's supported fields.
|
|
2
2
|
import { html, useState } from "/assets/app-html.js";
|
|
3
|
-
import { t
|
|
3
|
+
import { t } from "/assets/app-i18n.js";
|
|
4
4
|
import { Dialog } from "/assets/ui-dialog.js";
|
|
5
5
|
import { ChipList, ModelSelect } from "./settings-dialogs.js";
|
|
6
|
-
import { CliProductMark, cliLabel } from "./cli-picker.js";
|
|
7
6
|
import { useWindowsCliCatalog } from "./windows-cli-catalog.js";
|
|
8
7
|
import { useRuntimeCatalog, instanceMachineId } from "./runtime-catalog.js";
|
|
9
8
|
import { NativeAgendToolsHint, tr } from "./agent-runtime.js";
|
|
10
|
-
|
|
11
|
-
"zh-TW": { fixed:"Runtime 與 CLI 隨此對話保留。", workspace:"此 Agent 的對話會沿用這個工作區。" },
|
|
12
|
-
en: { fixed:"This conversation keeps its runtime and CLI.", workspace:"This agent's conversation keeps this workspace." },
|
|
13
|
-
});
|
|
9
|
+
import {AgentRuntimeSettings} from "./agent-runtime-settings.js";
|
|
14
10
|
const tn = key => t(`settings.${key}`);
|
|
15
11
|
export function NativeInstanceSettings({ name, inst, ctx, onClose }) {
|
|
16
12
|
const machineId = instanceMachineId(inst);
|
|
@@ -19,6 +15,7 @@ export function NativeInstanceSettings({ name, inst, ctx, onClose }) {
|
|
|
19
15
|
const native = (machineId ? machine?.inventory : catalog.data)?.clis?.find(cli => cli.id === inst.backend);
|
|
20
16
|
const [form,setForm] = useState(() => ({display_name:inst.display_name || name,description:inst.description || "",model:inst.model || "",systemPrompt:inst.systemPrompt || "",native_sandbox:inst.native_sandbox || "read-only",tags:Array.isArray(inst.tags) ? [...inst.tags] : []}));
|
|
21
17
|
const set = key => value => setForm(current => ({...current,[key]:value}));
|
|
18
|
+
const dirty=Object.entries(form).some(([key,value])=>JSON.stringify(typeof value==="string"?value.trim():value)!==JSON.stringify(key==="display_name"?inst.display_name||name:key==="native_sandbox"?inst.native_sandbox||"read-only":key==="tags"?inst.tags||[]:inst[key]||""));
|
|
22
19
|
const ready = !!form.display_name.trim() && !(form.native_sandbox === "read-only" && native?.readOnlySupport === false);
|
|
23
20
|
const stage = () => {
|
|
24
21
|
if (!ready) return;
|
|
@@ -31,8 +28,8 @@ export function NativeInstanceSettings({ name, inst, ctx, onClose }) {
|
|
|
31
28
|
if (Object.keys(patch).length) ctx.stageAgent(name,inst,patch);
|
|
32
29
|
onClose();
|
|
33
30
|
};
|
|
34
|
-
return html`<${Dialog} title=${ctx.shortName(name,inst)} onClose=${onClose} wide=${true} actions=${html`<button type="button" class="btn" onClick=${onClose}>${tn("cancel")}</button><button type="button" class="btn btn-primary" disabled=${!ready} onClick=${stage}>${tn("modalDone")}</button>`}>
|
|
35
|
-
<div class="form native-instance-settings"
|
|
31
|
+
return html`<${Dialog} title=${ctx.shortName(name,inst)} onClose=${onClose} wide=${true} drawer=${true} actions=${html`<button type="button" class="btn" onClick=${onClose}>${tn("cancel")}</button><button type="button" class="btn btn-primary" disabled=${!ready} onClick=${stage}>${tn("modalDone")}</button>`}>
|
|
32
|
+
<div class="form native-instance-settings"><${AgentRuntimeSettings} name=${name} inst=${{...inst,machine_name:machine?.name||inst.machine_name,machine_status:machine?.status||inst.machine_status,machine_missing:!!machineId&&!!machines.data&&machines.data.enabled!==false&&!machine}} disabled=${dirty||ctx.staged?.size>0} onChanged=${()=>{ctx.reload();onClose();}}/>
|
|
36
33
|
<label class="field"><span>${tn("displayName")}</span><input value=${form.display_name} maxLength=${64} onInput=${event => set("display_name")(event.target.value)} /></label>
|
|
37
34
|
<label class="field"><span>${tn("description")}</span><input value=${form.description} maxLength=${512} onInput=${event => set("description")(event.target.value)} /></label>
|
|
38
35
|
<div class="field"><label for="native-instance-model">${tn("model")}</label><${ModelSelect} id="native-instance-model" value=${form.model} onChange=${set("model")} backend=${inst.backend} instance=${name} runtime="windows" machineId=${machineId} inherit=${tr("model")} /></div>
|
|
@@ -23,7 +23,7 @@ export function NativePaneRecovery({ name, inst, lease }) {
|
|
|
23
23
|
finally { inFlight.current = false; if (lease.current()) setBusy(false); }
|
|
24
24
|
};
|
|
25
25
|
if (guestPage() || inst.native_adapter !== "pane") return null;
|
|
26
|
-
if (!recovery) return ["stopped", "crashed"].includes(inst.status)
|
|
26
|
+
if (!recovery) return !inst.general_topic && ["stopped", "crashed"].includes(inst.status)
|
|
27
27
|
? html`<div class="windows-agent-blocked"><button type="button" class="btn" disabled=${busy} onClick=${() => act(`/ui/start/${encodeURIComponent(name)}`)}>${t("chat.start")}</button></div>` : null;
|
|
28
28
|
if (recovery.kind === "session-unconfirmed") return html`<div class="windows-agent-blocked" role="alert">
|
|
29
29
|
<p>${t(recovery.provider === "codex" ? "chat.paneCodexSessionUnconfirmed" : "chat.paneSessionUnconfirmed")}</p>
|
package/dist/ui/panel-chat.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { NativePaneRecovery } from "./native-pane-recovery.js";
|
|
2
|
+
import { GeneralLocation } from "./general-location.js";
|
|
2
3
|
// #1408 step 1: the chat panel. Behind the session gate (/ui/js/), loaded by the app with a dynamic import in "full"
|
|
3
4
|
// mode only. boot() runs once per page (the chat store must hear the stream from the first frame); <ChatPanel> mounts
|
|
4
5
|
// for /ui and /ui/chat/<name>.
|
|
@@ -13,6 +14,7 @@ import { appStore, createStore, guestPage, guestTrusted, useStore } from "/asset
|
|
|
13
14
|
import { ComposerAuth } from "/assets/web-authorize.js";
|
|
14
15
|
import { useLease } from "/assets/app-ctx.js";
|
|
15
16
|
import { modelText, namesOf, namesStore, navStore as instanceNavStore, supportsInstanceFeature } from "/assets/instance-nav.js";
|
|
17
|
+
import { machineName } from "/assets/machine-name.js";
|
|
16
18
|
import { PanelHeader, onPanelKey, setTitle, statusClass, statusLabel, openDrawer, requestNewInstance } from "/assets/app-shell.js";
|
|
17
19
|
import { detailsPath, textLinkSegments, sectionPath } from "/assets/app-route.js";
|
|
18
20
|
import { InstanceSwitch } from "/assets/instance-switch.js";
|
|
@@ -353,7 +355,7 @@ function ChatView({ name, inst, lease, navKey, exec, awaiting }) {
|
|
|
353
355
|
const pick = (command) => store.runCommand(name, command, "", { alive: () => lease.current() });
|
|
354
356
|
const sub = html`<span class="status"><span class=${`dot ${cls}`} aria-hidden="true"></span>${statusLabel(inst, exec, awaiting)}</span>
|
|
355
357
|
${inst.runtime === "windows" ? html`<span class="tag">Windows</span>` : null}
|
|
356
|
-
${inst.machine_id ? html`<span class="tag runtime-machine-tag" title=${t("chat.machineName",inst.
|
|
358
|
+
${inst.machine_id ? html`<span class="tag runtime-machine-tag" title=${t("chat.machineName",machineName(inst.machine_id,inst.machine_name))}>${machineName(inst.machine_id,inst.machine_name)}</span>${inst.machine_status !== "online" ? html`<span class="tag runtime-machine-offline">${t(inst.machine_status === "revoked" ? "chat.machineRevoked" : "chat.machineOffline")}</span>` : null}` : null}
|
|
357
359
|
${inst.model ? html`<button type="button" class="hd-chip" title=${t("chat.chipModel", modelText(inst))} aria-label=${t("chat.chipModel", modelText(inst))} onClick=${() => supportsInstanceFeature(inst,"commands") ? pick("model") : navigate(sectionPath("agents"))}>${modelText(inst)}</button>` : null}
|
|
358
360
|
${inst.effort && supportsInstanceFeature(inst,"effort") ? html`<button type="button" class="hd-chip" title=${t("chat.chipEffort", inst.effort)} aria-label=${t("chat.chipEffort", inst.effort)} onClick=${() => pick("effort")}>${inst.effort}</button>` : null}`;
|
|
359
361
|
return html`<div class=${`panel p-chat${wrap ? " wrap-code" : ""}`} data-width=${wide ? "full" : undefined} ref=${view}>
|
|
@@ -365,6 +367,7 @@ function ChatView({ name, inst, lease, navKey, exec, awaiting }) {
|
|
|
365
367
|
${theme && theme.background ? html`<img ref=${bg} class=${`th-bg th-bg-${theme.background.fit}`} src=${themeImageUrl(name, theme.background.image)} alt="" aria-hidden="true" decoding="async" />` : null}
|
|
366
368
|
<${ChatAmbient} name=${name} surface=${effectSurface} part="layer" />
|
|
367
369
|
<${AmbientMotionNote} name=${name} />
|
|
370
|
+
${inst.general_topic ? html`<${GeneralLocation} name=${name} attentionOnly=${true} />` : null}
|
|
368
371
|
<${NativePaneRecovery} name=${name} inst=${inst} lease=${lease} />
|
|
369
372
|
${inst.native_blocked ? html`<div class="windows-agent-blocked" role="alert"><p>${t("chat.nativeBlocked")}</p><button type="button" class="btn" disabled=${recovering} onClick=${recoverNative}>${t("chat.nativeRecover")}</button></div>` : null}
|
|
370
373
|
<${Thread} name=${name} label=${inst.runtime === "windows" ? inst.display_name || name : name} th=${thread} />
|
|
@@ -751,7 +754,7 @@ function Composer({ name, inst, label = name, lease, busy }) {
|
|
|
751
754
|
};
|
|
752
755
|
const optId = (c) => `cmd-${c.name}`;
|
|
753
756
|
return html`<div class="composer-box">
|
|
754
|
-
${machineUnavailable ? html`<p class="note runtime-machine-unavailable" role="status">${t(inst.machine_status === "revoked" ? "chat.machineRevokedHint" : "chat.machineUnavailable",inst.
|
|
757
|
+
${machineUnavailable ? html`<p class="note runtime-machine-unavailable" role="status">${t(inst.machine_status === "revoked" ? "chat.machineRevokedHint" : "chat.machineUnavailable",machineName(inst.machine_id,inst.machine_name))}</p>` : null}
|
|
755
758
|
${inst?.native_busy && !busy && !inst?.native_blocked ? html`<p class="note" role="status">${t("chat.nativeBusy")}</p>` : null}
|
|
756
759
|
${palette ? html`<ul data-trust="palette" class="cmd-palette" id="cmdPalette" role="listbox" aria-label=${t("chat.cmdCommands")}>
|
|
757
760
|
${palette.length ? palette.map((c) => html`<li key=${c.name} id=${optId(c)} role="option" aria-selected=${c === pick ? "true" : "false"}
|
package/dist/ui/panel-fleet.js
CHANGED
|
@@ -429,6 +429,8 @@ export function CreateInstanceDialog({ onClose }) {
|
|
|
429
429
|
const [machineId,setMachineId] = useState("");
|
|
430
430
|
const [nativeAdapter,setNativeAdapter]=useState("exec");
|
|
431
431
|
const machineCatalog = useRuntimeCatalog();
|
|
432
|
+
// A computer paired a moment ago must show up: ask for a fresh list whenever the dialog opens (the hook then keeps polling).
|
|
433
|
+
useEffect(() => { void machineCatalog.refresh(); },[]);
|
|
432
434
|
const machines = machineCatalog.data?.machines || [];
|
|
433
435
|
const machine = machines.find(item => item.id === machineId);
|
|
434
436
|
const [step,setStep] = useState(1), [created,setCreated] = useState(null);
|
|
@@ -524,9 +526,9 @@ export function CreateInstanceDialog({ onClose }) {
|
|
|
524
526
|
<div class="agent-create-wizard" data-step=${step}>
|
|
525
527
|
<ol class="agent-create-steps" aria-label=${tr("progress")}>${["runtimeStep","configureStep","doneStep"].map((label,index) => html`<li key=${label} class=${index+1 === step ? "current" : index+1 < step ? "complete" : ""} aria-current=${index+1 === step ? "step" : undefined}><span class="agent-create-step-number">${index+1 < step ? html`<${Icon} name="check" size=${13} />` : index+1}</span><span>${tr(label)}</span></li>`)}</ol>
|
|
526
528
|
${step === 1 ? html`<section class="agent-create-stage"><h3 ref=${heading} tabindex="-1">${tr("where")}</h3><p class="note">${tr("runtimeLead")}</p>
|
|
527
|
-
<${RuntimePicker} value=${runtime} onChange=${chooseRuntime} windowsEnabled=${windowsCli} legend=${tr("environment")} machineId=${machineId} machines=${machines} onMachineChange=${id => chooseRuntime("windows",id)} />
|
|
529
|
+
<${RuntimePicker} value=${runtime} onChange=${chooseRuntime} windowsEnabled=${windowsCli} legend=${tr("environment")} machineId=${machineId} machines=${machines} onMachineChange=${id => chooseRuntime("windows",id)} onRefresh=${machineCatalog.refresh} loaded=${!!machineCatalog.data && !machineCatalog.error} />
|
|
528
530
|
${machineCatalog.loading && !machineCatalog.data ? html`<p class="note" role="status">${tr("machineLoading")}</p>` : null}
|
|
529
|
-
${machineCatalog.error ? html`<p class="note" role="alert">${tr("machineFailed")}
|
|
531
|
+
${machineCatalog.error ? html`<p class="note" role="alert">${tr("machineFailed")}</p>` : null}
|
|
530
532
|
<a class="note runtime-manage-link" href="/settings/runtimes">${tr("manage")}</a>
|
|
531
533
|
${runtime ? html`<div class="agent-create-cli">${backendField}</div>` : null}
|
|
532
534
|
</section>` : step === 2 ? html`<section class="agent-create-stage"><h3 ref=${heading} tabindex="-1">${tr("configureStep")}</h3><p class="note">${tr("configureLead")}</p>
|
package/dist/ui/panel-needs.js
CHANGED
|
@@ -25,6 +25,7 @@ import { toast } from "/assets/ui-toast.js";
|
|
|
25
25
|
import { notifyStore, notifySupport, configDetail, reasonText, refreshNotify, setNotify } from "/assets/app-needs.js";
|
|
26
26
|
import { openSignIn, signInOffered } from "./backend-signin.js";
|
|
27
27
|
import { decideProposal, installProposals } from "./agent-proposals.js";
|
|
28
|
+
import { GeneralLocation } from "./general-location.js";
|
|
28
29
|
|
|
29
30
|
register("needs", {
|
|
30
31
|
en: {
|
|
@@ -242,6 +243,7 @@ function NeedsItem({ item, nowMs, owner }) {
|
|
|
242
243
|
<div class="n-body">
|
|
243
244
|
<div class="n-title"><strong>${reasonText(item)}</strong><span class="n-age">${age(item.since, nowMs)}</span></div>
|
|
244
245
|
${detailOf(item) ? html`<p class="n-detail">${detailOf(item)}</p>` : null}
|
|
246
|
+
${item.generalLocation ? html`<${GeneralLocation} name=${item.instance} attentionOnly=${true} />` : null}
|
|
245
247
|
<div class="n-actions">
|
|
246
248
|
${item.type === "prompt" && Array.isArray(item.actions) ? item.actions.filter((a) => !a.input).map((a) => html`<button key=${a.id} type="button" class="btn btn-sm"
|
|
247
249
|
disabled=${promptBusy} onClick=${() => answer(a.id)}>${(p ? p.busy : fallback === "busy") ? tn("answering") : a.label}</button>`) : null}
|