@qilin-agent/client-ui-agent-opens 0.0.0-stage → 3.1.3
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/LICENSE +21 -0
- package/README.i18n.yaml +22 -0
- package/README.md +35 -2
- package/README.zh.md +36 -0
- package/lib/client.js +130 -0
- package/lib/index.js +6 -0
- package/lib/types/client/index.d.ts +24 -0
- package/lib/types/index.d.ts +4 -0
- package/package.json +69 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 kkutysllb
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.i18n.yaml
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Bilingual-pair consistency record for README.md (docs/i18n/README.md): per heading
|
|
2
|
+
# section, a hash of its English and Chinese blocks outside code blocks and generated regions.
|
|
3
|
+
# After editing either side, bring the other along and re-record with:
|
|
4
|
+
# pnpm run verify-translation-pairing --write packages/client/ui-agent-opens/README.md
|
|
5
|
+
/:
|
|
6
|
+
en: 4229f048f4b85bcf
|
|
7
|
+
zh: 6f3bcf3fa8f84842
|
|
8
|
+
/qilin-client-ui-agent-opens:
|
|
9
|
+
en: 5e211958f9b9125a
|
|
10
|
+
zh: 6e44e7b616852f3b
|
|
11
|
+
/qilin-client-ui-agent-opens/summary:
|
|
12
|
+
en: ab228b85522273b5
|
|
13
|
+
zh: 0799db69741f07c6
|
|
14
|
+
/qilin-client-ui-agent-opens/table-of-contents:
|
|
15
|
+
en: 8be5c7fe49b63b1f
|
|
16
|
+
zh: c698cfc8c561a921
|
|
17
|
+
/qilin-client-ui-agent-opens/known-limitations-and-deferred-work:
|
|
18
|
+
en: 2284af55257e37c1
|
|
19
|
+
zh: b2663a37bcd536da
|
|
20
|
+
/qilin-client-ui-agent-opens/known-limitations-and-deferred-work/dev-note:
|
|
21
|
+
en: e67f633dc6fe7773
|
|
22
|
+
zh: 4e706f1b2b984e63
|
package/README.md
CHANGED
|
@@ -1,3 +1,36 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
description: "Opens what the model asked to see in the Session's own Sidebar."
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
2
5
|
|
|
3
|
-
|
|
6
|
+
# @qilin-agent/client-ui-agent-opens
|
|
7
|
+
|
|
8
|
+
English | [中文](README.zh.md)
|
|
9
|
+
|
|
10
|
+
## Summary
|
|
11
|
+
|
|
12
|
+
The browser half of `sidebar_open`: it follows `ctx.remote.sidebarOpens.watch` for the Session the user is viewing and opens each request there. A page opens in the built-in browser when `@qilin-agent/client-ui-sidebar-browser` is composed and in a new browser tab otherwise; a file opens through its resource address, because which tab type claims an address is the Sidebar's decision. The plugin keeps no state: a request is delivered once and never replayed.
|
|
13
|
+
|
|
14
|
+
## Table of Contents
|
|
15
|
+
|
|
16
|
+
- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
|
|
17
|
+
- [Dev Note](#dev-note)
|
|
18
|
+
|
|
19
|
+
-----
|
|
20
|
+
|
|
21
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
22
|
+
## Known Limitations and Deferred Work
|
|
23
|
+
|
|
24
|
+
- **No folder targets.** A directory has no Sidebar surface to open (the file tree is rooted at the workspace), so the host tool refuses one before it reaches here.
|
|
25
|
+
- **One viewer per Session.** Two browser views of one Session share one Host stream: the newest attachment takes it over, and the older view stops receiving opens.
|
|
26
|
+
- **A carrier failure ends the watch silently.** The next Session switch opens a fresh stream; a request made while the stream was down and no view was attached still waits in the Host queue until then.
|
|
27
|
+
|
|
28
|
+
<a id="dev-note"></a>
|
|
29
|
+
### Dev Note
|
|
30
|
+
|
|
31
|
+
<details>
|
|
32
|
+
<summary>Working context for maintainers — click to expand</summary>
|
|
33
|
+
|
|
34
|
+
None.
|
|
35
|
+
|
|
36
|
+
</details>
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "在所属会话自己的侧栏中打开模型要求查看的内容。"
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# @qilin-agent/client-ui-agent-opens
|
|
7
|
+
|
|
8
|
+
[English](README.md) | 中文
|
|
9
|
+
|
|
10
|
+
## 概述
|
|
11
|
+
|
|
12
|
+
`sidebar_open` 的浏览器半边:跟随当前查看会话的 `ctx.remote.sidebarOpens.watch`,并在该会话的侧栏中打开每个请求。组合了 `@qilin-agent/client-ui-sidebar-browser` 时页面在**内置浏览器**中打开,否则退回新浏览器标签页;文件通过其资源地址打开,因为"哪个 tab 类型认领这个地址"是侧栏的决定。插件不保留状态:请求只投递一次,绝不重放。
|
|
13
|
+
|
|
14
|
+
## 目录
|
|
15
|
+
|
|
16
|
+
- [已知限制与后续工作](#known-limitations-and-deferred-work)
|
|
17
|
+
- [开发备注](#dev-note)
|
|
18
|
+
|
|
19
|
+
-----
|
|
20
|
+
|
|
21
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
22
|
+
## 已知限制与后续工作
|
|
23
|
+
|
|
24
|
+
- **不支持目录目标。** 目录没有可打开的侧栏界面(文件树以工作区为根),所以宿主工具在到达这里之前就拒绝了。
|
|
25
|
+
- **每个会话只有一个观看者。** 同一会话的两个浏览器视图共享一条 Host 流:最新的附着接管它,较旧的视图不再收到打开请求。
|
|
26
|
+
- **载体失败会静默结束监听。** 下一次切换会话会开一条新流;在流中断且无人附着期间发出的请求仍留在 Host 队列里,等到那时再投递。
|
|
27
|
+
|
|
28
|
+
<a id="dev-note"></a>
|
|
29
|
+
### 开发备注
|
|
30
|
+
|
|
31
|
+
<details>
|
|
32
|
+
<summary>维护者信息 — 点击展开</summary>
|
|
33
|
+
|
|
34
|
+
无。
|
|
35
|
+
|
|
36
|
+
</details>
|
package/lib/client.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "@qilin-agent/client-ui-agent-opens",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
//#region ../../util/workspace-path/src/file-address.ts
|
|
8
|
+
/** The scheme and type every file address opens with. */
|
|
9
|
+
const FILE_ADDRESS_PREFIX = "qilin-resource://file/";
|
|
10
|
+
/** Component-encode one id or path segment, keeping `:` literal for drive letters. */
|
|
11
|
+
function encodeSegment(segment) {
|
|
12
|
+
return encodeURIComponent(segment).replace(/%3A/gi, ":");
|
|
13
|
+
}
|
|
14
|
+
/** Encode a `/`-separated path segment by segment. */
|
|
15
|
+
function encodePath(path) {
|
|
16
|
+
return path.split("/").map(encodeSegment).join("/");
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Build the address of a file read through one Session.
|
|
20
|
+
* @param sessionId - the Session whose Host workspace resolves the path.
|
|
21
|
+
* @param path - absolute or workspace-relative path; backslashes are normalized to `/`, and leading `./` prefixes are dropped.
|
|
22
|
+
* @returns the `qilin-resource://file/session/<sessionId>/<path>` address.
|
|
23
|
+
*/
|
|
24
|
+
function sessionFileAddress(sessionId, path) {
|
|
25
|
+
const normalized = path.replace(/\\/g, "/").replace(/^(?:\.\/)+/, "");
|
|
26
|
+
return `${FILE_ADDRESS_PREFIX}session/${encodeSegment(sessionId)}/${encodePath(normalized)}`;
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region ../../util/workspace-path/src/index.ts
|
|
30
|
+
/**
|
|
31
|
+
* Browser-safe Workspace path and display helpers.
|
|
32
|
+
* @module @qilin-agent/util-workspace-path
|
|
33
|
+
*/
|
|
34
|
+
/** Whether a path uses a Windows drive or UNC prefix. */
|
|
35
|
+
function isWindowsStylePath(value) {
|
|
36
|
+
return /^[A-Za-z]:[/\\]/.test(value) || value.startsWith("\\\\");
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Whether a path is absolute in either spelling the Host accepts: POSIX (`/a/b`) or Windows drive or UNC.
|
|
40
|
+
* @param path - the path to classify.
|
|
41
|
+
* @returns `true` for an absolute path; `false` for a Workspace-relative one.
|
|
42
|
+
*/
|
|
43
|
+
function isAbsoluteWorkspacePath(path) {
|
|
44
|
+
return path.startsWith("/") || isWindowsStylePath(path);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The address for a path as a caller holds it: a relative path, or an absolute
|
|
48
|
+
* path inside the Session's workspace, becomes a `session`-scoped address; an
|
|
49
|
+
* absolute path outside it, or one whose workspace root is unknown, keeps its
|
|
50
|
+
* absolute path in that Session's address.
|
|
51
|
+
* @param sessionId - the Session the path is read in.
|
|
52
|
+
* @param cwd - that Session's workspace root, when known.
|
|
53
|
+
* @param path - absolute or workspace-relative path, in either separator spelling.
|
|
54
|
+
* @returns the `qilin-resource://file/…` address.
|
|
55
|
+
*/
|
|
56
|
+
function fileAddressFor(sessionId, cwd, path) {
|
|
57
|
+
const normalized = path.replace(/\\/g, "/");
|
|
58
|
+
if (!isAbsoluteWorkspacePath(normalized)) return sessionFileAddress(sessionId, normalized);
|
|
59
|
+
const root = cwd === void 0 ? "" : cwd.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
60
|
+
if (root !== "" && normalized === root) return sessionFileAddress(sessionId, "");
|
|
61
|
+
if (root !== "" && normalized.startsWith(`${root}/`)) return sessionFileAddress(sessionId, normalized.slice(root.length + 1));
|
|
62
|
+
return sessionFileAddress(sessionId, normalized);
|
|
63
|
+
}
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region lib/types/client/index.js
|
|
66
|
+
/** Required browser services: the Remote carrier, the Sidebar's tabs, and the viewed Session. */
|
|
67
|
+
const inject = [
|
|
68
|
+
"remote",
|
|
69
|
+
"sidebarRight",
|
|
70
|
+
"sidebarRightTabs",
|
|
71
|
+
"sessions",
|
|
72
|
+
"uiSession"
|
|
73
|
+
];
|
|
74
|
+
/**
|
|
75
|
+
* Client plugin body: follow the viewed Session's open requests and dispatch them.
|
|
76
|
+
* @param ctx - client root context carrying the Remote carrier and the Sidebar face.
|
|
77
|
+
*/
|
|
78
|
+
function apply(ctx) {
|
|
79
|
+
ctx.effect(() => {
|
|
80
|
+
const current = ctx.uiSession.adapter.current;
|
|
81
|
+
let stop;
|
|
82
|
+
const sync = () => {
|
|
83
|
+
stop?.();
|
|
84
|
+
stop = void 0;
|
|
85
|
+
const sessionId = current.getSnapshot().key;
|
|
86
|
+
if (sessionId === void 0) return;
|
|
87
|
+
const controller = new AbortController();
|
|
88
|
+
stop = () => {
|
|
89
|
+
controller.abort();
|
|
90
|
+
};
|
|
91
|
+
(async () => {
|
|
92
|
+
try {
|
|
93
|
+
for await (const request of ctx.remote.sidebarOpens.watch(sessionId, controller.signal)) {
|
|
94
|
+
if (controller.signal.aborted) return;
|
|
95
|
+
openRequested(ctx, sessionId, request);
|
|
96
|
+
}
|
|
97
|
+
} catch {}
|
|
98
|
+
})();
|
|
99
|
+
};
|
|
100
|
+
const unsubscribe = current.subscribe(sync);
|
|
101
|
+
sync();
|
|
102
|
+
return () => {
|
|
103
|
+
unsubscribe();
|
|
104
|
+
stop?.();
|
|
105
|
+
};
|
|
106
|
+
}, "ui-agent-opens: model open requests");
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Open one request in the Session's Sidebar.
|
|
110
|
+
* @param ctx - client root context.
|
|
111
|
+
* @param sessionId - the Session whose Sidebar the request targets.
|
|
112
|
+
* @param request - the resolved request.
|
|
113
|
+
*/
|
|
114
|
+
function openRequested(ctx, sessionId, request) {
|
|
115
|
+
if (request.kind === "url") {
|
|
116
|
+
if (ctx.sidebarRightTabs.get("browser") !== void 0) ctx.sidebarRight.openTab("browser", { params: { url: request.target } });
|
|
117
|
+
else window.open(request.target, "_blank", "noopener,noreferrer");
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const cwd = ctx.sessions.list.getSnapshot().byId[sessionId]?.cwd;
|
|
121
|
+
ctx.sidebarRight.openResource(fileAddressFor(sessionId, cwd, request.target));
|
|
122
|
+
}
|
|
123
|
+
//#endregion
|
|
124
|
+
exports.apply = apply;
|
|
125
|
+
exports.inject = inject;
|
|
126
|
+
return module.exports;
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
//# sourceMappingURL=client.js.map
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser half: open what the model asked to see in the Session's own Sidebar.
|
|
3
|
+
*
|
|
4
|
+
* The requests arrive as a Host Remote stream (`ctx.remote.sidebarOpens.watch`)
|
|
5
|
+
* for the Session the user is viewing, so the plugin watches the current Session
|
|
6
|
+
* and re-subscribes when the view switches. Delivery is transient by design: the
|
|
7
|
+
* Host consumes a request on send and drops it, so nothing here replays an open
|
|
8
|
+
* the user already saw — and nothing here is durable, which is why this package
|
|
9
|
+
* owns no store, no slot, and no copy.
|
|
10
|
+
*
|
|
11
|
+
* A page opens in the built-in browser when that tab type is composed and in a
|
|
12
|
+
* new browser tab otherwise. A file opens through its resource address, because
|
|
13
|
+
* which tab type claims an address is the Sidebar's decision, not this
|
|
14
|
+
* dispatcher's.
|
|
15
|
+
*/
|
|
16
|
+
import type { Context } from '@qilin-agent/kylin';
|
|
17
|
+
/** Required browser services: the Remote carrier, the Sidebar's tabs, and the viewed Session. */
|
|
18
|
+
export declare const inject: string[];
|
|
19
|
+
/**
|
|
20
|
+
* Client plugin body: follow the viewed Session's open requests and dispatch them.
|
|
21
|
+
* @param ctx - client root context carrying the Remote carrier and the Sidebar face.
|
|
22
|
+
*/
|
|
23
|
+
export declare function apply(ctx: Context): void;
|
|
24
|
+
//# sourceMappingURL=index.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,72 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qilin-agent/client-ui-agent-opens",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"description": "Opens what the model asked to see in the Session's Sidebar",
|
|
4
|
+
"version": "3.1.3",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/kkutysllb/QiLin.git",
|
|
11
|
+
"directory": "packages/client/ui-agent-opens"
|
|
12
|
+
},
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": "^22.19.0 || >=24.0.0"
|
|
15
|
+
},
|
|
16
|
+
"type": "module",
|
|
17
|
+
"main": "lib/index.js",
|
|
18
|
+
"types": "lib/types/index.d.ts",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./lib/types/index.d.ts",
|
|
22
|
+
"default": "./lib/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./client": {
|
|
25
|
+
"types": "./lib/types/client/index.d.ts",
|
|
26
|
+
"default": "./lib/client.js"
|
|
27
|
+
},
|
|
28
|
+
"./src/*": "./src/*",
|
|
29
|
+
"./package.json": "./package.json"
|
|
30
|
+
},
|
|
31
|
+
"qilin": {
|
|
32
|
+
"client": {
|
|
33
|
+
"inject": [
|
|
34
|
+
"@qilin-agent/api-gateway",
|
|
35
|
+
"@qilin-agent/api-remotes",
|
|
36
|
+
"@qilin-agent/client-ui-sidebar-browser",
|
|
37
|
+
"@qilin-agent/client-ui-sidebar-right",
|
|
38
|
+
"@qilin-agent/client-ui-session"
|
|
39
|
+
],
|
|
40
|
+
"platform": "web"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"license": "MIT",
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"@qilin-agent/kylin": "~4.0.3"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@qilin-agent/kylin": "~4.0.3",
|
|
49
|
+
"@qilin-agent/api-gateway": "3.1.3",
|
|
50
|
+
"@qilin-agent/client-locale": "3.1.3",
|
|
51
|
+
"@qilin-agent/api-remotes": "3.1.3",
|
|
52
|
+
"@qilin-agent/client-store": "3.1.3",
|
|
53
|
+
"@qilin-agent/client-test-runtime": "3.1.3",
|
|
54
|
+
"@qilin-agent/client-ui-renderer": "3.1.3",
|
|
55
|
+
"@qilin-agent/client-ui-session": "3.1.3",
|
|
56
|
+
"@qilin-agent/client-ui-sidebar-browser": "3.1.3",
|
|
57
|
+
"@qilin-agent/client-ui-sidebar-right": "3.1.3",
|
|
58
|
+
"@qilin-agent/client-ui-slots": "3.1.3",
|
|
59
|
+
"@qilin-agent/util-workspace-path": "3.1.3",
|
|
60
|
+
"@qilin-agent/sidebar-opens": "3.1.3",
|
|
61
|
+
"@qilin-agent/session": "3.1.3"
|
|
62
|
+
},
|
|
63
|
+
"files": [
|
|
64
|
+
"lib/index.js",
|
|
65
|
+
"lib/client.js",
|
|
66
|
+
"lib/types/**/*.d.ts"
|
|
67
|
+
],
|
|
68
|
+
"scripts": {
|
|
69
|
+
"bundle": "tsdown",
|
|
70
|
+
"watch": "tsdown --watch"
|
|
71
|
+
}
|
|
6
72
|
}
|