🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

会话接力插件 omdsh-dev/session-teleport

DeepSeek Harness
omdsh-dev36 天前有提交已提供中文文档扫描:含敏感能力

在多台设备间安全移交会话写入权

31.5/100
综合分
2
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/9/29 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

该插件未发布到 npm,按作者文档从 GitHub 源安装即可(静态检查只覆盖 npm 路径,不代表插件不可用)。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add omdsh-dev/session-teleport
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:已验证
是什么
dsh 原生插件 · other
装得上吗
本站已真实安装成功
非静态推断
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
更新放缓:最近一次提交在 36 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查需源码安装

仓库 package.json 标记 private,未发布到 npm,需从源码安装

展开查看检查明细 ↓

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✗npm 包@mattheliu/session-teleport(未发布到 npm,仅可源码安装)
✓Node 引擎要求 ^22.19.0 || >=24.0.0 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明

源码安装时若 pnpm 拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑。

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/13 08:09:10

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

可执行外部命令读写/删除本地文件读取剪贴板或密钥环境变量发起外部网络请求
查看扫描证据(共 108 处,展示前 94 条)
  • 写入本地文件
    dist/dsh-adapter.js:208
    await mkdir(directory, { recursive: true, mode: 448 });
  • 读取本地文件
    dist/credential-store.js:13
    raw = await readFile(this.path(sessionId), "utf8");
  • 写入本地文件
    dist/credential-store.js:31
    await mkdir(this.directory, { recursive: true, mode: 448 });
  • 写入本地文件
    dist/credential-store.js:44
    await rename(temporary, target);
  • 删除本地文件
    dist/credential-store.js:59
    await unlink(temporary);
  • 删除本地文件
    dist/credential-store.js:68
    await unlink(this.path(sessionId));
  • 读取本地文件
    dist/importer.js:275
    raw = await readFile(this.path(sessionId), "utf8");
  • 写入本地文件
    dist/importer.js:288
    await mkdir(this.directory, { recursive: true, mode: 448 });
  • 写入本地文件
    dist/importer.js:301
    await rename(temporary, target);
  • 删除本地文件
    dist/importer.js:316
    await unlink(temporary);
  • 删除本地文件
    dist/importer.js:325
    await unlink(this.path(sessionId));
  • 读取本地文件
    dist/plugin-lifecycle.js:227
    const info = await stat(credentialDir);
  • 发起网络请求
    dist/plugin-lifecycle.js:289
    const response = await fetch(new URL("/health", parsedUrl), {
  • 读取本地文件
    dist/plugin-lifecycle.js:401
    raw = await readFile(manifestPath, "utf8");
  • 读取本地文件
    dist/plugin-lifecycle.js:435
    await readFile(
  • 写入本地文件
    dist/plugin-lifecycle.js:454
    await mkdir(directory, { recursive: true, mode: 448 });
  • 写入本地文件
    dist/plugin-lifecycle.js:455
    await chmod(directory, 448);
  • 删除本地文件
    dist/plugin-lifecycle.js:471
    return async () => rm(path, { force: true });
  • 删除本地文件
    dist/plugin-lifecycle.js:480
    await rm(path, { force: true });
  • 读取本地文件
    dist/plugin-lifecycle.js:489
    const value = JSON.parse(await readFile(path, "utf8"));
  • 执行外部命令来自配置/变量
    dist/plugin-lifecycle.js:505
    const child = spawn(command, args, {
  • 写入本地文件
    dist/source-capture.js:41
    await mkdir(directory, { recursive: true, mode: 448 });
  • 删除本地文件
    dist/source-capture.js:56
    await unlink(temporary);
  • 删除本地文件
    dist/source-capture.js:71
    await unlink(temporary);
  • 写入本地文件
    dist/source-capture.js:88
    await chmod(temporaryRoot, 448);
  • 写入本地文件
    dist/source-capture.js:94
    await writeFile(
  • 执行外部命令来自配置/变量
    dist/source-capture.js:105
    const child = spawn(
  • 读取本地文件
    dist/source-capture.js:125
    const raw = await readFile(stagedBundlePath, "utf8");
  • 删除本地文件递归
    dist/source-capture.js:134
    await rm(temporaryRoot, { recursive: true, force: true });
  • 写入本地文件
    dist/source-capture.js:146
    await rename(temporary, path);
  • 删除本地文件
    dist/source-capture.js:155
    await unlink(temporary);
  • 读取本地文件
    dist/source-capture.js:193
    const raw = await readFile(statusPath, "utf8");
  • 读取本地文件
    dist/source-capture.js:261
    await lstat(path);
  • 读取密钥环境变量
    dist/cli.js:15
    const apiToken = process.env.TELEPORT_API_TOKEN;
  • 读取密钥环境变量
    dist/cli.js:16
    const adminToken = process.env.TELEPORT_ADMIN_TOKEN;
  • 读取密钥环境变量
    dist/device-cli.js:12
    const apiToken = process.env.DSH_TELEPORT_API_TOKEN;
  • 读取密钥环境变量
    dist/admin-cli.js:14
    const apiToken = process.env.DSH_TELEPORT_API_TOKEN;
  • 读取密钥环境变量
    dist/admin-cli.js:15
    const adminToken = process.env.DSH_TELEPORT_ADMIN_TOKEN;
  • 读取密钥环境变量
    dist/import-cli.js:40
    const apiToken = process.env.DSH_TELEPORT_API_TOKEN;
  • 读取密钥环境变量
    dist/import-cli.js:41
    const adminToken = process.env.DSH_TELEPORT_ADMIN_TOKEN;
  • 读取本地文件
    dist/import-cli.js:90
    raw = await readFile(path, "utf8");
  • 发起网络请求
    dist/client.js:21
    const response = await fetch(`${this.baseUrl}/health`, {
  • 发起网络请求
    dist/client.js:48
    return this.request("POST", "/v1/sessions", request);
  • 发起网络请求
    dist/client.js:51
    return this.request("POST", "/v1/sessions/materialize", request);
  • 发起网络请求
    dist/client.js:54
    return this.request("GET", "/v1/sessions", void 0, false, signal);
  • 发起网络请求
    dist/client.js:57
    return this.request("GET", `/v1/sessions/${encodeURIComponent(sessionId)}/head`, void 0, false, signal);
  • 发起网络请求
    dist/client.js:60
    return this.request(
  • 读取密钥环境变量
    src/admin-cli.ts:19
    const apiToken = process.env.DSH_TELEPORT_API_TOKEN;
  • 读取密钥环境变量
    src/admin-cli.ts:20
    const adminToken = process.env.DSH_TELEPORT_ADMIN_TOKEN;
  • 读取密钥环境变量
    src/cli.ts:16
    const apiToken = process.env.TELEPORT_API_TOKEN;
  • 读取密钥环境变量
    src/cli.ts:17
    const adminToken = process.env.TELEPORT_ADMIN_TOKEN;
  • 发起网络请求
    src/client.ts:45
    const response = await fetch(`${this.baseUrl}/health`, {
  • 发起网络请求
    src/client.ts:72
    return this.request("POST", "/v1/sessions", request);
  • 发起网络请求
    src/client.ts:76
    return this.request("POST", "/v1/sessions/materialize", request);
  • 发起网络请求
    src/client.ts:80
    return this.request("GET", "/v1/sessions", undefined, false, signal);
  • 发起网络请求
    src/client.ts:84
    return this.request("GET", `/v1/sessions/${encodeURIComponent(sessionId)}/head`, undefined, false, signal);
  • 发起网络请求
    src/client.ts:88
    return this.request(
  • 读取本地文件
    src/credential-store.ts:27
    raw = await readFile(this.path(sessionId), "utf8");
  • 写入本地文件
    src/credential-store.ts:46
    await mkdir(this.directory, { recursive: true, mode: 0o700 });
  • 写入本地文件
    src/credential-store.ts:58
    await rename(temporary, target);
  • 删除本地文件
    src/credential-store.ts:74
    await unlink(temporary);
  • 删除本地文件
    src/credential-store.ts:84
    await unlink(this.path(sessionId));
  • 读取密钥环境变量
    src/device-cli.ts:13
    const apiToken = process.env.DSH_TELEPORT_API_TOKEN;
  • 写入本地文件
    src/dsh-adapter.ts:206
    await mkdir(directory, { recursive: true, mode: 0o700 });
  • 读取密钥环境变量
    src/import-cli.ts:42
    const apiToken = process.env.DSH_TELEPORT_API_TOKEN;
  • 读取密钥环境变量
    src/import-cli.ts:43
    const adminToken = process.env.DSH_TELEPORT_ADMIN_TOKEN;
  • 读取本地文件
    src/import-cli.ts:98
    raw = await readFile(path, "utf8");
  • 读取本地文件
    src/importer.ts:350
    raw = await readFile(this.path(sessionId), "utf8");
  • 写入本地文件
    src/importer.ts:364
    await mkdir(this.directory, { recursive: true, mode: 0o700 });
  • 写入本地文件
    src/importer.ts:376
    await rename(temporary, target);
  • 删除本地文件
    src/importer.ts:392
    await unlink(temporary);
  • 删除本地文件
    src/importer.ts:402
    await unlink(this.path(sessionId));
  • 读取本地文件
    src/plugin-lifecycle.ts:339
    const info = await stat(credentialDir);
  • 发起网络请求
    src/plugin-lifecycle.ts:430
    const response = await fetch(new URL("/health", parsedUrl), {
  • 读取本地文件
    src/plugin-lifecycle.ts:605
    raw = await readFile(manifestPath, "utf8");
  • 读取本地文件
    src/plugin-lifecycle.ts:649
    await readFile(
  • 写入本地文件
    src/plugin-lifecycle.ts:672
    await mkdir(directory, { recursive: true, mode: 0o700 });
  • 写入本地文件
    src/plugin-lifecycle.ts:673
    await chmod(directory, 0o700);
  • 删除本地文件
    src/plugin-lifecycle.ts:689
    return async () => rm(path, { force: true });
  • 删除本地文件
    src/plugin-lifecycle.ts:698
    await rm(path, { force: true });
  • 读取本地文件
    src/plugin-lifecycle.ts:708
    const value: unknown = JSON.parse(await readFile(path, "utf8"));
  • 执行外部命令来自配置/变量
    src/plugin-lifecycle.ts:733
    const child = spawn(command, args, {
  • 写入本地文件
    src/source-capture.ts:84
    await mkdir(directory, { recursive: true, mode: 0o700 });
  • 删除本地文件
    src/source-capture.ts:99
    await unlink(temporary);
  • 删除本地文件
    src/source-capture.ts:115
    await unlink(temporary);
  • 写入本地文件
    src/source-capture.ts:141
    await chmod(temporaryRoot, 0o700);
  • 写入本地文件
    src/source-capture.ts:148
    await writeFile(
  • 执行外部命令来自配置/变量
    src/source-capture.ts:160
    const child = spawn(
  • 读取本地文件
    src/source-capture.ts:181
    const raw = await readFile(stagedBundlePath, "utf8");
  • 删除本地文件递归
    src/source-capture.ts:203
    await rm(temporaryRoot, { recursive: true, force: true });
  • 写入本地文件
    src/source-capture.ts:215
    await rename(temporary, path);
  • 删除本地文件
    src/source-capture.ts:225
    await unlink(temporary);
  • 读取本地文件
    src/source-capture.ts:278
    const raw = await readFile(statusPath, "utf8");
  • 读取本地文件
    src/source-capture.ts:352
    await lstat(path);
扫描于 2026/11/3(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

依赖的 DSH / Cordis 模块
@deepseek-ai/node-addon-system@deepseek-ai/cordis@deepseek-ai/dsh-session@deepseek-ai/dsh-session-persistence@deepseek-ai/dsh-session-persistence-jsonl@deepseek-ai/dsh-scope@deepseek-ai/dsh-invariants@deepseek-ai/dsh-attachment
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 多设备间移交会话写入权

README

由 DeepSeek 最新模型翻译生成
session-teleport DeepSeek Harness 多设备 Session 接力插件。PostgreSQL 是唯一在线权威;同一时间只有一台设备持有写入凭据,移交后旧凭据立即被 writer_epoch 拒绝。 这是独立插件仓库,只分发 BSD-3-Clause 许可下的 Session Teleport 代码。 DSH peer packages 是外部依赖,保留各自许可,不在本仓库中复制、镜像或重新授权。 本包保留 private: true,避免本地发布演练误触 npm 发布;受支持的插件源是 https://github.com/omdsh-dev/session-teleport,安装时必须固定到完整 commit。 当前适配目标是 DSH 0.2.0-rc.1(2026-09-28 的 npm next 预发布版本),严格编译基线为:@deepseek-ai/cordis@4.0.4,以及 @deepseek-ai/dsh-session、@deepseek-ai/dsh-session-persistence、 @deepseek-ai/dsh-session-persistence-jsonl 的 0.2.0-rc.1。这些精确版本只用于开发和验证;安装时仍由目标 DSH 环境按 peerDependencies 提供兼容版本。
展开完整文档(README 剩余 1 节)
session-teleport

DeepSeek Harness 多设备 Session 接力插件。PostgreSQL 是唯一在线权威;同一时间只有一台设备持有写入凭据,移交后旧凭据立即被 writer_epoch 拒绝。

这是独立插件仓库,只分发 BSD-3-Clause 许可下的 Session Teleport 代码。
DSH peer packages 是外部依赖,保留各自许可,不在本仓库中复制、镜像或重新授权。
本包保留 private: true,避免本地发布演练误触 npm 发布;受支持的插件源是 https://github.com/omdsh-dev/session-teleport,安装时必须固定到完整 commit。

当前适配目标是 DSH 0.2.0-rc.1(2026-09-28 的 npm next 预发布版本),严格编译基线为:@deepseek-ai/cordis@4.0.4,以及 @deepseek-ai/dsh-session、@deepseek-ai/dsh-session-persistence、 @deepseek-ai/dsh-session-persistence-jsonl 的 0.2.0-rc.1。这些精确版本只用于开发和验证;安装时仍由目标 DSH 环境按 peerDependencies 提供兼容版本。

新版采用 create/open → SessionHandle,支持 read/append/flush/close、 stat/list 和空会话持久化。0.7.0-rc.1 插件与 authority 服务应一起升级:数据库自动迁移到 schema 5,保留 fork 的 inheritedEventCount。
已有旧格式日志不会被自动改写成 format 4;应先通过兼容源后端迁移、读取并导出。
直接保存的旧 Teleport 日志仍可归档,但新版 adapter 会拒绝打开非 format 4 日志。
同一设备的所有 profile 必须共用 writer credential 目录;本地文件锁防止重复写者,跨设备交接由 PostgreSQL epoch fencing 保证。当前文件锁路径面向 macOS/Linux。

能做什么

- 原样保存 JSON event envelope,包括扩展字段。
- 在单个事务中校验 writer、revision、next seq 和连续事件序号。
- 通过 idempotency key 与请求摘要安全重认“已提交但响应丢失”的重试。
- 生成一次性、限时 handoff code;接管后旧 writer token 被 fencing。
- 旧设备丢失时可由独立管理员凭据执行显式恢复接管,并审计每次 writer 变化。
- 可对既有 Session 做无写入 dry-run、单事务导入和受约束的回滚切换。
- 可从旧 DSH Profile 临时挂载只读 overlay,经 open(id, "read") / handle.read() 抓取 Session,不必手写 TypeScript,也不解析 JSONL/SQLite 物理文件。
- 提供计划优先的 install / upgrade / uninstall / doctor 生命周期工具;只接受完整 commit pin,结构校验失败会恢复旧依赖。
- 提供一致快照、增量读取与 SSE 观察端点。
- 提供 DSH SessionPersistence / SessionHandle adapter 和 profile bundle patch。
- 将每个 Session 的 writer credential 保存为本机 0600 原子文件。
- 在非 loopback 监听时强制配置服务 Bearer token。

架构

DSH device A ─┐
DSH device B ─┼─ HTTP/SSE Teleport service ─ PostgreSQL
observer ─┘

客户端不直接连接 PostgreSQL。PostgreSQL 负责事务、行锁、CAS、writer fencing 和幂等记录;SSE 只是通知通道,客户端仍从权威表读取数据。

安装插件

首次安装从目标 commit 的本地 checkout 运行生命周期工具。默认只输出计划:

node dist/plugin-cli.js install --profile web --revision
node dist/plugin-cli.js install --profile web --revision \
--apply --profile-stopped --cutover-safe

目标环境需要通过其正常、受支持的软件源解析匹配版本的 @deepseek-ai peer packages。本仓库不分发这些依赖,也不要求把软件源配置或访问凭据放入仓库。

为运行该 profile 的每台设备配置:

export DSH_TELEPORT_ENABLE=1
export DSH_TELEPORT_URL=https://teleport.example.com
export DSH_TELEPORT_API_TOKEN=a-long-random-service-token
export DSH_TELEPORT_DEVICE_ID=office-mac
可选:启动健康检查超时,默认 5000 毫秒
export DSH_TELEPORT_HEALTH_TIMEOUT_MS=5000

仅安装 bundle 不会切换 Session authority:默认仍使用 DSH 0.2 自带 JSONL,Teleport adapter 保持禁用。先启动 Teleport 服务并验证 /health,再停止 profile、设置
DSH_TELEPORT_ENABLE=1 并重启,才会关闭 JSONL、启用 Teleport。显式启用后如果
服务不可达,profile 会拒绝启动,不会静默回退到另一份权威存储。

启用或停用会切换 Session authority,升级也会替换 profile 依赖。请先让正在写入的 turn 完成并停止对应 profile,再重启;不要把它当成对运行中 Session 无影响的热替换。
生命周期工具不会删除 PostgreSQL 数据或本机 writer 凭据。完整步骤见插件生命周期 和 DSH 接入说明。

运行服务需要 Node.js 22.19+ 和 PostgreSQL:

pnpm install --frozen-lockfile --ignore-scripts
pnpm build

cp .env.example .env
set -a && source .env && set +a
pnpm start

服务默认监听 127.0.0.1:43127,启动时自动创建所需数据库表。从插件包安装后也可直接运行 dsh-teleport-server。

除 /health 外,请求需要:

Authorization: Bearer $TELEPORT_API_TOKEN

管理员恢复还需要与普通 API token 不同的 TELEPORT_ADMIN_TOKEN。

主要端点:

- GET /v1/sessions
- POST /v1/sessions
- POST /v1/sessions/materialize
- GET /v1/sessions/:id/head
- GET /v1/sessions/:id?after=-1
- GET /v1/sessions/:id/watch?after=-1
- POST /v1/sessions/:id/append
- POST /v1/sessions/:id/handoffs
- POST /v1/handoffs/:code/accept
- POST /v1/admin/sessions/:id/recover-writer
- GET /v1/admin/sessions/:id/writer-audit
- POST /v1/admin/sessions/:id/rollback-import

换机

先让旧设备上的当前 turn 完成,然后生成一次性码:

pnpm device create-handoff

在新设备上设置不同的 DSH_TELEPORT_DEVICE_ID,再接受:

pnpm device accept-handoff

旧设备丢失且无法生成移交码时,在目标设备上配置管理员凭据并执行显式恢复:

export DSH_TELEPORT_ADMIN_TOKEN=a-different-long-admin-token
export DSH_TELEPORT_ADMIN_ACTOR=operator-name
pnpm admin recover-writer "lost device"
pnpm admin writer-audit

恢复操作会原子增加 writer_epoch、保存新凭据并记录原因;它不是自动 lease,不会因短暂断网自行驱逐 writer。

导入既有 Session

先让旧 writer 完成当前 turn、flush 并停止对应 Profile。迁移工具会临时启动这个 Profile,挂载一次性只读 overlay,并把一致只读 handle 结果写成 0600 bundle;它不修改旧 Profile 配置或旧存储,也不会覆盖已有输出文件:

dsh-teleport-import capture

然后依次运行:

dsh-teleport-import dry-run
dsh-teleport-import apply

导入不会修改旧存储。若切换后的只读 smoke check 失败,且 Teleport 尚未发生新 append、handoff 或 recovery,可使用管理员令牌执行:

dsh-teleport-import rollback "cutover smoke check failed"

一旦目标 head 或 writer epoch 前进,回滚会明确拒绝,避免切回旧前缀导致历史丢失。完整边界见 导入与可回滚切换。

开发与验证

pnpm install --frozen-lockfile --ignore-scripts
pnpm check
pnpm audit --prod

依赖来自公共 npm registry,安装不需要 npm 令牌。发布包检查会拒绝把 .npmrc 打进产物。

仓库内的通用测试覆盖 authority 的 CAS/fencing/handoff/idempotency、HTTP API、schema 与 credential file。更多说明见 测试边界。

模拟测试不能替代物理双机、真实网络和 PostgreSQL 故障演练。候选版本应按真实设备验收 执行两机测试。

正确性边界

- 当前版本是单写者接力与显式恢复,不是任意多写者自动合并。
- 如果未来支持离线多写者,推荐把冲突保存为显式 branch,再 fast-forward 或人工 merge;不把两条对话和工具副作用自动穿插。设计边界见多写者分支策略。
- 并发 append 只有一个能推进 revision;其他请求明确冲突,不会静默拼接历史。
- 本地 SQLite 可作为未来离线缓存;NAS/S3/MinIO 可作为未来快照或冷备,但不参与在线 head 竞争。
- 多服务副本需要跨实例通知机制;通知只用于唤醒,不能代替 PostgreSQL 权威读取。
- 尚未认证物理多主机网络、TLS/反向代理、PostgreSQL HA、长时间分区、PITR 恢复和持续负载。
- 不直接解析或改写 JSONL/SQLite 物理文件;导入使用原后端读出的一致 portable snapshot。

事件列使用 PostgreSQL JSON 而不是 JSONB,以保留 event envelope 的序列化键顺序和无损往返。schema 会拒绝无法证明无损的旧格式迁移,避免静默改写历史。

License

BSD-3-Clause
预览分享卡片长什么样 ↓omdsh-dev/session-teleport
分享 · DeepSeek Harness Hub
omdsh-dev/session-teleport

在多台设备间安全移交会话写入权

这是你的仓库?认领条目

回到顶部