@personai/agent-ui-terminal 1.0.0-preview.1 → 1.0.0-preview.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/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  ## 安装
6
6
 
7
7
  ```sh
8
- npm install @personai/agent-ui-terminal
8
+ npm install --save-exact @personai/agent-ui-terminal@1.0.0-preview.3
9
9
  ```
10
10
 
11
11
  应用入口需导入 `@personai/agent-ui-terminal/styles.css`。包内 `npm run dev` 提供本地模拟宿主演示。
@@ -38,6 +38,26 @@ terminal.dispose();
38
38
 
39
39
  `createTerminalTransport` 使用 Socket.IO 连接远端 PTY。服务端负责安全地创建并管理 shell 进程;浏览器组件只发送终端输入、尺寸变化和信号。连接地址、认证、租户信息和进程启动策略均由宿主决定。
40
40
 
41
+ ## 实例控制
42
+
43
+ `initTerminal()` 返回可直接调用的实例,无需获取内部 Terminal 类或 xterm 对象。以下三个方法从 `1.0.0-preview.3` 开始提供,`1.0.0-preview.2` 不包含它们。
44
+
45
+ | 方法 | 返回值 | 行为 |
46
+ | --- | --- | --- |
47
+ | `clear()` | `void` | 调用 xterm 清屏,清除缓冲区并保留当前提示行;不向 shell 发送 clear 命令、不停止 PTY,也不删除宿主历史 |
48
+ | `getSelection()` | `string` | 读取终端选中的文本;未选择时返回空字符串 |
49
+ | `focus()` | `void` | 聚焦终端输入;不重建实例或连接 |
50
+
51
+ ```ts
52
+ terminal.clear();
53
+ const selectedText = terminal.getSelection();
54
+ terminal.focus();
55
+ ```
56
+
57
+ 这些方法在实例存活期间使用;关闭后调用 `dispose()`,不要继续操作已销毁的实例。三个方法均保留当前主题和语言设置。
58
+
59
+ 主题初始化放在 `terminalOptions.themeTokens`;运行中通过 `setThemeTokens(tokens)` 更新,通过 `resetTheme()` 清除覆盖,通过 `refreshTheme()` 将当前 CSS 颜色同步到 xterm。`setLocale()` / `setMessages()` 更新文案。
60
+
41
61
  ## 自定义传输
42
62
 
43
63
  如果宿主的传输协议不同,实现并注入 `TerminalTransport` 即可。它负责连接、发送键盘输入/尺寸/信号,并通知组件输出、会话信息、退出和错误。需要 AI 能力时再实现 `AITransport` 的 `request` 与 `stream`;流式结果通过 `onChunk` 提供文本,通过 `onEvent` 提供结构化 Agent 活动。详见包导出的 TypeScript 接口。
package/dist/index.js CHANGED
@@ -2552,8 +2552,8 @@ class Yi {
2552
2552
  }
2553
2553
  }
2554
2554
  getOrCreateSession(e) {
2555
- var I;
2556
- if (!((I = this.terminal) != null && I.element))
2555
+ var C;
2556
+ if (!((C = this.terminal) != null && C.element))
2557
2557
  return;
2558
2558
  const t = Zi(e.requestId, e.conversationId), i = this.sessions.get(t);
2559
2559
  if (i)
@@ -3964,8 +3964,8 @@ class Ds {
3964
3964
  let g;
3965
3965
  try {
3966
3966
  g = JSON.parse(u);
3967
- } catch (I) {
3968
- h(I);
3967
+ } catch (C) {
3968
+ h(C);
3969
3969
  return;
3970
3970
  }
3971
3971
  const w = $s(g);
@@ -4072,13 +4072,13 @@ function Qs(s) {
4072
4072
  let o, r = !1;
4073
4073
  const a = {}, l = /* @__PURE__ */ new Map(), c = /* @__PURE__ */ new Map(), h = /* @__PURE__ */ new Map(), p = /* @__PURE__ */ new Set(), u = (d, m, E) => {
4074
4074
  var T;
4075
- const C = (T = a.current) == null ? void 0 : T.shellIntegrationAddon, $ = (C == null ? void 0 : C.sanitizeInput(d)) ?? d;
4076
- C == null || C.noteInput($), t.input($, m, E);
4075
+ const I = (T = a.current) == null ? void 0 : T.shellIntegrationAddon, $ = (I == null ? void 0 : I.sanitizeInput(d)) ?? d;
4076
+ I == null || I.noteInput($), t.input($, m, E);
4077
4077
  }, g = i ? async (d) => {
4078
4078
  var T, ie, Ue, He, ze, We;
4079
4079
  const m = ((ie = (T = globalThis.crypto) == null ? void 0 : T.randomUUID) == null ? void 0 : ie.call(T)) ?? `${Date.now()}-${Math.random()}`, E = new AbortController();
4080
4080
  h.set(m, E);
4081
- let C;
4081
+ let I;
4082
4082
  const $ = async (Y) => {
4083
4083
  var D, fe;
4084
4084
  await i.stream(
@@ -4099,7 +4099,7 @@ function Qs(s) {
4099
4099
  if (r) return;
4100
4100
  const _ = Fs(R);
4101
4101
  if (_) {
4102
- C = _, (se = (k = a.current) == null ? void 0 : k.modelSessionAddon) == null || se.appendUserInputRequest({
4102
+ I = _, (se = (k = a.current) == null ? void 0 : k.modelSessionAddon) == null || se.appendUserInputRequest({
4103
4103
  conversationId: "default",
4104
4104
  requestId: m,
4105
4105
  request: _,
@@ -4122,9 +4122,9 @@ function Qs(s) {
4122
4122
  requestId: m
4123
4123
  }), await $();
4124
4124
  } finally {
4125
- h.delete(m), !r && !C && !p.has(m) && await (((We = (ze = a.current) == null ? void 0 : ze.modelSessionAddon) == null ? void 0 : We.complete(m)) ?? Promise.resolve());
4125
+ h.delete(m), !r && !I && !p.has(m) && await (((We = (ze = a.current) == null ? void 0 : ze.modelSessionAddon) == null ? void 0 : We.complete(m)) ?? Promise.resolve());
4126
4126
  }
4127
- if (C && !r) {
4127
+ if (I && !r) {
4128
4128
  let Y, D;
4129
4129
  const fe = new Promise((R, _) => {
4130
4130
  Y = R, D = _;
@@ -4135,7 +4135,7 @@ function Qs(s) {
4135
4135
  }), l.set(m, async (R, _) => {
4136
4136
  var se;
4137
4137
  const k = (se = a.current) == null ? void 0 : se.modelSessionAddon;
4138
- k == null || k.clearUserInputRequest(m, R, _), C = void 0;
4138
+ k == null || k.clearUserInputRequest(m, R, _), I = void 0;
4139
4139
  try {
4140
4140
  await $([
4141
4141
  {
@@ -4143,11 +4143,11 @@ function Qs(s) {
4143
4143
  status: "resolved",
4144
4144
  payload: { answers: _ }
4145
4145
  }
4146
- ]), C || (await (k == null ? void 0 : k.complete(m)), Y());
4146
+ ]), I || (await (k == null ? void 0 : k.complete(m)), Y());
4147
4147
  } catch (ne) {
4148
4148
  throw D(ne), ne;
4149
4149
  } finally {
4150
- k == null || k.setUserInputSubmitting(m, !1), C || (l.delete(m), c.delete(m));
4150
+ k == null || k.setUserInputSubmitting(m, !1), I || (l.delete(m), c.delete(m));
4151
4151
  }
4152
4152
  }), await fe;
4153
4153
  }
@@ -4156,7 +4156,7 @@ function Qs(s) {
4156
4156
  signal: E
4157
4157
  }) => {
4158
4158
  var $, T;
4159
- let C = "";
4159
+ let I = "";
4160
4160
  return await i.stream(
4161
4161
  ((T = ($ = s.prompts) == null ? void 0 : $.explain) == null ? void 0 : T.call($, d)) ?? `Command:
4162
4162
  ${d.command}
@@ -4165,18 +4165,18 @@ Output:
4165
4165
  ${d.output}`,
4166
4166
  {
4167
4167
  onChunk: ({ delta: ie }) => {
4168
- C += ie, m == null || m(ie);
4168
+ I += ie, m == null || m(ie);
4169
4169
  },
4170
4170
  signal: E
4171
4171
  }
4172
- ), C;
4172
+ ), I;
4173
4173
  } : void 0, f = i ? (d) => {
4174
4174
  var m, E;
4175
4175
  return i.request(((E = (m = s.prompts) == null ? void 0 : m.generate) == null ? void 0 : E.call(m, d)) ?? d);
4176
4176
  } : void 0, F = (d) => {
4177
4177
  const m = a.current;
4178
4178
  return m ? m.executeCommand(d) : Promise.reject(new Error("终端尚未完成初始化"));
4179
- }, I = new ks(s.el, {
4179
+ }, C = new ks(s.el, {
4180
4180
  cursorBlink: !0,
4181
4181
  cursorStyle: "bar",
4182
4182
  cursorWidth: 2,
@@ -4234,14 +4234,14 @@ ${d.output}`,
4234
4234
  requestId: m,
4235
4235
  answers: E
4236
4236
  }) => {
4237
- const C = l.get(d);
4238
- C && await C(m, E);
4237
+ const I = l.get(d);
4238
+ I && await I(m, E);
4239
4239
  },
4240
4240
  onCancelSession: async ({ requestId: d }) => {
4241
- var E, C, $, T;
4241
+ var E, I, $, T;
4242
4242
  p.add(d), l.delete(d), (E = c.get(d)) == null || E.resolve(), c.delete(d);
4243
4243
  const m = h.get(d);
4244
- m == null || m.abort(new DOMException("会话已终止", "AbortError")), await ((C = i == null ? void 0 : i.cancel) == null ? void 0 : C.call(
4244
+ m == null || m.abort(new DOMException("会话已终止", "AbortError")), await ((I = i == null ? void 0 : i.cancel) == null ? void 0 : I.call(
4245
4245
  i,
4246
4246
  (m == null ? void 0 : m.signal) ?? new AbortController().signal
4247
4247
  )), await ((T = ($ = a.current) == null ? void 0 : $.modelSessionAddon) == null ? void 0 : T.complete(
@@ -4250,22 +4250,22 @@ ${d.output}`,
4250
4250
  }
4251
4251
  } : void 0
4252
4252
  });
4253
- a.current = I;
4253
+ a.current = C;
4254
4254
  const v = t.onOutput((d) => {
4255
- I.terminal.write(d, () => {
4255
+ C.terminal.write(d, () => {
4256
4256
  var m;
4257
- (m = I.shellIntegrationAddon) == null || m.noteOutput(d);
4257
+ (m = C.shellIntegrationAddon) == null || m.noteOutput(d);
4258
4258
  });
4259
4259
  }), S = t.onSessionChange((d) => {
4260
4260
  var m, E;
4261
- o = d, (m = I.shellIntegrationAddon) == null || m.setCwd(d.currentDirectory), d.supportsBlocks || (E = s.onDegraded) == null || E.call(s, d);
4261
+ o = d, (m = C.shellIntegrationAddon) == null || m.setCwd(d.currentDirectory), d.supportsBlocks || (E = s.onDegraded) == null || E.call(s, d);
4262
4262
  }), q = t.onExit((d) => {
4263
4263
  var m;
4264
4264
  (m = s.onExit) == null || m.call(s, d);
4265
4265
  }), K = t.onError((d) => {
4266
4266
  te(d);
4267
- }), _e = I.terminal.onResize((d) => {
4268
- I.shouldNotifyTransportResize() && t.resize(d);
4267
+ }), _e = C.terminal.onResize((d) => {
4268
+ C.shouldNotifyTransportResize() && t.resize(d);
4269
4269
  });
4270
4270
  t.connect().catch((d) => {
4271
4271
  te(qs(d));
@@ -4276,15 +4276,27 @@ ${d.output}`,
4276
4276
  return {
4277
4277
  dispose() {
4278
4278
  var d;
4279
- r = !0, v.dispose(), S.dispose(), q.dispose(), K.dispose(), _e.dispose(), t.dispose(), (d = i == null ? void 0 : i.dispose) == null || d.call(i), I.dispose();
4279
+ r = !0, v.dispose(), S.dispose(), q.dispose(), K.dispose(), _e.dispose(), t.dispose(), (d = i == null ? void 0 : i.dispose) == null || d.call(i), C.dispose();
4280
+ },
4281
+ /** Clears xterm's buffer while keeping the current prompt line and PTY session. */
4282
+ clear() {
4283
+ C.terminal.clear();
4284
+ },
4285
+ /** Returns selected terminal text, or an empty string when nothing is selected. */
4286
+ getSelection() {
4287
+ return C.terminal.getSelection();
4288
+ },
4289
+ /** Focuses the terminal input without reconnecting its transport. */
4290
+ focus() {
4291
+ C.terminal.focus();
4280
4292
  },
4281
4293
  /** Runs a command through the real prompt, producing a normal block. */
4282
4294
  executeCommand(d) {
4283
- return I.executeCommand(d);
4295
+ return C.executeCommand(d);
4284
4296
  },
4285
4297
  appendChatChunk(d, m) {
4286
4298
  var E;
4287
- (E = I.modelSessionAddon) == null || E.appendMessageChunk({
4299
+ (E = C.modelSessionAddon) == null || E.appendMessageChunk({
4288
4300
  requestId: (m == null ? void 0 : m.requestId) ?? "default",
4289
4301
  conversationId: (m == null ? void 0 : m.conversationId) ?? "default",
4290
4302
  response: d
@@ -4292,22 +4304,22 @@ ${d.output}`,
4292
4304
  },
4293
4305
  completeChat(d = "default") {
4294
4306
  var m;
4295
- return ((m = I.modelSessionAddon) == null ? void 0 : m.complete(d)) ?? Promise.resolve();
4307
+ return ((m = C.modelSessionAddon) == null ? void 0 : m.complete(d)) ?? Promise.resolve();
4296
4308
  },
4297
4309
  setThemeTokens(d) {
4298
- I.setThemeTokens(d);
4310
+ C.setThemeTokens(d);
4299
4311
  },
4300
4312
  resetTheme() {
4301
- I.resetTheme();
4313
+ C.resetTheme();
4302
4314
  },
4303
4315
  refreshTheme() {
4304
- I.refreshTheme();
4316
+ C.refreshTheme();
4305
4317
  },
4306
4318
  setLocale(d, m) {
4307
- n.setLocale(d), m && n.setMessages(m), I.refreshLocale();
4319
+ n.setLocale(d), m && n.setMessages(m), C.refreshLocale();
4308
4320
  },
4309
4321
  setMessages(d) {
4310
- n.setMessages(d), I.refreshLocale();
4322
+ n.setMessages(d), C.refreshLocale();
4311
4323
  }
4312
4324
  };
4313
4325
  }
@@ -49,6 +49,12 @@ export interface ChatTerminalOptions {
49
49
  export type IOptions = ChatTerminalOptions;
50
50
  export declare function initTerminal(options: ChatTerminalOptions): {
51
51
  dispose(): void;
52
+ /** Clears xterm's buffer while keeping the current prompt line and PTY session. */
53
+ clear(): void;
54
+ /** Returns selected terminal text, or an empty string when nothing is selected. */
55
+ getSelection(): string;
56
+ /** Focuses the terminal input without reconnecting its transport. */
57
+ focus(): void;
52
58
  /** Runs a command through the real prompt, producing a normal block. */
53
59
  executeCommand(command: string): Promise<TerminalCommandBlock>;
54
60
  appendChatChunk(response: string, ids?: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@personai/agent-ui-terminal",
3
- "version": "1.0.0-preview.1",
3
+ "version": "1.0.0-preview.3",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -29,7 +29,7 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "@microsoft/fetch-event-source": "^2.0.1",
32
- "@personai/agent-ui-chat-core": "1.0.0-preview.1",
32
+ "@personai/agent-ui-chat-core": "1.0.0-preview.3",
33
33
  "@preact/signals-core": "^1.14.4",
34
34
  "@xterm/addon-fit": "^0.10.0",
35
35
  "@xterm/addon-web-links": "^0.11.0",