mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-08 09:24:41 +00:00
Merge remote-tracking branch 'origin/main' into diagnose/3589-contributor-6b87ee9f
This commit is contained in:
@@ -33,7 +33,7 @@ Arguments marked `?` are optional.
|
||||
| -------------------- | -------------------------------------- | ------------------------------------------------------------------------------------- |
|
||||
| `browser_snapshot` | `browserId` | Return the page as an accessibility tree with element refs. |
|
||||
| `browser_screenshot` | `browserId, fullPage?` | Capture a PNG of the viewport, or the full page with `fullPage`. |
|
||||
| `browser_logs` | `browserId, maxEntries?` | Read recent console messages and network timing entries. |
|
||||
| `browser_logs` | `browserId, maxEntries?` | Read recent console messages and network entries: URL, timing, size and HTTP status. |
|
||||
| `browser_wait` | `browserId, text? \| url?, timeoutMs?` | Wait until the page contains text or reaches a URL fragment (exactly one of the two). |
|
||||
|
||||
## Interacting
|
||||
|
||||
@@ -31,7 +31,7 @@ nav: 工具参考
|
||||
| -------------------- | -------------------------------------- | ------------------------------------------------------------------- |
|
||||
| `browser_snapshot` | `browserId` | 以带元素引用的无障碍树形式返回页面。 |
|
||||
| `browser_screenshot` | `browserId, fullPage?` | 截取视口的 PNG;传入 `fullPage` 时截取整个页面。 |
|
||||
| `browser_logs` | `browserId, maxEntries?` | 读取最近的控制台消息和网络耗时条目。 |
|
||||
| `browser_logs` | `browserId, maxEntries?` | 读取最近的控制台消息和网络条目:URL、耗时、大小和 HTTP 状态码。 |
|
||||
| `browser_wait` | `browserId, text? \| url?, timeoutMs?` | 等待页面包含某段文本或到达某个 URL 片段(两者必须且只能提供一个)。 |
|
||||
|
||||
## 交互 {#interacting}
|
||||
|
||||
+4
-2
@@ -211,6 +211,8 @@ paseo workspace rename <workspace-id> --reset # back to the branch or director
|
||||
paseo workspace archive <workspace-id>
|
||||
```
|
||||
|
||||
Inside an agent session, `PASEO_WORKSPACE_ID` holds the agent's own workspace ID, so an agent can run `paseo workspace rename "$PASEO_WORKSPACE_ID" "Auth rework"` or `paseo workspace archive "$PASEO_WORKSPACE_ID"` even when other workspaces share its directory.
|
||||
|
||||
Add `--forge <name>` to PR checkout when Paseo cannot infer the forge from the source checkout. See [Git worktrees](/docs/worktrees) for setup hooks and services.
|
||||
|
||||
## Terminals
|
||||
@@ -289,10 +291,10 @@ behavior.
|
||||
|
||||
```bash
|
||||
paseo ls # Non-archived agents in active workspaces
|
||||
paseo ls -a # Also include archived agents
|
||||
paseo ls -a # Also include archived and background agents, with a BACKGROUND column
|
||||
paseo ls -g # Non-archived agents across all workspaces
|
||||
paseo ls -a -g --json # All agents, including archived, as JSON
|
||||
paseo ls --background # Also include agents in background workspaces
|
||||
paseo ls --background # Also include background agents and agents in background workspaces
|
||||
```
|
||||
|
||||
## Streaming output
|
||||
|
||||
@@ -202,6 +202,8 @@ paseo workspace rename <workspace-id> --reset # back to the branch or director
|
||||
paseo workspace archive <workspace-id>
|
||||
```
|
||||
|
||||
在 Agent 会话中,`PASEO_WORKSPACE_ID` 保存该 Agent 自己的工作区 ID。即使其他工作区共享同一个目录,Agent 也可以运行 `paseo workspace rename "$PASEO_WORKSPACE_ID" "Auth rework"` 或 `paseo workspace archive "$PASEO_WORKSPACE_ID"`。
|
||||
|
||||
当 Paseo 无法从源检出推断代码托管平台时,为 PR 检出添加 `--forge <name>`。setup 钩子和服务见 [Git worktree](/docs/worktrees)。
|
||||
|
||||
## 终端 {#terminals}
|
||||
@@ -270,10 +272,10 @@ paseo plugin remove my-plugin
|
||||
|
||||
```bash
|
||||
paseo ls # Non-archived agents in active workspaces
|
||||
paseo ls -a # Also include archived agents
|
||||
paseo ls -a # Also include archived and background agents, with a BACKGROUND column
|
||||
paseo ls -g # Non-archived agents across all workspaces
|
||||
paseo ls -a -g --json # All agents, including archived, as JSON
|
||||
paseo ls --background # Also include agents in background workspaces
|
||||
paseo ls --background # Also include background agents and agents in background workspaces
|
||||
```
|
||||
|
||||
## 流式输出 {#streaming-output}
|
||||
|
||||
+12
-12
@@ -87,18 +87,18 @@ MCP does not expose an agent-detach tool. Detaching is a manual user action in t
|
||||
|
||||
### Agents
|
||||
|
||||
| Tool | Function |
|
||||
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `create_agent` | Create an agent, optionally placing it in an existing workspace with `workspaceId`. Use `initialPrompt` to start work; returns without waiting for completion. |
|
||||
| `send_agent_prompt` | Send a prompt using `agentId` and `prompt`; returns without waiting for completion. |
|
||||
| `get_agent_status` | Return the latest snapshot for an agent. |
|
||||
| `list_agents` | List recent agents as compact metadata; `includeBackground: true` includes background work. |
|
||||
| `cancel_agent` | Abort an agent's current run but keep the agent alive. |
|
||||
| `archive_agent` | Soft-delete an agent and remove it from the active list. |
|
||||
| `kill_agent` | Terminate an agent session permanently. |
|
||||
| `update_agent` | Update an agent name, labels, or runtime settings such as mode/model/thinking/features. |
|
||||
| `get_agent_activity` | Return recent agent timeline entries as a curated summary. |
|
||||
| `set_agent_mode` | Switch an agent's session mode. |
|
||||
| Tool | Function |
|
||||
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `create_agent` | Create an agent, optionally placing it in an existing workspace with `workspaceId`. Use `initialPrompt` to start work; returns without waiting for completion. `background: true` hides it from tabs, default lists, your subagent track and notifications; you still get its finish notification. Omission inherits your own value. |
|
||||
| `send_agent_prompt` | Send a prompt using `agentId` and `prompt`; returns without waiting for completion. |
|
||||
| `get_agent_status` | Return the latest snapshot for an agent. |
|
||||
| `list_agents` | List recent agents as compact metadata; `includeBackground: true` includes background work. |
|
||||
| `cancel_agent` | Abort an agent's current run but keep the agent alive. |
|
||||
| `archive_agent` | Soft-delete an agent and remove it from the active list. |
|
||||
| `kill_agent` | Terminate an agent session permanently. |
|
||||
| `update_agent` | Update an agent name, labels, or runtime settings such as mode/model/thinking/features. |
|
||||
| `get_agent_activity` | Return recent agent timeline entries as a curated summary. |
|
||||
| `set_agent_mode` | Switch an agent's session mode. |
|
||||
|
||||
Paseo agents receive finish, error, and permission notifications by default; set `notifyOnFinish: false` to disable them. External MCP clients use `get_agent_status` and `get_agent_activity` to check results.
|
||||
|
||||
|
||||
+12
-12
@@ -79,18 +79,18 @@ MCP 不提供解除 Agent 父子关系的工具。解除关系是用户在应用
|
||||
|
||||
### Agent {#agents}
|
||||
|
||||
| 工具 | 功能 |
|
||||
| -------------------- | ---------------------------------------------------------------------------------------------------- |
|
||||
| `create_agent` | 创建 Agent,可用 `workspaceId` 把它放到已有的工作区。用 `initialPrompt` 开始工作;不等待完成就返回。 |
|
||||
| `send_agent_prompt` | 用 `agentId` 和 `prompt` 发送提示词;不等待完成就返回。 |
|
||||
| `get_agent_status` | 返回 Agent 的最新快照。 |
|
||||
| `list_agents` | 以精简元数据列出最近的 Agent;`includeBackground: true` 会包含后台工作。 |
|
||||
| `cancel_agent` | 中止 Agent 当前的运行,但保留该 Agent。 |
|
||||
| `archive_agent` | 软删除 Agent,并把它从活动列表中移除。 |
|
||||
| `kill_agent` | 永久终止 Agent 会话。 |
|
||||
| `update_agent` | 更新 Agent 的名称、标签或运行时设置,例如模式、模型、思考选项和功能。 |
|
||||
| `get_agent_activity` | 以整理后的摘要返回 Agent 最近的时间线条目。 |
|
||||
| `set_agent_mode` | 切换 Agent 的会话模式。 |
|
||||
| 工具 | 功能 |
|
||||
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `create_agent` | 创建 Agent,可用 `workspaceId` 把它放到已有的工作区。用 `initialPrompt` 开始工作;不等待完成就返回。`background: true` 会把它从标签页、默认列表、你的子 Agent 列表和通知中隐藏;你仍会收到它的完成通知。省略时继承你自己的设置。 |
|
||||
| `send_agent_prompt` | 用 `agentId` 和 `prompt` 发送提示词;不等待完成就返回。 |
|
||||
| `get_agent_status` | 返回 Agent 的最新快照。 |
|
||||
| `list_agents` | 以精简元数据列出最近的 Agent;`includeBackground: true` 会包含后台工作。 |
|
||||
| `cancel_agent` | 中止 Agent 当前的运行,但保留该 Agent。 |
|
||||
| `archive_agent` | 软删除 Agent,并把它从活动列表中移除。 |
|
||||
| `kill_agent` | 永久终止 Agent 会话。 |
|
||||
| `update_agent` | 更新 Agent 的名称、标签或运行时设置,例如模式、模型、思考选项和功能。 |
|
||||
| `get_agent_activity` | 以整理后的摘要返回 Agent 最近的时间线条目。 |
|
||||
| `set_agent_mode` | 切换 Agent 的会话模式。 |
|
||||
|
||||
Paseo Agent 默认会收到完成、错误和权限通知;设置 `notifyOnFinish: false` 可以关闭它们。外部 MCP 客户端使用 `get_agent_status` 和 `get_agent_activity` 查看结果。
|
||||
|
||||
|
||||
@@ -588,7 +588,7 @@ server.addHook("agent.permission.after", async ({ agent, requestId, resolution }
|
||||
});
|
||||
```
|
||||
|
||||
Agent hooks exclude the daemon's private disposable helpers. Public background workspaces and their ordinary agents use the same hooks.
|
||||
Agent hooks exclude the daemon's private disposable helpers. Background agents and the agents in background workspaces use the same hooks.
|
||||
|
||||
## Ordering and returned values
|
||||
|
||||
@@ -598,7 +598,7 @@ Creation request
|
||||
→ resolve defaults and validate provider configuration
|
||||
→ derive launch configuration with Paseo runtime tools and daemon prompt
|
||||
→ agent.open.before hooks (same ordering; env and plugin-local tools and MCP servers)
|
||||
→ set PASEO_AGENT_ID and PASEO_AGENT_CWD
|
||||
→ set PASEO_AGENT_ID, PASEO_AGENT_CWD, and PASEO_WORKSPACE_ID when the agent has a workspace
|
||||
→ open provider session and save agent configuration
|
||||
```
|
||||
|
||||
|
||||
@@ -571,7 +571,7 @@ server.addHook("agent.permission.after", async ({ agent, requestId, resolution }
|
||||
});
|
||||
```
|
||||
|
||||
Agent 钩子不包括 daemon 的私有临时辅助 Agent。公开的后台工作区及其普通 Agent 使用相同钩子。
|
||||
Agent 钩子不包括 daemon 的私有临时辅助 Agent。后台 Agent 和后台工作区中的 Agent 使用相同钩子。
|
||||
|
||||
## 顺序与返回值 {#ordering-and-returned-values}
|
||||
|
||||
@@ -581,7 +581,7 @@ Creation request
|
||||
→ resolve defaults and validate provider configuration
|
||||
→ derive launch configuration with Paseo runtime tools and daemon prompt
|
||||
→ agent.open.before hooks (same ordering; env and plugin-local tools and MCP servers)
|
||||
→ set PASEO_AGENT_ID and PASEO_AGENT_CWD
|
||||
→ set PASEO_AGENT_ID, PASEO_AGENT_CWD, and PASEO_WORKSPACE_ID when the agent has a workspace
|
||||
→ open provider session and save agent configuration
|
||||
```
|
||||
|
||||
|
||||
@@ -73,6 +73,7 @@ Use this table as the complete registration checklist.
|
||||
| `plugin.addSurface(id, Component)` in the old root entry | `client.addScreen({ id, title, Component })` in `index.client.tsx` |
|
||||
| `plugin.addSidebarItem({ id, title, icon, surface })` in the old root entry | `client.addSidebarHeaderItem({ id, title, Component })` in `index.client.tsx`; see [sidebar items](reference#sidebar-items) |
|
||||
| New sidebar footer contribution | `client.addSidebarFooterItem({ id, title, Component })` in `index.client.tsx` |
|
||||
| New sidebar replacement view | `client.addSidebarView({ id, title, Component })` in `index.client.tsx`; see [sidebar views](sidebar-views) |
|
||||
| New file panel contribution | `client.addFilePanel(panel)` in `index.client.tsx` |
|
||||
| `plugin.addWorkspacePanel(panel)` in the old root entry | `client.addWorkspacePanel(panel)` in `index.client.tsx` |
|
||||
| Agent panel with `context: "agent"` | `client.addAgentPanel(panel)` in `index.client.tsx`; omit `context` |
|
||||
@@ -144,8 +145,8 @@ pill.remove();
|
||||
```
|
||||
|
||||
Move dynamic text from the former component into updates from your model or SDK subscription.
|
||||
Custom icon components may still use hooks. Composer pills always show icon and label and never
|
||||
show chevrons. See [buttons](./reference.md#button-descriptor) for menus, popovers, and visibility.
|
||||
Custom icon components may still use hooks. Composer pills always show icon and label; menus and
|
||||
popovers add a chevron. See [buttons](./reference.md#button-descriptor) for menus, popovers, and visibility.
|
||||
|
||||
## 4. Separate imports
|
||||
|
||||
|
||||
@@ -287,6 +287,11 @@ Paseo refreshes an agent by closing its current provider session and opening it
|
||||
configuration and persistence. Re-read credentials, environment, global configuration, and MCP
|
||||
servers during `session.open`. There is no separate reload operation.
|
||||
|
||||
Conversation and combined rewind require a core-restored session with persistence. After a
|
||||
successful rewind, Paseo closes the session and reopens its latest persistence value with
|
||||
`history: "replay"`. Publish the retained native branch before `session.ready`, including its
|
||||
children. File-only rewind keeps the current session and conversation history.
|
||||
|
||||
Use `restoration: "core"` when Paseo can reopen a session from persistence. A provider-owned child
|
||||
uses `restoration: "parent"`; emit it as another `session.opened` event with `parentSessionId` and
|
||||
recreate it while restoring the parent.
|
||||
|
||||
@@ -73,16 +73,16 @@ The required root manifest is `paseo-plugin.json`:
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Required | Behavior |
|
||||
| -------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `id` | Yes | Default installation ID. |
|
||||
| `name` | No | Non-empty display name for the registry and website; independent of the installation ID. |
|
||||
| `icon` | No | Relative path to a PNG inside the plugin package. |
|
||||
| `media` | No | Array of image or video paths inside the package, or HTTPS URLs. An empty array is valid. |
|
||||
| `description` | No | Non-empty summary shown below the plugin ID in **Settings → Plugins**. |
|
||||
| `translations` | No | Listing text in other languages, read by the registry and ignored by Paseo. See [other languages](/docs/plugins/publishing#other-languages). |
|
||||
| `requirements` | No | Supported Paseo versions, described below. |
|
||||
| `build` | No | Preparation commands, described in the CLI reference. |
|
||||
| Field | Required | Behavior |
|
||||
| -------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `id` | Yes | Default installation ID. |
|
||||
| `name` | No | Non-empty display name for the registry, the website, your row in **Settings → Plugins**, and your settings header. Without it, Paseo shows the ID. |
|
||||
| `icon` | No | Relative path to a PNG inside the plugin package. |
|
||||
| `media` | No | Array of image or video paths inside the package, or HTTPS URLs. An empty array is valid. |
|
||||
| `description` | No | Non-empty summary shown below the plugin name in **Settings → Plugins**. |
|
||||
| `translations` | No | Listing text in other languages, read by the registry and ignored by Paseo. See [other languages](/docs/plugins/publishing#other-languages). |
|
||||
| `requirements` | No | Supported Paseo versions, described below. |
|
||||
| `build` | No | Preparation commands, described in the CLI reference. |
|
||||
|
||||
Paths are relative to `paseo-plugin.json`, use forward slashes, and cannot contain `..`
|
||||
segments. Registry listings for visual plugins require an interface image even though `media` is
|
||||
@@ -641,6 +641,13 @@ Cleanup can be async. Release timers, watchers, sockets, and other resources cre
|
||||
|
||||
Register async handlers with `server.addHook(name, handler)`. See [Hooks](/docs/plugins/hooks) for names, payloads, ordering, errors, and complete examples.
|
||||
|
||||
## Sidebar views
|
||||
|
||||
`addSidebarView({ id, title, description?, Component })` registers an optional replacement for the
|
||||
sidebar content region. It returns cleanup. The user selects one view in Settings > Sidebar;
|
||||
registration does not activate it. See [Build a sidebar view](sidebar-views.md) for the props,
|
||||
ref-based queries and actions, device state and theme tokens.
|
||||
|
||||
## Screens and sidebar items
|
||||
|
||||
A screen is a full page of plugin UI. A sidebar item is a component Paseo renders in the sidebar
|
||||
@@ -1309,8 +1316,11 @@ Themes need a host that supports them. A client released before `addTheme` canno
|
||||
## Settings screens
|
||||
|
||||
Register a component with `client.addSettingsScreen({ id, title, icon, Component })` in
|
||||
`index.client.tsx`. It appears under **Settings → Plugins → your plugin** on that host.
|
||||
`id` is unique within the installation; `icon` is any [icon](#icons) form. Registration returns an
|
||||
`index.client.tsx`. Your plugin's row in **Settings → Plugins** on that host then shows a settings
|
||||
button (cog) that opens your first screen. When you register several screens, the settings page shows
|
||||
one tab per screen, labeled with its `title` and `icon`, in registration order. With one screen there
|
||||
are no tabs. The page header shows your manifest `name`, or your plugin ID when you leave `name`
|
||||
out; it never includes a screen title. `id` is unique within the installation; `icon` is any [icon](#icons) form. Registration returns an
|
||||
idempotent remover, and plugin teardown removes remaining screens.
|
||||
|
||||
Call `client.openSettings(id)` or a Command Center callback's `openSettings(id)` to open one
|
||||
@@ -1391,13 +1401,13 @@ export default function contribute(server: PluginServerContext) {
|
||||
}
|
||||
```
|
||||
|
||||
| Definition field | Contract |
|
||||
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `id` | Lowercase identifier matching `[a-z][a-z0-9_-]*`; unique within the installation. |
|
||||
| `scope` | Required `"host"`. All authorized clients of that host share the document. No per-user, device-local, or cross-host synchronization. |
|
||||
| `version` | Required positive integer describing the schema, independent of the write revision. |
|
||||
| `schema` | Zod schema for JSON values. Supply defaults so parsing `{}` produces a complete document. |
|
||||
| `migrate(values, fromVersion)` | Optional synchronous or asynchronous conversion from an older stored version. Its output must pass the current schema. |
|
||||
| Definition field | Contract |
|
||||
| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `id` | Lowercase identifier matching `[a-z][a-z0-9_-]*`; unique within the installation. |
|
||||
| `scope` | Required `"host"` or `"device"`. Host documents are shared by authorized clients. Device documents stay on this device, keyed by installation and settings ID; use them for layout preferences. |
|
||||
| `version` | Required positive integer describing the schema, independent of the write revision. |
|
||||
| `schema` | Zod schema for JSON values. Supply defaults so parsing `{}` produces a complete document. |
|
||||
| `migrate(values, fromVersion)` | Optional synchronous or asynchronous conversion from an older stored version. Its output must pass the current schema. |
|
||||
|
||||
Call `useSettings(preferences)` in any contributed component. It returns a discriminated state:
|
||||
|
||||
@@ -1904,7 +1914,10 @@ decisions.
|
||||
`client.addComposerPill({ id, workspaceId, agentId, button })` uses the same [button descriptor](#button-descriptor)
|
||||
and returns the same registration. It targets one agent's composer track alongside Tasks and
|
||||
Subagents. Composer pills always show the icon and `label` (or `title` when `label` is omitted).
|
||||
They never show a chevron, including for menus and popovers.
|
||||
Menus and popovers add an upward chevron on every layout, because their surface opens above the
|
||||
pill; actions show none, so a pill that runs on tap reads differently from one that opens something.
|
||||
The track keeps its space above the composer whether or not it has pills, so adding and removing
|
||||
pills does not move the transcript.
|
||||
|
||||
```tsx
|
||||
const pill = client.addComposerPill({
|
||||
@@ -2085,6 +2098,8 @@ function PullRequestAction({ theme }: PluginSurfaceProps) {
|
||||
|
||||
The returned API covers projects, workspaces, agents, terminals, providers, and daemon config. See the [SDK API reference](/docs/sdk/reference) for its methods. Connection lifecycle methods are intentionally absent because Paseo owns the connection.
|
||||
|
||||
To run a helper the user shouldn't see, such as a classifier or summarizer, create it as a [background agent](/docs/sdk/agents#run-a-helper-as-a-background-agent) in an existing workspace.
|
||||
|
||||
### Discover hosts and target another host
|
||||
|
||||
Use `useHosts()` to display configured hosts and `getPaseoClient(serverId)` in an action callback
|
||||
@@ -2229,10 +2244,168 @@ export default function contribute(server: PluginServerContext) {
|
||||
}
|
||||
```
|
||||
|
||||
Inputs and outputs are validated on both sides. RPC names start with a lowercase letter and contain lowercase letters, numbers, dots, hyphens, or underscores. `useRpc()` returns a typed async function. Use TanStack Query for request state, caching, and mutations.
|
||||
Inputs are parsed once in the plugin process; outputs are parsed once on the client. Send schema inputs from the client and return output-schema inputs from the handler; schema transforms run at the receiving side. RPC names start with a lowercase letter and contain lowercase letters, numbers, dots, hyphens, or underscores. `useRpc()` returns a typed async function. Use TanStack Query for request state, caching, and mutations.
|
||||
|
||||
Backend handlers receive the same `PaseoApi` as `{ paseo }`. Their connection belongs to the subprocess and closes when the plugin stops. It does not subscribe to timelines or catalog events until plugin code subscribes. Follow the [SDK event contract](/docs/sdk/events) for cleanup and timeline replacements. Backend code can use Node APIs and dependencies installed in the plugin directory.
|
||||
|
||||
## Stream events from the daemon {#subscriptions}
|
||||
|
||||
A subscription runs a daemon handler for as long as one client listens and sends that client typed
|
||||
events. Use it for watchers, tails, and live media; use plugin RPC for a single answer.
|
||||
|
||||
Define the contract in `shared/`:
|
||||
|
||||
```ts
|
||||
// shared/diff.ts
|
||||
import { defineSubscription } from "@getpaseo/plugin";
|
||||
import { z } from "zod";
|
||||
|
||||
export const diffWatch = defineSubscription({
|
||||
name: "diff.watch",
|
||||
input: z.object({ cwd: z.string(), from: z.string() }),
|
||||
event: z.object({ type: z.literal("changes"), files: z.array(z.string()) }),
|
||||
});
|
||||
```
|
||||
|
||||
Handle it in `index.server.ts`. The handler receives `push` and an abort `signal` next to `paseo`:
|
||||
|
||||
```ts
|
||||
server.handle(diffWatch, ({ cwd, from }, { push, signal }) => {
|
||||
const watcher = watchChanges(cwd, from, (files) => void push({ type: "changes", files }));
|
||||
signal.addEventListener("abort", () => watcher.close(), { once: true });
|
||||
});
|
||||
```
|
||||
|
||||
Subscribe from a surface with `useSubscription(contract, input, onEvent)`:
|
||||
|
||||
```tsx
|
||||
const [files, setFiles] = useState<string[]>([]);
|
||||
const subscription = useSubscription(diffWatch, { cwd, from: "HEAD" }, (event) => {
|
||||
setFiles(event.files);
|
||||
});
|
||||
if (subscription.status === "error") return <Text>{subscription.error.message}</Text>;
|
||||
```
|
||||
|
||||
Outside React, `client.subscribe` takes the same arguments plus options and returns a remover:
|
||||
|
||||
```ts
|
||||
const stop = client.subscribe(diffWatch, { cwd, from: "HEAD" }, onChanges, { signal });
|
||||
await stop.ready; // the daemon accepted the subscription
|
||||
stop(); // or await stop.release()
|
||||
```
|
||||
|
||||
RPCs and subscriptions share one name space; registering the same name for both fails the plugin
|
||||
load.
|
||||
|
||||
### Handler lifetime
|
||||
|
||||
| The handler... | Result |
|
||||
| --------------------------------------------- | ------------------------------------------------------------ |
|
||||
| Throws synchronously, or the input is invalid | The subscribe call fails with the error. No events are sent. |
|
||||
| Returns nothing | The subscription stays open until the client releases it. |
|
||||
| Returns a promise that resolves | The subscription closes. |
|
||||
| Returns a promise that rejects | The subscription closes with the error. |
|
||||
|
||||
`signal` aborts when the client releases the subscription, unmounts the hook, changes its input,
|
||||
disconnects, or the plugin stops. Pushes after the abort are dropped. `push` validates the event
|
||||
against the contract and resolves once the event has left the plugin process; await it in a loop
|
||||
to pace a producer.
|
||||
|
||||
Each subscriber runs its own handler. Paseo does not share one handler between clients and does
|
||||
not replay missed events: after a reconnect or a plugin reload, mounted surfaces subscribe again and
|
||||
the handler starts from the beginning. Share an
|
||||
expensive watcher inside your plugin when several subscribers need it.
|
||||
|
||||
`useSubscription` returns a union on `status`:
|
||||
|
||||
| Status | Meaning |
|
||||
| ------------ | ---------------------------------------------------------------------------------------------- |
|
||||
| `connecting` | Sent; the daemon has not accepted it yet. |
|
||||
| `live` | The handler is running. Events arrive through `onEvent`. |
|
||||
| `closed` | The handler finished. |
|
||||
| `error` | The subscribe failed, the handler failed, an event failed the contract, or the plugin stopped. |
|
||||
|
||||
The hook subscribes again when `input` changes by shallow equality. `onEvent` is always the latest
|
||||
render's callback, so an inline function does not resubscribe. `client.subscribe` takes
|
||||
`{ signal, onClosed }`; `onClosed` receives `{ error? }`.
|
||||
|
||||
### Bytes
|
||||
|
||||
A top-level `Uint8Array` crosses the wire as binary, without JSON or base64. Everything else is
|
||||
JSON. The rule applies to subscription events, RPC outputs, and RPC inputs. A Node `Buffer` is a
|
||||
`Uint8Array`.
|
||||
|
||||
```ts
|
||||
export const thumbnail = defineRpc({
|
||||
name: "thumbnail.render",
|
||||
input: z.object({ path: z.string(), width: z.number() }),
|
||||
output: z.instanceof(Uint8Array),
|
||||
});
|
||||
|
||||
// index.server.ts
|
||||
server.handle(thumbnail, ({ path, width }) => renderPng(path, width));
|
||||
|
||||
// client
|
||||
const renderThumbnail = useRpc(thumbnail);
|
||||
const png = await renderThumbnail({ path, width }); // Uint8Array
|
||||
```
|
||||
|
||||
Declare bytes with `z.instanceof(Uint8Array)`. An event can be bytes or JSON per push: declare a
|
||||
union and branch with `event instanceof Uint8Array`. A `Uint8Array` nested in an object fails
|
||||
validation on the sending side with an error that names the rule; send the bytes as their own
|
||||
event or RPC value and the metadata as a separate JSON event.
|
||||
|
||||
One event or RPC value carries at most 100 MiB less 65 KiB of framing. Push large payloads in
|
||||
chunks of a few hundred KiB; each `push` waits for the previous one to leave the plugin process.
|
||||
Uploads from a phone have a lower limit: Android closes the connection when one send exceeds
|
||||
16 MiB, so split a larger upload into several RPCs.
|
||||
|
||||
### Several streams on one subscription
|
||||
|
||||
Put a header byte in front of each payload to multiplex streams. Keep the layout in `shared/`, so
|
||||
the handler and the surface call named functions:
|
||||
|
||||
```ts
|
||||
// shared/media-frames.ts
|
||||
export const MediaOpcode = { Video: 0x01, Audio: 0x02 } as const;
|
||||
|
||||
export function encodeMediaFrame(opcode: number, payload: Uint8Array): Uint8Array {
|
||||
const bytes = new Uint8Array(1 + payload.byteLength);
|
||||
bytes[0] = opcode;
|
||||
bytes.set(payload, 1);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
export function decodeMediaFrame(bytes: Uint8Array) {
|
||||
return { opcode: bytes[0], payload: bytes.subarray(1) }; // a view, not a copy
|
||||
}
|
||||
|
||||
export const mediaStream = defineSubscription({
|
||||
name: "media.stream",
|
||||
input: z.object({ source: z.string() }),
|
||||
event: z.instanceof(Uint8Array),
|
||||
});
|
||||
```
|
||||
|
||||
```ts
|
||||
useSubscription(mediaStream, { source }, (bytes) => {
|
||||
const frame = decodeMediaFrame(bytes);
|
||||
if (frame.opcode === MediaOpcode.Audio) audio.enqueue(frame.payload);
|
||||
else video.draw(frame.payload);
|
||||
});
|
||||
```
|
||||
|
||||
Events from one subscription arrive in push order, JSON and binary alike.
|
||||
`plugin-examples/media-stream` streams video and audio this way and uploads a frame back through
|
||||
an RPC with a `Uint8Array` input; `plugin-examples/diff-watch` watches a working tree.
|
||||
|
||||
### Older hosts
|
||||
|
||||
Subscriptions and binary RPC values need a host that advertises them. Against an older host,
|
||||
`useSubscription` reports `error` asking to update the host, `client.subscribe` rejects `ready`
|
||||
with the same message, and an RPC with a `Uint8Array` input or output fails. Raise your plugin's
|
||||
`requirements.paseo` to the release that ships subscriptions.
|
||||
|
||||
## Debug backend output
|
||||
|
||||
Backend contributions can write to stdout and stderr with normal Node logging:
|
||||
|
||||
@@ -73,10 +73,10 @@ my-plugin/
|
||||
| 字段 | 是否必填 | 行为 |
|
||||
| -------------- | -------- | ---------------------------------------------------------------------------------------------------------- |
|
||||
| `id` | 是 | 默认的安装 ID。 |
|
||||
| `name` | 否 | 注册表和网站使用的非空显示名称;与安装 ID 无关。 |
|
||||
| `name` | 否 | 注册表、网站、**设置 → 插件** 中的插件行以及插件设置页标题使用的非空显示名称。没有 `name` 时显示插件 ID。 |
|
||||
| `icon` | 否 | 插件包内 PNG 文件的相对路径。 |
|
||||
| `media` | 否 | 包内图片或视频路径的数组,或 HTTPS URL。空数组是有效的。 |
|
||||
| `description` | 否 | 在 **设置 → 插件** 中显示在插件 ID 下方的非空摘要。 |
|
||||
| `description` | 否 | 在 **设置 → 插件** 中显示在插件名称下方的非空摘要。 |
|
||||
| `translations` | 否 | 其他语言的展示文本,由注册表读取,Paseo 会忽略它。见[其他语言](/docs/plugins/publishing#other-languages)。 |
|
||||
| `requirements` | 否 | 支持的 Paseo 版本,见下文。 |
|
||||
| `build` | 否 | 准备命令,见 CLI 参考。 |
|
||||
@@ -510,6 +510,10 @@ export default function contribute(client: PluginClientContext) {
|
||||
|
||||
使用 `server.addHook(name, handler)` 注册异步处理函数。请参阅[钩子](/docs/plugins/hooks),了解命名、载荷、顺序、错误处理和完整示例。
|
||||
|
||||
## 侧边栏视图 {#sidebar-views}
|
||||
|
||||
`addSidebarView({ id, title, description?, Component })` 注册可替换侧边栏内容区域的视图,并返回清理函数。用户在设置的侧边栏页面中选择视图;注册不会激活它。视图属性、基于引用的查询与操作、设备状态和主题令牌见[构建侧边栏视图](sidebar-views.md)。
|
||||
|
||||
## 界面和侧边栏项 {#screens-and-sidebar-items}
|
||||
|
||||
界面是一整页的插件 UI。侧边栏项是 Paseo 在侧边栏顶部或底部渲染的组件;按下它时做什么由它决定。先注册界面,再注册一个打开它的项:
|
||||
@@ -1087,7 +1091,7 @@ export default function contribute(client: PluginClientContext) {
|
||||
|
||||
## 设置界面 {#settings-screens}
|
||||
|
||||
在 `index.client.tsx` 中用 `client.addSettingsScreen({ id, title, icon, Component })` 注册一个组件。它会出现在该主机的 **设置 → 插件 → 你的插件** 下。`id` 在同一次安装中唯一;`icon` 可以是任意一种[图标](#icons)形式。注册返回一个幂等的移除函数,插件销毁时会移除剩余的界面。
|
||||
在 `index.client.tsx` 中用 `client.addSettingsScreen({ id, title, icon, Component })` 注册一个组件。之后,该主机 **设置 → 插件** 中你的插件行会显示一个设置按钮(齿轮),点击后打开你的第一个界面。注册多个界面时,设置页面会按注册顺序为每个界面显示一个标签页,使用其 `title` 和 `icon`;只有一个界面时不显示标签页。页面标题显示 manifest 中的 `name`;没有 `name` 时显示插件 ID,且从不包含界面标题。`id` 在同一次安装中唯一;`icon` 可以是任意一种[图标](#icons)形式。注册返回一个幂等的移除函数,插件销毁时会移除剩余的界面。
|
||||
|
||||
调用 `client.openSettings(id)` 或 Command Center 回调中的 `openSettings(id)` 可以打开你自己的某个界面。即使多台主机安装了同一个插件,每次安装也有各自的值和路由。
|
||||
|
||||
@@ -1157,13 +1161,13 @@ export default function contribute(server: PluginServerContext) {
|
||||
}
|
||||
```
|
||||
|
||||
| 定义字段 | 契约 |
|
||||
| ------------------------------ | --------------------------------------------------------------------------------------------- |
|
||||
| `id` | 匹配 `[a-z][a-z0-9_-]*` 的小写标识符;在同一次安装中唯一。 |
|
||||
| `scope` | 必须为 `"host"`。该主机所有获得授权的客户端共享这份文档。没有按用户、设备本地或跨主机的同步。 |
|
||||
| `version` | 必填的正整数,描述 schema 的版本,与写入修订号无关。 |
|
||||
| `schema` | JSON 值的 Zod schema。请提供默认值,使解析 `{}` 能得到一份完整的文档。 |
|
||||
| `migrate(values, fromVersion)` | 可选的同步或异步函数,从较旧的存储版本转换。它的输出必须通过当前的 schema。 |
|
||||
| 定义字段 | 契约 |
|
||||
| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------- |
|
||||
| `id` | 匹配 `[a-z][a-z0-9_-]*` 的小写标识符;在同一次安装中唯一。 |
|
||||
| `scope` | 必填 `"host"` 或 `"device"`。主机文档由授权客户端共享。设备文档以插件安装和设置 ID 为键保存在当前设备上,用于布局偏好。 |
|
||||
| `version` | 必填的正整数,描述 schema 的版本,与写入修订号无关。 |
|
||||
| `schema` | JSON 值的 Zod schema。请提供默认值,使解析 `{}` 能得到一份完整的文档。 |
|
||||
| `migrate(values, fromVersion)` | 可选的同步或异步函数,从较旧的存储版本转换。它的输出必须通过当前的 schema。 |
|
||||
|
||||
在任何贡献的组件中调用 `useSettings(preferences)`。它返回一个可区分的状态:
|
||||
|
||||
@@ -1779,6 +1783,8 @@ function PullRequestAction({ theme }: PluginSurfaceProps) {
|
||||
|
||||
返回的 API 涵盖项目、工作区、Agent、终端、Provider 和 daemon 配置。其方法请参阅 [SDK API 参考](/docs/sdk/reference)。这里刻意没有连接生命周期方法,因为连接由 Paseo 负责。
|
||||
|
||||
如果要运行用户不需要看到的辅助任务,例如分类或摘要,请在已有工作区中把它创建为[后台 Agent](/docs/sdk/agents#run-a-helper-as-a-background-agent)。
|
||||
|
||||
### 发现主机并指定另一台主机 {#discover-hosts-and-target-another-host}
|
||||
|
||||
使用 `useHosts()` 显示已配置的主机,并在操作回调中使用 `getPaseoClient(serverId)` 对其中一台主机
|
||||
@@ -1924,6 +1930,140 @@ export default function contribute(server: PluginServerContext) {
|
||||
|
||||
后端处理函数以 `{ paseo }` 的形式收到同样的 `PaseoApi`。它们的连接属于子进程,并在插件停止时关闭。在插件代码订阅之前,它不会订阅时间线或目录事件。清理和时间线替换请遵循 [SDK 事件契约](/docs/sdk/events)。后端代码可以使用 Node API 以及安装在插件目录中的依赖。
|
||||
|
||||
## 从 daemon 推送事件 {#subscriptions}
|
||||
|
||||
订阅会在一个客户端监听期间运行 daemon 处理函数,并向该客户端发送带类型的事件。监听文件、追踪日志和实时媒体使用订阅;只需要一次应答时使用插件 RPC。
|
||||
|
||||
在 `shared/` 中定义契约:
|
||||
|
||||
```ts
|
||||
// shared/diff.ts
|
||||
import { defineSubscription } from "@getpaseo/plugin";
|
||||
import { z } from "zod";
|
||||
|
||||
export const diffWatch = defineSubscription({
|
||||
name: "diff.watch",
|
||||
input: z.object({ cwd: z.string(), from: z.string() }),
|
||||
event: z.object({ type: z.literal("changes"), files: z.array(z.string()) }),
|
||||
});
|
||||
```
|
||||
|
||||
在 `index.server.ts` 中处理它。处理函数除了 `paseo`,还会收到 `push` 和一个中止 `signal`:
|
||||
|
||||
```ts
|
||||
server.handle(diffWatch, ({ cwd, from }, { push, signal }) => {
|
||||
const watcher = watchChanges(cwd, from, (files) => void push({ type: "changes", files }));
|
||||
signal.addEventListener("abort", () => watcher.close(), { once: true });
|
||||
});
|
||||
```
|
||||
|
||||
在界面中用 `useSubscription(contract, input, onEvent)` 订阅:
|
||||
|
||||
```tsx
|
||||
const [files, setFiles] = useState<string[]>([]);
|
||||
const subscription = useSubscription(diffWatch, { cwd, from: "HEAD" }, (event) => {
|
||||
setFiles(event.files);
|
||||
});
|
||||
if (subscription.status === "error") return <Text>{subscription.error.message}</Text>;
|
||||
```
|
||||
|
||||
在 React 之外,`client.subscribe` 接受同样的参数和一个选项对象,并返回一个移除函数:
|
||||
|
||||
```ts
|
||||
const stop = client.subscribe(diffWatch, { cwd, from: "HEAD" }, onChanges, { signal });
|
||||
await stop.ready; // the daemon accepted the subscription
|
||||
stop(); // or await stop.release()
|
||||
```
|
||||
|
||||
RPC 和订阅共用一个名称空间;同一个名称同时注册为 RPC 和订阅会导致插件加载失败。
|
||||
|
||||
### 处理函数的生命周期 {#handler-lifetime}
|
||||
|
||||
| 处理函数…… | 结果 |
|
||||
| ------------------------ | -------------------------------------------- |
|
||||
| 同步抛出错误,或输入无效 | 订阅调用失败并返回该错误,不会发送任何事件。 |
|
||||
| 不返回任何值 | 订阅保持打开,直到客户端释放它。 |
|
||||
| 返回一个兑现的 Promise | 订阅关闭。 |
|
||||
| 返回一个拒绝的 Promise | 订阅关闭并带上该错误。 |
|
||||
|
||||
当客户端释放订阅、卸载 hook、更改输入、断开连接,或者插件停止时,`signal` 会中止。中止之后的推送会被丢弃。`push` 会按契约校验事件,并在事件离开插件进程后兑现;在循环中 await 它可以控制生产者的速度。
|
||||
|
||||
每个订阅者运行各自的处理函数。Paseo 不会在多个客户端之间共享同一个处理函数,也不会重放错过的事件:重新连接或重新加载插件之后,已挂载的界面会重新订阅,处理函数从头开始运行。多个订阅者需要同一个昂贵的监听器时,请在插件内部共享它。
|
||||
|
||||
`useSubscription` 返回一个以 `status` 区分的联合类型:
|
||||
|
||||
| 状态 | 含义 |
|
||||
| ------------ | -------------------------------------------------------- |
|
||||
| `connecting` | 已发送,daemon 尚未接受。 |
|
||||
| `live` | 处理函数正在运行,事件通过 `onEvent` 送达。 |
|
||||
| `closed` | 处理函数已结束。 |
|
||||
| `error` | 订阅失败、处理函数失败、事件不符合契约,或者插件已停止。 |
|
||||
|
||||
`input` 按浅比较发生变化时,hook 会重新订阅。`onEvent` 始终是最近一次渲染的回调,所以内联函数不会导致重新订阅。`client.subscribe` 接受 `{ signal, onClosed }`;`onClosed` 会收到 `{ error? }`。
|
||||
|
||||
### 字节 {#bytes}
|
||||
|
||||
顶层的 `Uint8Array` 以二进制形式传输,不经过 JSON 或 base64,其他值都是 JSON。这条规则适用于订阅事件、RPC 输出和 RPC 输入。Node 的 `Buffer` 也是 `Uint8Array`。
|
||||
|
||||
```ts
|
||||
export const thumbnail = defineRpc({
|
||||
name: "thumbnail.render",
|
||||
input: z.object({ path: z.string(), width: z.number() }),
|
||||
output: z.instanceof(Uint8Array),
|
||||
});
|
||||
|
||||
// index.server.ts
|
||||
server.handle(thumbnail, ({ path, width }) => renderPng(path, width));
|
||||
|
||||
// client
|
||||
const renderThumbnail = useRpc(thumbnail);
|
||||
const png = await renderThumbnail({ path, width }); // Uint8Array
|
||||
```
|
||||
|
||||
用 `z.instanceof(Uint8Array)` 声明字节。事件可以在每次推送时选择字节或 JSON:声明一个联合类型,并用 `event instanceof Uint8Array` 分支。嵌套在对象中的 `Uint8Array` 会在发送端校验失败,错误信息会说明这条规则;把字节作为单独的事件或 RPC 值发送,元数据作为另一个 JSON 事件发送。
|
||||
|
||||
一个事件或 RPC 值最多 100 MiB(减去 65 KiB 的帧头)。大载荷请按几百 KiB 分块推送;每次 `push` 都会等待上一次离开插件进程。手机上传的上限更低:一次发送超过 16 MiB 时 Android 会关闭连接,所以较大的上传请拆成多个 RPC。
|
||||
|
||||
### 在一个订阅中传输多条流 {#several-streams-on-one-subscription}
|
||||
|
||||
在每个载荷前加一个头字节即可复用多条流。把布局放在 `shared/` 中,让处理函数和界面调用有名字的函数:
|
||||
|
||||
```ts
|
||||
// shared/media-frames.ts
|
||||
export const MediaOpcode = { Video: 0x01, Audio: 0x02 } as const;
|
||||
|
||||
export function encodeMediaFrame(opcode: number, payload: Uint8Array): Uint8Array {
|
||||
const bytes = new Uint8Array(1 + payload.byteLength);
|
||||
bytes[0] = opcode;
|
||||
bytes.set(payload, 1);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
export function decodeMediaFrame(bytes: Uint8Array) {
|
||||
return { opcode: bytes[0], payload: bytes.subarray(1) }; // a view, not a copy
|
||||
}
|
||||
|
||||
export const mediaStream = defineSubscription({
|
||||
name: "media.stream",
|
||||
input: z.object({ source: z.string() }),
|
||||
event: z.instanceof(Uint8Array),
|
||||
});
|
||||
```
|
||||
|
||||
```ts
|
||||
useSubscription(mediaStream, { source }, (bytes) => {
|
||||
const frame = decodeMediaFrame(bytes);
|
||||
if (frame.opcode === MediaOpcode.Audio) audio.enqueue(frame.payload);
|
||||
else video.draw(frame.payload);
|
||||
});
|
||||
```
|
||||
|
||||
同一个订阅的事件按推送顺序到达,JSON 和二进制都一样。`plugin-examples/media-stream` 用这种方式传输视频和音频,并通过一个输入为 `Uint8Array` 的 RPC 把一帧上传回去;`plugin-examples/diff-watch` 监听工作树。
|
||||
|
||||
### 旧版主机 {#older-hosts}
|
||||
|
||||
订阅和二进制 RPC 值需要主机声明支持。连接旧版主机时,`useSubscription` 会报告 `error` 并提示更新主机,`client.subscribe` 的 `ready` 会以同样的信息拒绝,带有 `Uint8Array` 输入或输出的 RPC 会失败。请把插件的 `requirements.paseo` 提高到包含订阅功能的版本。
|
||||
|
||||
## 调试后端输出 {#debug-backend-output}
|
||||
|
||||
后端贡献可以用普通的 Node 日志写入 stdout 和 stderr:
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
# Build a sidebar view
|
||||
|
||||
A sidebar view replaces the content between Paseo's header and footer. You receive theme tokens,
|
||||
active entity refs and navigation methods, then render your own React Native UI. Install the
|
||||
[sidebar-views example](https://github.com/getpaseo/paseo/tree/main/plugin-examples/sidebar-views)
|
||||
to try projects, task-based agents, nested agents and label folders.
|
||||
|
||||
## Register a view
|
||||
|
||||
```tsx
|
||||
import type {
|
||||
PluginClientContext,
|
||||
PluginSidebarViewProps,
|
||||
WorkspaceRef,
|
||||
} from "@getpaseo/plugin/client";
|
||||
import { useWorkspaces, useWorkspace } from "@getpaseo/plugin/client";
|
||||
import { Pressable, ScrollView, Text } from "react-native";
|
||||
|
||||
function WorkspaceRow({
|
||||
reference,
|
||||
navigation,
|
||||
theme,
|
||||
}: {
|
||||
reference: WorkspaceRef;
|
||||
navigation: PluginSidebarViewProps["navigation"];
|
||||
theme: PluginSidebarViewProps["theme"];
|
||||
}) {
|
||||
const workspace = useWorkspace(reference);
|
||||
if (!workspace) return null;
|
||||
return (
|
||||
<Pressable onPress={() => navigation.openWorkspace(reference)}>
|
||||
<Text style={{ color: theme.colors.foreground }}>{workspace.title ?? workspace.name}</Text>
|
||||
</Pressable>
|
||||
);
|
||||
}
|
||||
function Workspaces({ navigation, theme }: PluginSidebarViewProps) {
|
||||
const workspaces = useWorkspaces({ orderBy: "name" });
|
||||
return (
|
||||
<ScrollView>
|
||||
{workspaces.map((reference) => (
|
||||
<WorkspaceRow
|
||||
key={reference.key}
|
||||
reference={reference}
|
||||
navigation={navigation}
|
||||
theme={theme}
|
||||
/>
|
||||
))}
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
export default function contribute(client: PluginClientContext) {
|
||||
return client.addSidebarView({ id: "workspaces", title: "My workspaces", Component: Workspaces });
|
||||
}
|
||||
```
|
||||
|
||||
Registration adds an option under **Settings > Sidebar > Sidebar view**. The user selects it;
|
||||
installing the plugin leaves the current view selected. Header navigation, plugin header items,
|
||||
the callout and footer stay visible. A missing or disabled view falls back to Default view.
|
||||
A view that throws shows a placeholder with **Use default view**.
|
||||
|
||||
The view renders once across connected hosts. `active.workspace` and `active.agent` are refs or
|
||||
`null`. Use `navigation.openWorkspace`, `openAgent`, `openTerminal` or `openProject` with a ref;
|
||||
`openProject` starts the new workspace flow. Navigation closes the compact sidebar. Call
|
||||
`dismiss()` after navigation you implement yourself.
|
||||
|
||||
## Query refs, read each row
|
||||
|
||||
`useWorkspaces`, `useAgents`, `useProjects` and `useTerminals` return immutable arrays of refs.
|
||||
A ref has `kind`, `hostId`, `id` and a cross-host `key`. The same entity keeps the same frozen
|
||||
ref for the app session. Pass it to `useWorkspace`, `useAgent`, `useProject` or `useTerminal`
|
||||
inside the row. A deleted entity returns `null`.
|
||||
|
||||
```tsx
|
||||
const projects = useProjects({ orderBy: "displayName" }); // Includes empty projects.
|
||||
const agents = useAgents({
|
||||
hosts: [hostId],
|
||||
where: (agent) => agent.workspace.key === workspaceRef.key,
|
||||
orderBy: ["lastActivityAt", "desc"],
|
||||
});
|
||||
const folders = useWorkspaces({
|
||||
groupBy: (workspace) => workspace.labels, // A workspace can appear in several folders.
|
||||
});
|
||||
```
|
||||
|
||||
Every query accepts `hosts` (server IDs), `where`, `orderBy` and `groupBy`. Omit `hosts` to query
|
||||
every connected host. `orderBy` accepts a snapshot field, `[field, "asc" | "desc"]`, or a
|
||||
comparator. Null fields sort last. Keep comparators cheap; they run when data changes.
|
||||
`groupBy` returns a string, `{ key, title?, color? }`, or an array of these. Grouped results have
|
||||
`key`, `title`, `color` and `items`. Groups follow first appearance after sorting; duplicate keys
|
||||
use the first title. An empty array puts an item in no group.
|
||||
|
||||
An unchanged ref sequence keeps its array identity. Groups keep their identity when their
|
||||
metadata and item sequence stay the same. A workspace status change updates that row without
|
||||
rendering the list again. A grouping change updates the affected groups. Inline query objects
|
||||
and functions work without managing subscriptions yourself.
|
||||
|
||||
Entity hooks accept a selector and optional equality function:
|
||||
|
||||
```tsx
|
||||
const workspace = useWorkspace(ref); // Interned snapshot, compared by identity.
|
||||
const name = useWorkspace(ref, (item) => ({ title: item.title })); // Shallow equality.
|
||||
const selected = useWorkspace(ref, selector, equals);
|
||||
```
|
||||
|
||||
Without a selector, custom equality receives snapshots. With a selector, it receives selections.
|
||||
Existing workspace panels can continue passing a host-local string ID to `useWorkspace` and
|
||||
`useAgent`. Prefer refs for a view spanning hosts. `useHosts()` retains its existing
|
||||
`serverId`, `label` and connection `status` fields.
|
||||
|
||||
Workspace snapshots include `project`, `pinnedAt`, `labels`, `background`, `branch`,
|
||||
`lastPromptAt`, `parentAgent` and `createdAt` (null on older hosts). Use `indicator` and the
|
||||
localized `indicatorLabel` to display the status Paseo shows. `status` remains the raw daemon
|
||||
status. Agent snapshots include their workspace and parent refs. Project snapshots provide
|
||||
`displayName`, `rootPath`, `kind` and `iconRevision`.
|
||||
|
||||
## List terminals
|
||||
|
||||
The terminals example selects a host, lists its terminals, focuses a terminal tab and closes a
|
||||
terminal through its handle. `useTerminals({ hosts: [hostId] })` requires a host advertising
|
||||
`terminalCollection`. A terminal reader throws an update-host error for an unsupported queried
|
||||
host; other entity hooks continue working. Keep host selection outside an error boundary so the
|
||||
user can select another host. Omit `hosts` to require the capability on every connected host.
|
||||
|
||||
Terminal snapshots contain `workspace`, `title`, `isScript` and `createdAt`. A terminal kept alive
|
||||
by a worker from before timestamp tracking has `createdAt: null`. Script ownership follows
|
||||
workspace script runtime entries. The host observation follows terminal creation, closure, title
|
||||
changes and reconnects, and releases when its last plugin subscriber leaves.
|
||||
|
||||
## Act on a ref
|
||||
|
||||
`usePaseo()` routes ref-based actions to the ref's host:
|
||||
|
||||
```tsx
|
||||
const paseo = usePaseo();
|
||||
await paseo.workspaces.ref(workspaceRef).setPinned(true);
|
||||
await paseo.workspaces.ref(workspaceRef).setLabel("Later", { assigned: true });
|
||||
await paseo.workspaces.ref(workspaceRef).setLabel("Inbox", { assigned: false });
|
||||
paseo.projects.ref(projectRef).open();
|
||||
await paseo.projects.ref(projectRef).createWorkspace();
|
||||
paseo.terminals.ref(terminalRef).focus();
|
||||
await paseo.terminals.ref(terminalRef).kill();
|
||||
```
|
||||
|
||||
Workspace and agent handles retain their existing methods. Assigning a new label creates it;
|
||||
assigning an existing label keeps its color. The label-folder example adds the destination
|
||||
before removing the source, so a failed assignment preserves the original folder. It owns the
|
||||
move gesture and error display.
|
||||
|
||||
## Keep layout choices on the device
|
||||
|
||||
Define a [settings document](reference.md#settings-screens) with `scope: "device"` for collapsed
|
||||
folders or manual order. `useSettings` reads and writes it through the same revision-based API
|
||||
as host settings. Device documents belong to the installation and settings ID, persist across
|
||||
app launches, and are shared across that installation's client surfaces. They are not synced
|
||||
to other devices. Use `scope: "host"` for data clients should share through the daemon.
|
||||
|
||||
## Match the app's density
|
||||
|
||||
Render with React Native primitives. The SDK exports no sidebar-view rows, sections, lists,
|
||||
badges or drag helpers. The existing header/footer item kit is a separate surface.
|
||||
|
||||
For a dense row use a minimum height of 28, horizontal padding of 12, a 12-point primary label
|
||||
with 16-point line height and an 11-point secondary label. Use these supplied tokens:
|
||||
|
||||
| Row element | Theme token |
|
||||
| -------------------------------- | ------------------------ |
|
||||
| Primary label | `colors.foreground` |
|
||||
| Secondary label, section heading | `colors.foregroundMuted` |
|
||||
| Selected row fill | `colors.surface2` |
|
||||
| Separator | `colors.border` |
|
||||
| Action or focus accent | `colors.accent` |
|
||||
| Success indicator | `colors.statusSuccess` |
|
||||
| Needs input indicator | `colors.statusWarning` |
|
||||
| Failure indicator | `colors.statusDanger` |
|
||||
|
||||
Give pressable rows an accessible label and show controls on touch layouts. The examples use
|
||||
only plugin-owned components and the tokens passed to the view.
|
||||
@@ -0,0 +1,135 @@
|
||||
# 构建侧边栏视图 {#build-a-sidebar-view}
|
||||
|
||||
侧边栏视图替换 Paseo 页眉与页脚之间的内容。插件接收主题令牌、当前实体引用和导航方法,使用 React Native 绘制自己的界面。安装[侧边栏视图示例](https://github.com/getpaseo/paseo/tree/main/plugin-examples/sidebar-views),体验项目列表、按任务分组的 Agent、嵌套 Agent 和标签文件夹。
|
||||
|
||||
## 注册视图 {#register-a-view}
|
||||
|
||||
```tsx
|
||||
import type {
|
||||
PluginClientContext,
|
||||
PluginSidebarViewProps,
|
||||
WorkspaceRef,
|
||||
} from "@getpaseo/plugin/client";
|
||||
import { useWorkspaces, useWorkspace } from "@getpaseo/plugin/client";
|
||||
import { Pressable, ScrollView, Text } from "react-native";
|
||||
|
||||
function WorkspaceRow({
|
||||
reference,
|
||||
navigation,
|
||||
theme,
|
||||
}: {
|
||||
reference: WorkspaceRef;
|
||||
navigation: PluginSidebarViewProps["navigation"];
|
||||
theme: PluginSidebarViewProps["theme"];
|
||||
}) {
|
||||
const workspace = useWorkspace(reference);
|
||||
if (!workspace) return null;
|
||||
return (
|
||||
<Pressable onPress={() => navigation.openWorkspace(reference)}>
|
||||
<Text style={{ color: theme.colors.foreground }}>{workspace.title ?? workspace.name}</Text>
|
||||
</Pressable>
|
||||
);
|
||||
}
|
||||
function Workspaces({ navigation, theme }: PluginSidebarViewProps) {
|
||||
const workspaces = useWorkspaces({ orderBy: "name" });
|
||||
return (
|
||||
<ScrollView>
|
||||
{workspaces.map((reference) => (
|
||||
<WorkspaceRow
|
||||
key={reference.key}
|
||||
reference={reference}
|
||||
navigation={navigation}
|
||||
theme={theme}
|
||||
/>
|
||||
))}
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
export default function contribute(client: PluginClientContext) {
|
||||
return client.addSidebarView({ id: "workspaces", title: "My workspaces", Component: Workspaces });
|
||||
}
|
||||
```
|
||||
|
||||
注册后,用户可以在**设置 → 侧边栏 → 侧边栏视图**中选择该视图。安装插件不会切换当前视图。页眉导航、插件页眉项、提示区域和页脚保持可见。所选视图缺失或被禁用时,显示默认视图。视图抛出异常时,显示带有**使用默认视图**按钮的占位界面。
|
||||
|
||||
视图同时展示所有已连接主机的数据。`active.workspace` 和 `active.agent` 是引用或 `null`。将引用传给 `navigation.openWorkspace`、`openAgent`、`openTerminal` 或 `openProject`;`openProject` 打开新建工作区流程。导航会关闭紧凑布局的侧边栏。如果插件自行执行导航,完成后调用 `dismiss()`。
|
||||
|
||||
## 查询引用,逐行读取 {#query-refs-read-each-row}
|
||||
|
||||
`useWorkspaces`、`useAgents`、`useProjects` 和 `useTerminals` 返回不可变的引用数组。引用包含 `kind`、`hostId`、`id` 和跨主机唯一的 `key`。在一次应用会话中,同一实体使用同一个冻结引用。在行组件内将它传给 `useWorkspace`、`useAgent`、`useProject` 或 `useTerminal`。实体删除后返回 `null`。
|
||||
|
||||
```tsx
|
||||
const projects = useProjects({ orderBy: "displayName" }); // Includes empty projects.
|
||||
const agents = useAgents({
|
||||
hosts: [hostId],
|
||||
where: (agent) => agent.workspace.key === workspaceRef.key,
|
||||
orderBy: ["lastActivityAt", "desc"],
|
||||
});
|
||||
const folders = useWorkspaces({
|
||||
groupBy: (workspace) => workspace.labels, // A workspace can appear in several folders.
|
||||
});
|
||||
```
|
||||
|
||||
每个查询都支持 `hosts`(服务器 ID)、`where`、`orderBy` 和 `groupBy`。省略 `hosts` 时查询所有已连接主机。`orderBy` 接受快照字段、`[field, "asc" | "desc"]` 或比较函数。空值排在最后。比较函数会在数据变化时执行,应保持开销较小。
|
||||
|
||||
`groupBy` 返回字符串、`{ key, title?, color? }` 或这些值组成的数组。分组结果包含 `key`、`title`、`color` 和 `items`。分组按排序后首次出现的顺序排列;重复的键使用第一个标题。返回空数组表示该项不属于任何分组。
|
||||
|
||||
引用序列不变时,数组对象保持不变。分组的元数据和项序列不变时,分组对象保持不变。工作区状态变化只更新对应行,不会重新渲染整个列表。分组变化会更新受影响的分组。查询对象和函数可以内联编写,无需自行管理订阅。
|
||||
|
||||
实体钩子支持选择器和可选的相等比较函数:
|
||||
|
||||
```tsx
|
||||
const workspace = useWorkspace(ref); // Interned snapshot, compared by identity.
|
||||
const name = useWorkspace(ref, (item) => ({ title: item.title })); // Shallow equality.
|
||||
const selected = useWorkspace(ref, selector, equals);
|
||||
```
|
||||
|
||||
没有选择器时,自定义相等比较函数接收快照;有选择器时接收选择结果。现有工作区面板仍可向 `useWorkspace` 和 `useAgent` 传入主机本地的字符串 ID。跨主机视图应使用引用。`useHosts()` 保留现有的 `serverId`、`label` 和连接 `status` 字段。
|
||||
|
||||
工作区快照包含 `project`、`pinnedAt`、`labels`、`background`、`branch`、`lastPromptAt`、`parentAgent` 和 `createdAt`(旧主机上为 null)。使用 `indicator` 和本地化的 `indicatorLabel` 显示 Paseo 的状态。`status` 保持 daemon 原始状态。Agent 快照包含工作区和父 Agent 引用。项目快照提供 `displayName`、`rootPath`、`kind` 和 `iconRevision`。
|
||||
|
||||
## 列出终端 {#list-terminals}
|
||||
|
||||
终端示例支持选择主机、列出终端、聚焦终端标签页,以及通过句柄关闭终端。`useTerminals({ hosts: [hostId] })` 要求主机声明 `terminalCollection` 能力。查询不支持该能力的主机时,终端读取会抛出要求更新主机的错误;其他实体钩子继续工作。将主机选择器放在错误边界之外,方便用户切换主机。省略 `hosts` 时,所有已连接主机都必须支持该能力。
|
||||
|
||||
终端快照包含 `workspace`、`title`、`isScript` 和 `createdAt`。在时间戳跟踪引入前启动的 worker 所保留的终端,其 `createdAt` 为 null。脚本归属来自工作区脚本运行时条目。主机观察跟踪终端创建、关闭、标题变化和重新连接,并在最后一个终端读取订阅者离开时释放。
|
||||
|
||||
## 通过引用执行操作 {#act-on-a-ref}
|
||||
|
||||
`usePaseo()` 将基于引用的操作发送到引用所属的主机:
|
||||
|
||||
```tsx
|
||||
const paseo = usePaseo();
|
||||
await paseo.workspaces.ref(workspaceRef).setPinned(true);
|
||||
await paseo.workspaces.ref(workspaceRef).setLabel("Later", { assigned: true });
|
||||
await paseo.workspaces.ref(workspaceRef).setLabel("Inbox", { assigned: false });
|
||||
paseo.projects.ref(projectRef).open();
|
||||
await paseo.projects.ref(projectRef).createWorkspace();
|
||||
paseo.terminals.ref(terminalRef).focus();
|
||||
await paseo.terminals.ref(terminalRef).kill();
|
||||
```
|
||||
|
||||
工作区和 Agent 句柄保留现有方法。分配新标签会创建该标签;分配已有标签会保留其颜色。标签文件夹示例先添加目标标签,再移除源标签,因此分配失败时仍保留原文件夹。插件负责移动手势和错误显示。
|
||||
|
||||
## 在设备上保存布局选择 {#keep-layout-choices-on-the-device}
|
||||
|
||||
定义 `scope: "device"` 的[设置文档](reference.md#settings-screens),保存折叠文件夹或手动排序。`useSettings` 通过与主机设置相同的修订版本 API 读取和写入。设备文档属于插件安装和设置 ID,在应用重启后保留,并由该安装的客户端界面共享。它们不会同步到其他设备。需要由客户端通过 daemon 共享的数据使用 `scope: "host"`。
|
||||
|
||||
## 匹配应用的信息密度 {#match-the-apps-density}
|
||||
|
||||
使用 React Native 原语绘制界面。SDK 不导出侧边栏视图的行、分区、列表、徽标或拖拽辅助组件。现有页眉和页脚组件包属于独立界面。
|
||||
|
||||
紧凑行的最小高度为 28,水平内边距为 12;主标签字号为 12、行高为 16,次标签字号为 11。使用以下主题令牌:
|
||||
|
||||
| 行元素 | 主题令牌 |
|
||||
| ---------------- | ------------------------ |
|
||||
| 主标签 | `colors.foreground` |
|
||||
| 次标签、分区标题 | `colors.foregroundMuted` |
|
||||
| 选中行背景 | `colors.surface2` |
|
||||
| 分隔线 | `colors.border` |
|
||||
| 操作或焦点强调色 | `colors.accent` |
|
||||
| 成功指示 | `colors.statusSuccess` |
|
||||
| 需要输入指示 | `colors.statusWarning` |
|
||||
| 失败指示 | `colors.statusDanger` |
|
||||
|
||||
为可点击行提供无障碍标签,并在触摸布局中显示操作控件。示例仅使用插件自己的组件和传给视图的主题令牌。
|
||||
@@ -130,6 +130,29 @@ use `client.agents.list({ filter: { includeBackground: true } })` to include the
|
||||
Exact-ID handles work normally. See [workspace creation](./workspaces.md#create-a-fresh-workspace)
|
||||
for defaults and caller inheritance.
|
||||
|
||||
## Run a helper as a background agent
|
||||
|
||||
Pass `background: true` to run a helper in a workspace the user may have open. A background agent
|
||||
opens no tab, stays out of its parent's subagent track and the sidebar status, and raises no
|
||||
notifications. Default lists leave it out.
|
||||
|
||||
```ts
|
||||
const helper = await client.workspaces.ref(workspaceId).agents.create({
|
||||
config: { provider: "codex/gpt-5.5" },
|
||||
prompt: "Summarize the last turn in one line.",
|
||||
background: true,
|
||||
});
|
||||
const result = await helper.waitForFinish();
|
||||
console.log(result.lastMessage);
|
||||
await helper.archive();
|
||||
```
|
||||
|
||||
An agent created under a background `parent` is background unless you pass `background: false`.
|
||||
A parent on another host passes this on only as a handle; with a bare address, pass `background`.
|
||||
Placing a background agent in an existing workspace or under a parent requires
|
||||
`features.backgroundAgents`; on an older host the SDK throws "Update the host to use background
|
||||
agents." before sending.
|
||||
|
||||
## Request structured output
|
||||
|
||||
```ts
|
||||
|
||||
@@ -124,6 +124,23 @@ const workspace = await client.workspaces.create({
|
||||
|
||||
后台 Agent 会保留历史记录,并在 daemon 重启后继续存在。默认的发现结果会隐藏它们;使用 `client.agents.list({ filter: { includeBackground: true } })` 可以包含它们。精确 ID 的句柄可以正常使用。默认值和调用方继承规则见[创建工作区](./workspaces.md#create-a-fresh-workspace)。
|
||||
|
||||
## 以后台 Agent 运行辅助任务 {#run-a-helper-as-a-background-agent}
|
||||
|
||||
传入 `background: true`,可以在用户可能正打开的工作区中运行辅助 Agent。后台 Agent 不会打开标签页,不会出现在父 Agent 的子 Agent 列表和侧边栏状态中,也不会触发通知。默认列表不包含它。
|
||||
|
||||
```ts
|
||||
const helper = await client.workspaces.ref(workspaceId).agents.create({
|
||||
config: { provider: "codex/gpt-5.5" },
|
||||
prompt: "Summarize the last turn in one line.",
|
||||
background: true,
|
||||
});
|
||||
const result = await helper.waitForFinish();
|
||||
console.log(result.lastMessage);
|
||||
await helper.archive();
|
||||
```
|
||||
|
||||
在后台 `parent` 下创建的 Agent 默认也是后台 Agent,除非传入 `background: false`。位于其他主机的父 Agent 只有以句柄传入时才会传递该设置;只传地址时,请显式传入 `background`。把后台 Agent 放到已有工作区或某个父 Agent 下需要 `features.backgroundAgents`;在较旧的主机上,SDK 会在发送前抛出 "Update the host to use background agents."。
|
||||
|
||||
## 请求结构化输出 {#request-structured-output}
|
||||
|
||||
```ts
|
||||
|
||||
@@ -42,6 +42,25 @@ await directory.subscription.release();
|
||||
|
||||
`client.agents.subscribe()` and agent-handle `subscribe()` add local listeners to observations owned by that API instance. They do not request data. Use the returned subscription's callbacks when multiple filtered views need separate updates.
|
||||
|
||||
## Follow an agent's background work
|
||||
|
||||
Background work is what an agent left running after its tool call returned: a Claude shell started with `run_in_background`, a `Monitor` watch, or another task that is not a subagent. Subagents and workflows stay in the agent's subagents.
|
||||
|
||||
```ts
|
||||
const agent = client.agents.ref(agentId);
|
||||
|
||||
const unsubscribe = agent.backgroundWork.subscribe((items) => render(items));
|
||||
|
||||
// When this view closes:
|
||||
unsubscribe();
|
||||
```
|
||||
|
||||
`subscribe()` calls the listener with the current list once the subscription is established, again after each reconnect, then with the whole list on every change. Use `list()` for a one-shot read. Each item has `id`, `kind` (`"shell"`, `"monitor"`, or `"other"`; treat unknown values as `"other"`), `description` (or `null`), optional `command`, and `startedAt`, the time Paseo first observed it. Inventory is scoped to the current provider session and is not persisted. When its runtime is lost, its old items are discarded. An unavailable inventory is reported as an error rather than an empty list.
|
||||
|
||||
Pass `subscribe(listener, { onError })` to receive setup or list errors. After a setup failure, create a new subscription; after a list failure, the list is read again on the next reconnect. Without `onError`, the error is logged.
|
||||
|
||||
`list()` rejects and `subscribe()` throws when the host does not advertise `features.agentBackgroundWork`; ask the user to update the host. For an item with `canStop`, call `backgroundWork.stop(item)` to wait for provider confirmation. Stop requires `features.agentBackgroundWorkStop`.
|
||||
|
||||
## Follow timeline events
|
||||
|
||||
```ts
|
||||
|
||||
@@ -40,6 +40,25 @@ await directory.subscription.release();
|
||||
|
||||
`client.agents.subscribe()` 和 Agent 句柄上的 `subscribe()` 会为该 API 实例持有的观察添加本地监听器。它们不会请求数据。当多个带过滤条件的视图需要各自的更新时,请使用返回的订阅上的回调。
|
||||
|
||||
## 跟踪 Agent 的后台工作 {#follow-an-agents-background-work}
|
||||
|
||||
后台工作是 Agent 的工具调用返回后仍在运行的工作,例如使用 `run_in_background` 启动的 Claude shell 或 `Monitor` 监控。子 Agent 和 workflow 显示在子 Agent 中。
|
||||
|
||||
```ts
|
||||
const agent = client.agents.ref(agentId);
|
||||
|
||||
const unsubscribe = agent.backgroundWork.subscribe((items) => render(items));
|
||||
|
||||
// When this view closes:
|
||||
unsubscribe();
|
||||
```
|
||||
|
||||
订阅建立后,`subscribe()` 会向监听器投递当前列表;每次重连后和每次变化时,也会投递完整列表。用 `list()` 执行单次读取。每项包含 `id`、`kind`(`"shell"`、`"monitor"` 或 `"other"`,未知值按 `"other"` 处理)、可为 `null` 的 `description`、可选的 `command`,以及 Paseo 首次观察到该项的时间 `startedAt`。列表属于当前 Provider 会话,不会持久化。运行时丢失后,旧项会被清除。列表不可用时会报告错误,而不是返回空列表。
|
||||
|
||||
传入 `subscribe(listener, { onError })` 可接收读取当前列表失败的错误;此时不会调用监听器,下次重连时会重新读取。未提供 `onError` 时会记录错误。
|
||||
|
||||
主机未声明 `features.agentBackgroundWork` 时,`list()` 会 reject,`subscribe()` 会抛出错误;请让用户更新主机。对具有 `canStop` 的项目调用 `backgroundWork.stop(item)`,等待提供方确认。停止操作需要 `features.agentBackgroundWorkStop`。
|
||||
|
||||
## 跟踪时间线事件 {#follow-timeline-events}
|
||||
|
||||
```ts
|
||||
|
||||
@@ -61,8 +61,9 @@ Creation options include `config`, `cwd`, `parent`, `title`, `prompt`, `env`, `o
|
||||
|
||||
`parent` accepts a host-local agent ID, an `AgentAddress` (`{ serverId, agentId }`), or an agent handle. A handle’s `address` keeps its host identity after disconnect. When creating through `hosts.client(serverId)`, pass the parent handle or complete address; the SDK carries parent metadata to the destination.
|
||||
|
||||
`background` is workspace creation intent. It cannot accompany an existing `workspaceId`.
|
||||
See [background workspaces](./workspaces.md) for caller inheritance and inclusive discovery.
|
||||
`background` creates a [background agent](./agents.md#run-a-helper-as-a-background-agent). A
|
||||
workspace the call creates is background too. With a `parent` and no value, the parent's own
|
||||
`background` applies. See [background workspaces](./workspaces.md) for inclusive discovery.
|
||||
|
||||
`config` accepts:
|
||||
|
||||
@@ -79,32 +80,35 @@ See [background workspaces](./workspaces.md) for caller inheritance and inclusiv
|
||||
|
||||
### Agent handle
|
||||
|
||||
| Member | Result | Behavior |
|
||||
| ------------------------------ | --------------------------------- | ------------------------------------------------------------------------------------------------------- |
|
||||
| `id` | `string` | Stable daemon agent ID. |
|
||||
| `address` | `AgentAddress` | Complete `{ serverId, agentId }` identity; requires a connected host to establish it. |
|
||||
| `workspaceId` | `string \| null` | Current workspace placement. |
|
||||
| `cwd` | `string \| null` | Current working directory. |
|
||||
| `status` | Agent status or `null` | Current lifecycle status. |
|
||||
| `capabilities` | Capability flags or `null` | What the provider session supports. |
|
||||
| `availableModes` | Agent modes or `null` | Modes the session can switch to. |
|
||||
| `pendingPermissions` | Permission requests or `null` | Requests waiting on an answer. |
|
||||
| `activeTurn` | Active turn or `null` | The turn in flight, with `turnId` and `startedAt`. |
|
||||
| `lastUsage` | Usage or `null` | Token counts, cost, and context-window use from the last turn. |
|
||||
| `lastError` | `string \| null` | Last error the daemon recorded for the agent. |
|
||||
| `features` | Agent features or `null` | Provider feature toggles and selects with their current values. |
|
||||
| `runtimeInfo` | Runtime info or `null` | Live provider, session ID, model, thinking option, and mode. |
|
||||
| `archivedAt` | `string \| null` | Archive timestamp; `null` while the agent is active. |
|
||||
| `current()` | `PaseoAgent \| null` | Current detailed value observed by this handle; never fetches. |
|
||||
| `refresh(requestId?)` | `PaseoAgentRefetchResult \| null` | Fetches the current agent and project placement. |
|
||||
| `send(text, options?)` | `Promise<void>` | Resolves when the daemon accepts the prompt. |
|
||||
| `respondToPermission(options)` | `Promise<void>` | Answers a pending permission by `requestId` with an allow or deny `response`. |
|
||||
| `run(text, options?)` | `PaseoAgentRunResult` | Sends a prompt and waits for that turn. `timeoutMs` controls the wait; it defaults to 10 minutes. |
|
||||
| `waitForFinish(timeoutMs?)` | `PaseoAgentRunResult` | Waits for the active turn, including an initial prompt. Default timeout: 10 minutes. |
|
||||
| `commands(options?)` | `PaseoAgentCommandsResult` | Asks the live session for its slash commands and skills, including built-in ones. Options: `requestId`. |
|
||||
| `subscribe(handler)` | Unsubscribe function | Filters agent-directory updates to this ID and refreshes the handle properties. |
|
||||
| `archive()` | `{ archivedAt }` | Soft-deletes the agent and closes its runtime. |
|
||||
| `detach()` | `Promise<void>` | Removes the parent relationship without stopping the agent. |
|
||||
| Member | Result | Behavior |
|
||||
| ---------------------------------------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `id` | `string` | Stable daemon agent ID. |
|
||||
| `address` | `AgentAddress` | Complete `{ serverId, agentId }` identity; requires a connected host to establish it. |
|
||||
| `workspaceId` | `string \| null` | Current workspace placement. |
|
||||
| `cwd` | `string \| null` | Current working directory. |
|
||||
| `status` | Agent status or `null` | Current lifecycle status. |
|
||||
| `capabilities` | Capability flags or `null` | What the provider session supports. |
|
||||
| `availableModes` | Agent modes or `null` | Modes the session can switch to. |
|
||||
| `pendingPermissions` | Permission requests or `null` | Requests waiting on an answer. |
|
||||
| `activeTurn` | Active turn or `null` | The turn in flight, with `turnId` and `startedAt`. |
|
||||
| `lastUsage` | Usage or `null` | Token counts, cost, and context-window use from the last turn. |
|
||||
| `lastError` | `string \| null` | Last error the daemon recorded for the agent. |
|
||||
| `features` | Agent features or `null` | Provider feature toggles and selects with their current values. |
|
||||
| `runtimeInfo` | Runtime info or `null` | Live provider, session ID, model, thinking option, and mode. |
|
||||
| `archivedAt` | `string \| null` | Archive timestamp; `null` while the agent is active. |
|
||||
| `current()` | `PaseoAgent \| null` | Current detailed value observed by this handle; never fetches. |
|
||||
| `refresh(requestId?)` | `PaseoAgentRefetchResult \| null` | Fetches the current agent and project placement. |
|
||||
| `send(text, options?)` | `Promise<void>` | Resolves when the daemon accepts the prompt. |
|
||||
| `respondToPermission(options)` | `Promise<void>` | Answers a pending permission by `requestId` with an allow or deny `response`. |
|
||||
| `run(text, options?)` | `PaseoAgentRunResult` | Sends a prompt and waits for that turn. `timeoutMs` controls the wait; it defaults to 10 minutes. |
|
||||
| `waitForFinish(timeoutMs?)` | `PaseoAgentRunResult` | Waits for the active turn, including an initial prompt. Default timeout: 10 minutes. |
|
||||
| `commands(options?)` | `PaseoAgentCommandsResult` | Asks the live session for its slash commands and skills, including built-in ones. Options: `requestId`. |
|
||||
| `subscribe(handler)` | Unsubscribe function | Filters agent-directory updates to this ID and refreshes the handle properties. |
|
||||
| `archive()` | `{ archivedAt }` | Soft-deletes the agent and closes its runtime. |
|
||||
| `detach()` | `Promise<void>` | Removes the parent relationship without stopping the agent. |
|
||||
| `backgroundWork.list(options?)` | `Promise<PaseoAgentBackgroundWorkItem[]>` | Reads the agent's current background work once. Requires `features.agentBackgroundWork`. Options: `requestId`, `timeout`. |
|
||||
| `backgroundWork.stop(item, options?)` | `Promise<void>` | Stops a stoppable item and waits for provider confirmation. Pass the item returned by `list` or `subscribe`. Requires `features.agentBackgroundWorkStop`. |
|
||||
| `backgroundWork.subscribe(listener, options?)` | `() => void` | Calls `listener` with the current list, again after each reconnect, then with the full list on every change. `onError` receives a failed list read. Requires `features.agentBackgroundWork`. Returns the unsubscribe function. |
|
||||
|
||||
`workspaceId` through `archivedAt` mirror the last snapshot the handle observed. A handle from `ref()` reads `null` for all of them until `refresh()`, `run()`, `waitForFinish()`, a timeline refetch, or `subscribe()` delivers a snapshot. Optional values in an observed snapshot also read as `null`. Call `current()` when you need the whole snapshot or need to distinguish those states.
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ nav: API 参考
|
||||
|
||||
`parent` 接受主机本地的 Agent ID、`AgentAddress`(`{ serverId, agentId }`)或 Agent 句柄。句柄的 `address` 在断开连接后仍保留其主机身份。通过 `hosts.client(serverId)` 创建时,请传入父 Agent 句柄或完整地址;SDK 会把父级元数据带到目标主机。
|
||||
|
||||
`background` 表示创建工作区的意图。它不能与已有的 `workspaceId` 一起使用。调用方继承规则和包含后台的发现方式见[后台工作区](./workspaces.md)。
|
||||
`background` 会创建一个[后台 Agent](./agents.md#run-a-helper-as-a-background-agent)。本次调用创建的工作区也是后台工作区。带 `parent` 且省略该值时,沿用父 Agent 自己的 `background`。包含后台的发现方式见[后台工作区](./workspaces.md)。
|
||||
|
||||
`config` 接受:
|
||||
|
||||
@@ -76,32 +76,35 @@ nav: API 参考
|
||||
|
||||
### Agent 句柄 {#agent-handle}
|
||||
|
||||
| 成员 | 结果 | 行为 |
|
||||
| ------------------------------ | --------------------------------- | ----------------------------------------------------------------------- |
|
||||
| `id` | `string` | 稳定的 daemon Agent ID。 |
|
||||
| `address` | `AgentAddress` | 完整的 `{ serverId, agentId }` 身份;需要连接到主机才能确定。 |
|
||||
| `workspaceId` | `string \| null` | 当前所在的工作区。 |
|
||||
| `cwd` | `string \| null` | 当前工作目录。 |
|
||||
| `status` | Agent 状态或 `null` | 当前的生命周期状态。 |
|
||||
| `capabilities` | 能力标志或 `null` | Provider 会话支持的能力。 |
|
||||
| `availableModes` | Agent 模式或 `null` | 会话可以切换到的模式。 |
|
||||
| `pendingPermissions` | 权限请求或 `null` | 等待回应的请求。 |
|
||||
| `activeTurn` | 当前轮次或 `null` | 正在进行的轮次,包含 `turnId` 和 `startedAt`。 |
|
||||
| `lastUsage` | 用量或 `null` | 上一轮的 token 数、费用和上下文窗口用量。 |
|
||||
| `lastError` | `string \| null` | daemon 为该 Agent 记录的最后一个错误。 |
|
||||
| `features` | Agent 功能或 `null` | Provider 的功能开关和选择项及其当前值。 |
|
||||
| `runtimeInfo` | 运行时信息或 `null` | 实时的 Provider、会话 ID、模型、思考选项和模式。 |
|
||||
| `archivedAt` | `string \| null` | 归档时间戳;Agent 处于活动状态时为 `null`。 |
|
||||
| `current()` | `PaseoAgent \| null` | 此句柄观察到的当前详细值;从不发起请求。 |
|
||||
| `refresh(requestId?)` | `PaseoAgentRefetchResult \| null` | 获取当前的 Agent 及其所在项目。 |
|
||||
| `send(text, options?)` | `Promise<void>` | 在 daemon 接受提示词后 resolve。 |
|
||||
| `respondToPermission(options)` | `Promise<void>` | 按 `requestId` 以允许或拒绝的 `response` 回应一个待处理的权限请求。 |
|
||||
| `run(text, options?)` | `PaseoAgentRunResult` | 发送提示词并等待该轮结束。`timeoutMs` 控制等待时间,默认 10 分钟。 |
|
||||
| `waitForFinish(timeoutMs?)` | `PaseoAgentRunResult` | 等待当前轮次结束,包括初始提示词。默认超时:10 分钟。 |
|
||||
| `commands(options?)` | `PaseoAgentCommandsResult` | 向正在运行的会话查询它的斜杠命令和技能,包括内置的。选项:`requestId`。 |
|
||||
| `subscribe(handler)` | 取消订阅函数 | 把 Agent 目录更新过滤到此 ID,并刷新句柄的属性。 |
|
||||
| `archive()` | `{ archivedAt }` | 软删除 Agent 并关闭它的运行时。 |
|
||||
| `detach()` | `Promise<void>` | 解除父子关系,但不停止 Agent。 |
|
||||
| 成员 | 结果 | 行为 |
|
||||
| ---------------------------------------------- | ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `id` | `string` | 稳定的 daemon Agent ID。 |
|
||||
| `address` | `AgentAddress` | 完整的 `{ serverId, agentId }` 身份;需要连接到主机才能确定。 |
|
||||
| `workspaceId` | `string \| null` | 当前所在的工作区。 |
|
||||
| `cwd` | `string \| null` | 当前工作目录。 |
|
||||
| `status` | Agent 状态或 `null` | 当前的生命周期状态。 |
|
||||
| `capabilities` | 能力标志或 `null` | Provider 会话支持的能力。 |
|
||||
| `availableModes` | Agent 模式或 `null` | 会话可以切换到的模式。 |
|
||||
| `pendingPermissions` | 权限请求或 `null` | 等待回应的请求。 |
|
||||
| `activeTurn` | 当前轮次或 `null` | 正在进行的轮次,包含 `turnId` 和 `startedAt`。 |
|
||||
| `lastUsage` | 用量或 `null` | 上一轮的 token 数、费用和上下文窗口用量。 |
|
||||
| `lastError` | `string \| null` | daemon 为该 Agent 记录的最后一个错误。 |
|
||||
| `features` | Agent 功能或 `null` | Provider 的功能开关和选择项及其当前值。 |
|
||||
| `runtimeInfo` | 运行时信息或 `null` | 实时的 Provider、会话 ID、模型、思考选项和模式。 |
|
||||
| `archivedAt` | `string \| null` | 归档时间戳;Agent 处于活动状态时为 `null`。 |
|
||||
| `current()` | `PaseoAgent \| null` | 此句柄观察到的当前详细值;从不发起请求。 |
|
||||
| `refresh(requestId?)` | `PaseoAgentRefetchResult \| null` | 获取当前的 Agent 及其所在项目。 |
|
||||
| `send(text, options?)` | `Promise<void>` | 在 daemon 接受提示词后 resolve。 |
|
||||
| `respondToPermission(options)` | `Promise<void>` | 按 `requestId` 以允许或拒绝的 `response` 回应一个待处理的权限请求。 |
|
||||
| `run(text, options?)` | `PaseoAgentRunResult` | 发送提示词并等待该轮结束。`timeoutMs` 控制等待时间,默认 10 分钟。 |
|
||||
| `waitForFinish(timeoutMs?)` | `PaseoAgentRunResult` | 等待当前轮次结束,包括初始提示词。默认超时:10 分钟。 |
|
||||
| `commands(options?)` | `PaseoAgentCommandsResult` | 向正在运行的会话查询它的斜杠命令和技能,包括内置的。选项:`requestId`。 |
|
||||
| `subscribe(handler)` | 取消订阅函数 | 把 Agent 目录更新过滤到此 ID,并刷新句柄的属性。 |
|
||||
| `archive()` | `{ archivedAt }` | 软删除 Agent 并关闭它的运行时。 |
|
||||
| `detach()` | `Promise<void>` | 解除父子关系,但不停止 Agent。 |
|
||||
| `backgroundWork.list(options?)` | `Promise<PaseoAgentBackgroundWorkItem[]>` | 单次读取 Agent 当前的后台工作。需要 `features.agentBackgroundWork`;选项为 `requestId` 和 `timeout`。 |
|
||||
| `backgroundWork.stop(item, options?)` | `Promise<void>` | 停止可停止的项目并等待提供方确认。传入 `list` 或 `subscribe` 返回的项目。需要 `features.agentBackgroundWorkStop`。 |
|
||||
| `backgroundWork.subscribe(listener, options?)` | `() => void` | 向监听器投递当前列表,并在每次重连和变化时投递完整列表。`onError` 接收读取失败的错误;需要 `features.agentBackgroundWork`。返回取消订阅函数。 |
|
||||
|
||||
从 `workspaceId` 到 `archivedAt` 的属性反映句柄最近观察到的快照。由 `ref()` 得到的句柄在 `refresh()`、`run()`、`waitForFinish()`、时间线重新获取或 `subscribe()` 投递快照之前,这些属性都读作 `null`。已观察到的快照中的可选值也读作 `null`。当你需要整个快照,或需要区分这些状态时,请调用 `current()`。
|
||||
|
||||
|
||||
@@ -61,10 +61,10 @@ remain durable, with ordinary history, lifecycle hooks, archive, and exact-ID ac
|
||||
sidebar offers **Show background** without dropping replicated state.
|
||||
|
||||
`background` is creation-only. Omission defaults to false; when `callerAgentId` identifies an agent
|
||||
creating a new workspace, omission inherits its workspace's value. Explicit false or true overrides
|
||||
inheritance. Agent creation with `workspaceId` rejects explicit background creation intent; configure
|
||||
visibility when creating the workspace. SDK/RPC implicit agent creation also accepts `background`
|
||||
as workspace creation intent, never as an agent property.
|
||||
creating a new workspace, omission inherits the caller's visibility: true when the caller agent or
|
||||
its workspace is background. Explicit false or true overrides inheritance. Agent creation that
|
||||
creates its workspace applies `background` to both the workspace and the agent. To hide one agent
|
||||
in an existing workspace, see [background agents](./agents.md#run-a-helper-as-a-background-agent).
|
||||
|
||||
Creation and inclusive discovery require `features.backgroundWorkspaces`; the client rejects an
|
||||
unsupported host with an update-host message.
|
||||
@@ -135,8 +135,8 @@ do {
|
||||
|
||||
Background workspaces are left out unless `filter.includeBackground` is true. Use
|
||||
`client.workspaces.list({ filter: { includeBackground: true } })` or
|
||||
`client.agents.list({ filter: { includeBackground: true } })`. Workspace descriptors carry
|
||||
`background`; agents derive visibility from their workspace and have no separate setting.
|
||||
`client.agents.list({ filter: { includeBackground: true } })`. Workspace and agent snapshots carry
|
||||
`background`. An agent is hidden when it or its workspace is background.
|
||||
|
||||
## Refresh and archive a handle
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ const workspace = await client.workspaces.create({
|
||||
|
||||
传入 `background: true` 可以让工作区不出现在默认的发现结果中。它的 Agent 和终端仍然是持久的,有正常的历史记录、生命周期钩子、归档和精确 ID 访问。应用侧边栏提供 **显示后台工作区**,不会丢弃已同步的状态。
|
||||
|
||||
`background` 只能在创建时设置。省略时默认为 false;当 `callerAgentId` 指明是某个 Agent 在创建新工作区时,省略则继承该 Agent 所在工作区的值。显式的 false 或 true 会覆盖继承。带 `workspaceId` 创建 Agent 时会拒绝显式的后台创建意图;请在创建工作区时配置可见性。SDK/RPC 的隐式 Agent 创建也接受 `background`,但它表示工作区的创建意图,而不是 Agent 的属性。
|
||||
`background` 只能在创建时设置。省略时默认为 false;当 `callerAgentId` 指明是某个 Agent 在创建新工作区时,省略则继承调用方的可见性:调用方 Agent 或其工作区为后台时即为 true。显式的 false 或 true 会覆盖继承。同时创建工作区的 Agent 创建请求会把 `background` 同时应用到工作区和 Agent。要在已有工作区中隐藏单个 Agent,见[后台 Agent](./agents.md#run-a-helper-as-a-background-agent)。
|
||||
|
||||
创建和包含后台工作区的发现都需要 `features.backgroundWorkspaces`;对于不支持的主机,客户端会拒绝并提示更新主机。
|
||||
|
||||
@@ -122,7 +122,7 @@ do {
|
||||
} while (cursor);
|
||||
```
|
||||
|
||||
除非 `filter.includeBackground` 为 true,否则后台工作区不会被列出。使用 `client.workspaces.list({ filter: { includeBackground: true } })` 或 `client.agents.list({ filter: { includeBackground: true } })`。工作区描述符带有 `background`;Agent 的可见性来自它所在的工作区,没有单独的设置。
|
||||
除非 `filter.includeBackground` 为 true,否则后台工作区不会被列出。使用 `client.workspaces.list({ filter: { includeBackground: true } })` 或 `client.agents.list({ filter: { includeBackground: true } })`。工作区和 Agent 快照都带有 `background`。Agent 本身或其工作区为后台时,Agent 会被隐藏。
|
||||
|
||||
## 刷新和归档句柄 {#refresh-and-archive-a-handle}
|
||||
|
||||
|
||||
@@ -84,16 +84,7 @@ Add `--forge <name>` when Paseo cannot infer the forge from the source checkout.
|
||||
|
||||
## Copy local files with .worktreeinclude
|
||||
|
||||
Create `.worktreeinclude` at the root of your source checkout to copy selected gitignored files into new worktrees:
|
||||
|
||||
```gitignore
|
||||
.env
|
||||
config/*.local.json
|
||||
```
|
||||
|
||||
Patterns use gitignore syntax. A file must match `.worktreeinclude` and be gitignored to be copied. Tracked files are never copied, and existing destination files are never overwritten. Without `.worktreeinclude`, no local files are copied. Symlinked directories and nested repositories inside an included path cause the copy to fail and abort worktree creation.
|
||||
|
||||
The built-in `worktree-include` plugin copies from the checkout you create the worktree from, including when that checkout is another worktree. Copying finishes before setup, terminals, and the first agent. A copy failure aborts creation and removes the new worktree. Copies use copy-on-write clones on APFS and btrfs when supported, with a regular copy elsewhere.
|
||||
Use [`.worktreeinclude`](/docs/worktrees/worktreeinclude) to copy selected gitignored files, such as `.env`, from your source checkout before setup runs.
|
||||
|
||||
## paseo.json
|
||||
|
||||
@@ -82,16 +82,7 @@ paseo workspace create \
|
||||
|
||||
## 使用 .worktreeinclude 复制本地文件 {#copy-local-files-with-worktreeinclude}
|
||||
|
||||
在源检出的根目录创建 `.worktreeinclude`,将选定的 Git 忽略文件复制到新的 worktree:
|
||||
|
||||
```gitignore
|
||||
.env
|
||||
config/*.local.json
|
||||
```
|
||||
|
||||
模式使用 gitignore 语法。只有同时匹配 `.worktreeinclude` 且被 Git 忽略的文件才会复制。已跟踪的文件不会被复制,目标中已存在的文件不会被覆盖。没有 `.worktreeinclude` 时,不会复制任何本地文件。包含路径中的符号链接目录和嵌套仓库会导致复制失败,并中止 worktree 创建。
|
||||
|
||||
内置的 `worktree-include` 插件从你创建 worktree 时所选的检出复制文件,即使源检出本身也是 worktree。复制会在 setup、终端和第一个 Agent 运行之前完成。复制失败会中止创建并删除新的 worktree。支持时会在 APFS 和 btrfs 上使用写时复制克隆,否则执行普通复制。
|
||||
使用 [`.worktreeinclude`](/docs/worktrees/worktreeinclude),在 setup 运行前将 `.env` 等选定的 Git 忽略文件从源检出复制到新 worktree。
|
||||
|
||||
## paseo.json {#paseojson}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
title: .worktreeinclude
|
||||
description: Copy gitignored local files into new Paseo worktrees.
|
||||
nav: .worktreeinclude
|
||||
order: 12
|
||||
category: Workspaces
|
||||
---
|
||||
|
||||
# .worktreeinclude
|
||||
|
||||
Add `.worktreeinclude` at the root of your source checkout to copy local configuration into new [Git worktrees](/docs/worktrees). Paseo copies matching gitignored files before setup, terminals, and the first Agent start.
|
||||
|
||||
## Copy your local configuration
|
||||
|
||||
For a project whose `.gitignore` includes `.env` and `config/*.local.json`, create this `.worktreeinclude`:
|
||||
|
||||
```gitignore
|
||||
.env
|
||||
config/*.local.json
|
||||
```
|
||||
|
||||
When you create a worktree through Paseo, an existing `.env` and matching files such as `config/dev.local.json` are copied to the same relative paths in the new worktree. Their contents stay out of Git.
|
||||
|
||||
Commit `.worktreeinclude` to share the patterns with your team. Each teammate supplies their own ignored files in the checkout they create worktrees from.
|
||||
|
||||
## Pattern syntax
|
||||
|
||||
Use gitignore syntax, with one pattern per line. Comments start with `#`. Prefix a pattern with `!` to exclude a match:
|
||||
|
||||
```gitignore
|
||||
# Local environment and configuration
|
||||
.env
|
||||
config/*.local.json
|
||||
!config/production.local.json
|
||||
```
|
||||
|
||||
A pattern only selects files that Git already ignores. Adding a path here does not ignore it, so also configure `.gitignore` when needed. Tracked files and untracked files that Git does not ignore are excluded.
|
||||
|
||||
## When files are copied
|
||||
|
||||
The built-in `worktree-include` plugin reads the file from the checkout you create the worktree from. That source can itself be another worktree. Keep the plugin enabled for copying to run.
|
||||
|
||||
- Without `.worktreeinclude`, the plugin copies nothing.
|
||||
- Matching files must exist in the source checkout.
|
||||
- Existing destination files are skipped, preserving their contents.
|
||||
- Copies happen during creation, with no ongoing synchronization between checkouts.
|
||||
- A copy error aborts creation and removes the new worktree. Avoid including symlinked directories or nested repositories, which cannot be copied as files.
|
||||
|
||||
## Run commands during setup
|
||||
|
||||
Use `.worktreeinclude` for files that already exist locally. Use a [setup hook](/docs/worktrees#setup-and-teardown) to install dependencies, generate files, or fetch secrets:
|
||||
|
||||
```json
|
||||
{
|
||||
"worktree": {
|
||||
"setup": "npm ci"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Put this configuration in `paseo.json` on the base branch used for the new worktree. Copied files are available when setup runs.
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
title: .worktreeinclude
|
||||
description: 将 Git 忽略的本地文件复制到新的 Paseo worktree。
|
||||
nav: .worktreeinclude
|
||||
order: 12
|
||||
category: Workspaces
|
||||
---
|
||||
|
||||
# .worktreeinclude
|
||||
|
||||
在源检出的根目录添加 `.worktreeinclude`,将本地配置复制到新的 [Git worktree](/docs/worktrees)。Paseo 会在 setup、终端和首个 Agent 启动之前复制匹配的 Git 忽略文件。
|
||||
|
||||
## 复制本地配置 {#copy-your-local-configuration}
|
||||
|
||||
如果项目的 `.gitignore` 包含 `.env` 和 `config/*.local.json`,创建以下 `.worktreeinclude`:
|
||||
|
||||
```gitignore
|
||||
.env
|
||||
config/*.local.json
|
||||
```
|
||||
|
||||
通过 Paseo 创建 worktree 时,已有的 `.env` 和 `config/dev.local.json` 等匹配文件会被复制到新 worktree 中的相同相对路径。文件内容仍不会纳入 Git。
|
||||
|
||||
提交 `.worktreeinclude`,与团队共享这些模式。每位团队成员在用于创建 worktree 的检出中提供自己的忽略文件。
|
||||
|
||||
## 模式语法 {#pattern-syntax}
|
||||
|
||||
使用 gitignore 语法,每行一个模式。注释以 `#` 开头。模式前加 `!` 可排除匹配项:
|
||||
|
||||
```gitignore
|
||||
# Local environment and configuration
|
||||
.env
|
||||
config/*.local.json
|
||||
!config/production.local.json
|
||||
```
|
||||
|
||||
模式只会选择 Git 已忽略的文件。在此添加路径不会使 Git 忽略该文件,需要时还应配置 `.gitignore`。已跟踪的文件和未被 Git 忽略的未跟踪文件都不会被复制。
|
||||
|
||||
## 文件复制时机 {#when-files-are-copied}
|
||||
|
||||
内置的 `worktree-include` 插件从用于创建 worktree 的检出读取该文件。源检出也可以是另一个 worktree。保持该插件启用才能执行复制。
|
||||
|
||||
- 没有 `.worktreeinclude` 时,插件不会复制任何文件。
|
||||
- 匹配的文件必须存在于源检出中。
|
||||
- 目标中已存在的文件会被跳过,保留原有内容。
|
||||
- 复制发生在创建期间,之后不会在检出之间持续同步。
|
||||
- 复制错误会中止创建并删除新 worktree。避免包含符号链接目录或嵌套仓库,因为它们无法作为文件复制。
|
||||
|
||||
## 在 setup 期间运行命令 {#run-commands-during-setup}
|
||||
|
||||
使用 `.worktreeinclude` 复制本地已有文件。使用 [setup 钩子](/docs/worktrees#setup-and-teardown) 安装依赖、生成文件或获取密钥:
|
||||
|
||||
```json
|
||||
{
|
||||
"worktree": {
|
||||
"setup": "npm ci"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
将此配置放入新 worktree 所用基准分支的 `paseo.json` 中。setup 运行时,复制的文件已可使用。
|
||||
Reference in New Issue
Block a user