@cjhyy/code-shell-server 0.9.22 → 0.9.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -1
- package/dist/bin/code-shell-settings-recovery.d.ts +2 -0
- package/dist/bin/code-shell-settings-recovery.js +4 -0
- package/dist/desktop-web/http-api.js +14 -0
- package/dist/environment-identity.d.ts +4 -0
- package/dist/environment-identity.js +67 -0
- package/dist/hub/auth-store.d.ts +2 -0
- package/dist/hub/auth-store.js +7 -0
- package/dist/index.auth.d.ts +3 -0
- package/dist/index.auth.js +2 -0
- package/dist/index.links.d.ts +1 -0
- package/dist/index.links.js +1 -0
- package/dist/index.panels.d.ts +7 -0
- package/dist/index.panels.js +7 -0
- package/dist/index.remote-relay.d.ts +5 -0
- package/dist/index.remote-relay.js +6 -0
- package/dist/index.serve.d.ts +2 -0
- package/dist/index.serve.js +2 -0
- package/dist/links/http.d.ts +3 -0
- package/dist/links/http.js +11 -2
- package/dist/links/remote-configuration.d.ts +4 -0
- package/dist/links/remote-configuration.js +32 -0
- package/dist/links/service.d.ts +6 -1
- package/dist/links/service.js +241 -19
- package/dist/mobile-remote/mobile-upload-service.d.ts +4 -0
- package/dist/mobile-remote/mobile-upload-service.js +69 -11
- package/dist/mobile-remote/remote-host-manager.d.ts +10 -5
- package/dist/mobile-remote/remote-host-manager.js +53 -5
- package/dist/panels/agent-task-service.js +9 -2
- package/dist/panels/automations.d.ts +49 -0
- package/dist/panels/automations.js +134 -0
- package/dist/panels/bridge-contract.d.ts +5 -3
- package/dist/panels/bridge-contract.js +11 -1
- package/dist/panels/directory-bookmarks.d.ts +9 -1
- package/dist/panels/directory-bookmarks.js +64 -5
- package/dist/panels/execution-gate.d.ts +26 -0
- package/dist/panels/execution-gate.js +70 -0
- package/dist/panels/http.d.ts +26 -2
- package/dist/panels/http.js +7 -1
- package/dist/panels/hub-automations.d.ts +31 -0
- package/dist/panels/hub-automations.js +352 -0
- package/dist/panels/management-http.d.ts +20 -2
- package/dist/panels/management-http.js +15 -2
- package/dist/panels/management.d.ts +26 -4
- package/dist/panels/management.js +382 -39
- package/dist/panels/package-mutation.d.ts +8 -0
- package/dist/panels/package-mutation.js +23 -0
- package/dist/panels/process-service.d.ts +3 -0
- package/dist/panels/process-service.js +77 -4
- package/dist/panels/resources/clone.d.ts +13 -0
- package/dist/panels/resources/clone.js +99 -0
- package/dist/panels/resources/http.d.ts +11 -0
- package/dist/panels/resources/http.js +74 -0
- package/dist/panels/resources/references.d.ts +5 -0
- package/dist/panels/resources/references.js +26 -0
- package/dist/panels/resources/service.js +63 -27
- package/dist/panels/runtime-services.js +16 -3
- package/dist/panels/runtime.d.ts +16 -4
- package/dist/panels/runtime.js +522 -64
- package/dist/panels/shared-tool-jobs.d.ts +40 -0
- package/dist/panels/shared-tool-jobs.js +37 -0
- package/dist/panels/storage-store.d.ts +21 -0
- package/dist/panels/storage-store.js +43 -0
- package/dist/panels/task-cookie-host.d.ts +26 -0
- package/dist/panels/task-cookie-host.js +145 -0
- package/dist/panels/task-cookies.d.ts +51 -0
- package/dist/panels/task-cookies.js +183 -0
- package/dist/panels/tool-executor.d.ts +21 -5
- package/dist/panels/tool-executor.js +171 -30
- package/dist/panels/tool-jobs-storage.d.ts +2 -0
- package/dist/panels/tool-jobs-storage.js +34 -0
- package/dist/panels/tool-jobs.d.ts +49 -3
- package/dist/panels/tool-jobs.js +270 -15
- package/dist/panels/types.d.ts +43 -0
- package/dist/project-runtime/backup.d.ts +26 -0
- package/dist/project-runtime/backup.js +500 -0
- package/dist/project-runtime/control-server.d.ts +4 -0
- package/dist/project-runtime/control-server.js +19 -1
- package/dist/project-runtime/docker-provider.d.ts +4 -0
- package/dist/project-runtime/docker-provider.js +16 -1
- package/dist/project-runtime/managed-entry.js +4 -0
- package/dist/project-runtime/registry.d.ts +2 -0
- package/dist/project-runtime/registry.js +15 -0
- package/dist/project-runtime/seccomp.d.ts +8 -0
- package/dist/project-runtime/seccomp.js +72 -0
- package/dist/project-runtime/types.d.ts +2 -0
- package/dist/remote-relay/byte-stream.d.ts +9 -0
- package/dist/remote-relay/byte-stream.js +71 -0
- package/dist/remote-relay/connector.d.ts +27 -0
- package/dist/remote-relay/connector.js +197 -0
- package/dist/remote-relay/local-stream.d.ts +10 -0
- package/dist/remote-relay/local-stream.js +120 -0
- package/dist/remote-relay/protocol.d.ts +39 -0
- package/dist/remote-relay/protocol.js +64 -0
- package/dist/serve/automation-worker.d.ts +16 -0
- package/dist/serve/automation-worker.js +126 -0
- package/dist/serve/cli.d.ts +3 -0
- package/dist/serve/cli.js +17 -0
- package/dist/serve/headless-server.d.ts +3 -0
- package/dist/serve/headless-server.js +140 -10
- package/dist/serve/settings-recovery-cli.d.ts +29 -0
- package/dist/serve/settings-recovery-cli.js +128 -0
- package/dist/session-http-responses.d.ts +7 -0
- package/dist/session-http-responses.js +40 -0
- package/dist/sessions-service.d.ts +6 -2
- package/dist/sessions-service.js +23 -4
- package/dist/worker-bridge-core.d.ts +5 -0
- package/dist/worker-bridge-core.js +23 -0
- package/package.json +15 -6
package/README.md
CHANGED
|
@@ -12,6 +12,8 @@ CodeShell 服务端传输与管理服务(纯 Node、零 Electron):mobile r
|
|
|
12
12
|
| ---------------------------------------- | ----------------------------------------------------------------- |
|
|
13
13
|
| `@cjhyy/code-shell-server/storage` | 磁盘 Session、附件暂存、图片探测和稳定 client-message ID |
|
|
14
14
|
| `@cjhyy/code-shell-server/worker` | 与传输无关的 stdio worker 生命周期和行协议桥接 |
|
|
15
|
+
| `@cjhyy/code-shell-server/auth` | 独立服务可复用的单 owner HTTP 初始化、登录及会话撤销 |
|
|
16
|
+
| `@cjhyy/code-shell-server/remote-relay` | 设备出站 WSS 连接器、固定回环目标、受限二进制字节流与 v1 协议 |
|
|
15
17
|
| `@cjhyy/code-shell-server/mobile-remote` | 配对、访问门禁、rooms、上传、LAN/tunnel host 和移动端协议类型 |
|
|
16
18
|
| `@cjhyy/code-shell-server/serve` | Headless HTTP/WebSocket host 与 `code-shell-serve` CLI 组合默认值 |
|
|
17
19
|
| `@cjhyy/code-shell-server/desktop-web` | 设备配对换 Cookie、已知 Workspace 校验与共享管理 HTTP API |
|
|
@@ -24,7 +26,7 @@ import { WorkerBridgeCore } from "@cjhyy/code-shell-server/worker";
|
|
|
24
26
|
```
|
|
25
27
|
|
|
26
28
|
包根入口保留 storage、worker、mobile-remote、serve 和 desktop-web 的兼容导出;
|
|
27
|
-
Link
|
|
29
|
+
Link 和独立服务认证分别使用 `/links`、`/auth` 入口。新消费方应避免根入口,以免只使用 storage
|
|
28
30
|
或 transport 时也求值无关的宿主组合。
|
|
29
31
|
|
|
30
32
|
`/storage`、`/worker` 和 `/mobile-remote` 没有 Coding/Web 静态导入。外部
|
|
@@ -32,6 +34,39 @@ Agent 策略由产品宿主通过 `ResidentAgentOptions.appendSystemPrompt` 注
|
|
|
32
34
|
`/serve` 则是有意保留的开箱即用产品入口:CLI 被调用时解析 Coding stdio
|
|
33
35
|
worker 和已构建的 Web app。
|
|
34
36
|
|
|
37
|
+
电脑出站中继使用 `/remote-relay`。先以 `mode: "relay"` 启动已配置访问口令的
|
|
38
|
+
`RemoteHostManager`,再把 `host.relayTarget()` 交给连接器;该能力在 Host 停止时立即
|
|
39
|
+
撤销,不能继续暴露复用原端口的其他服务。协议、生命周期和真实网络测试范围见
|
|
40
|
+
[设备中继说明](../../docs/device-relay.md)。桌面登记设置和物理手机验收仍是后续工作。
|
|
41
|
+
|
|
42
|
+
`MobileUploadService.revokeDevice(deviceId)` 同步禁止该设备的新票据和领取操作,
|
|
43
|
+
中止尚未被任务认领的上传,并返回可等待的临时文件清理结果。`RemoteHostManager`
|
|
44
|
+
的同名方法会调用它、报告异步清理错误;`stop()` 会等待清理完成。已被任务认领的
|
|
45
|
+
附件保留至原持有者 `finalize` 或 `release`;撤销后的 `release` 不会重新开放票据。
|
|
46
|
+
自定义 `uploads` 适配器也必须实现这项设备级撤销。其他设备的上传不受影响。
|
|
47
|
+
|
|
48
|
+
## 独立服务的单 owner 认证
|
|
49
|
+
|
|
50
|
+
`/auth` 直接复用 Hub 的认证实现,运行时仅导出 `createHubAuth` 和
|
|
51
|
+
`HUB_SESSION_COOKIE`,同时提供 `HubAuth`、`HubAuthOptions`、`HubSession` 类型。
|
|
52
|
+
它不会创建 HTTP listener、Worker、项目或设备中继,也不提供多租户身份体系。
|
|
53
|
+
|
|
54
|
+
宿主通过 `createHubAuth({ dataDir, publicOrigin, onRevoke })` 创建认证服务,先将请求
|
|
55
|
+
交给 `auth.handle(request, response)` 处理现有 `/api/v1/auth/*` 端点。其他受保护
|
|
56
|
+
路由必须同时检查 `auth.isOriginAllowed(request)` 和 `await auth.authenticate(request)`;
|
|
57
|
+
认证成功本身不授予文件、工具或手机配对权限。
|
|
58
|
+
|
|
59
|
+
`publicOrigin` 是部署者配置的可信根 Origin,公网必须使用 HTTPS,不能从请求的
|
|
60
|
+
转发头推导。`dataDir` 保存单 owner 账号和会话;首次返回的 `bootstrapToken` 应交给
|
|
61
|
+
管理员私密保存,不能记录到公共日志。`onRevoke(sessionId)` 用于同步撤销宿主持有的
|
|
62
|
+
连接,长期连接还应重新检查会话有效性;Cookie/令牌只能用于本服务。
|
|
63
|
+
多个独立认证服务应使用不同主机名和数据目录;Cookie 不按端口隔离,只更换监听
|
|
64
|
+
端口不能隔离浏览器登录。
|
|
65
|
+
|
|
66
|
+
`HubAuth.store` 保留原有返回类型,供宿主检查活动会话;本入口不额外导出 store
|
|
67
|
+
构造器或令牌解析工具。`bun run test:package-release` 会从实际 tarball 的公开入口
|
|
68
|
+
检查严格 NodeNext 类型,并用原生 Node HTTP 验证初始化、登录、Origin、撤销和重载。
|
|
69
|
+
|
|
35
70
|
## 两种宿主,共用浏览器工作台
|
|
36
71
|
|
|
37
72
|
`packages/web/app/Workbench.tsx` 组合对话、模型、Skills、MCP、历史、文件及 Link 页面。
|
|
@@ -44,6 +79,10 @@ Desktop Web 不会创建第二个 Hub Worker,也不需要再建 Hub 管理员
|
|
|
44
79
|
`hub/` 与 `/links` 实现。Electron 原生 renderer 仍使用原来的 IPC。
|
|
45
80
|
具体文件与环境差异见 [共享 Web 工作台](../../docs/todo/shared-web-workbench.md)。
|
|
46
81
|
|
|
82
|
+
Desktop Web 和 Hub 在注销或显式撤销会话时,会关闭该会话尚未完成的受保护 HTTP
|
|
83
|
+
响应,包括正在下载的文件,并停止源文件读取、释放句柄。其他会话的下载不受影响;
|
|
84
|
+
已经返回接受结果的后台任务仍由原来的任务生命周期管理。注销请求本身仍返回成功。
|
|
85
|
+
|
|
47
86
|
## code-shell-serve — 个人 Hub Web host
|
|
48
87
|
|
|
49
88
|
在任意机器上把一个 workspace 变成浏览器可访问的 CodeShell:
|
|
@@ -138,3 +177,8 @@ Docker 部署到其他机器见 [Docker 部署说明](../../docs/docker-deployme
|
|
|
138
177
|
更新 Hub 后需重建并重启服务。Desktop Web 的入口构建到 `packages/desktop/out/mobile`,
|
|
139
178
|
还依赖新的主进程 HTTP 接线,因此必须重新构建 Desktop 并重启应用;只更新 Hub 的 `dist-app`
|
|
140
179
|
或刷新旧桌面的浏览器页面不会完成升级。
|
|
180
|
+
|
|
181
|
+
The focused `/remote-relay` entry also exposes `environmentIdentity(dataDir)` so
|
|
182
|
+
desktop enrollment uses the same stable identity as its Web descriptor. It is not
|
|
183
|
+
added to the root entry. `DeviceRelayState` includes terminal `unauthorized` for
|
|
184
|
+
TLS-verified 401/403 control handshakes; temporary failures continue reconnecting.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { describeEnvironment, environmentIdentity } from "../environment-identity.js";
|
|
1
2
|
import { randomBytes, randomUUID } from "node:crypto";
|
|
2
3
|
import { isAbsolute } from "node:path";
|
|
3
4
|
import { createHubConfiguration, HubConfigurationError } from "../hub/configuration.js";
|
|
@@ -5,6 +6,7 @@ import { createHubFiles } from "../hub/files.js";
|
|
|
5
6
|
import { createHubMcpConfiguration } from "../hub/mcp-configuration.js";
|
|
6
7
|
import { createHubSessions } from "../hub/session-management.js";
|
|
7
8
|
import { createHubSkills } from "../hub/skills-management.js";
|
|
9
|
+
import { createSessionHttpResponses } from "../session-http-responses.js";
|
|
8
10
|
const COOKIE = "cs_desktop_session";
|
|
9
11
|
const SESSION_TTL = 30 * 60_000;
|
|
10
12
|
const MAX_SESSIONS = 512;
|
|
@@ -109,11 +111,13 @@ export function createDesktopWebApi(options) {
|
|
|
109
111
|
if (!Number.isSafeInteger(ttl) || ttl < 1000 || ttl > 24 * 60 * 60_000)
|
|
110
112
|
throw new Error("Desktop HTTP session lifetime must be between one second and one day");
|
|
111
113
|
const sessions = new Map();
|
|
114
|
+
const responses = createSessionHttpResponses();
|
|
112
115
|
const identities = new WeakMap();
|
|
113
116
|
const workspaces = new Map();
|
|
114
117
|
let generation = 0;
|
|
115
118
|
let closed = false;
|
|
116
119
|
function cancelSession(session) {
|
|
120
|
+
responses.cancelOwner(session.id);
|
|
117
121
|
for (const services of workspaces.values()) {
|
|
118
122
|
services.configuration.cancelOwner(session.id);
|
|
119
123
|
services.mcp.cancelOwner(session.id);
|
|
@@ -336,6 +340,16 @@ export function createDesktopWebApi(options) {
|
|
|
336
340
|
}
|
|
337
341
|
if (!current || !token)
|
|
338
342
|
throw new HubConfigurationError(401, "设备连接已失效,请重新连接桌面。");
|
|
343
|
+
responses.track(current.session.id, response);
|
|
344
|
+
if (url.pathname === "/api/v1/environment" && request.method === "GET") {
|
|
345
|
+
const id = await environmentIdentity(options.dataDir);
|
|
346
|
+
if (closed ||
|
|
347
|
+
generation !== requestGeneration ||
|
|
348
|
+
principal(token)?.session !== current.session)
|
|
349
|
+
throw new HubConfigurationError(401, "设备连接已失效,请重新连接桌面。");
|
|
350
|
+
json(response, 200, describeEnvironment(id, "desktop"));
|
|
351
|
+
return true;
|
|
352
|
+
}
|
|
339
353
|
const input = workspaceInput(request, url);
|
|
340
354
|
const cwd = await options.resolveWorkspace(input, current.device.id);
|
|
341
355
|
if (closed ||
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { EnvironmentDescriptor } from "@cjhyy/code-shell-web";
|
|
2
|
+
/** Publish once, atomically. Never silently replace a corrupt identity or adopt a symlink. */
|
|
3
|
+
export declare function environmentIdentity(dataDir: string): Promise<string>;
|
|
4
|
+
export declare function describeEnvironment(id: string, kind: EnvironmentDescriptor["kind"]): EnvironmentDescriptor;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { constants } from "node:fs";
|
|
3
|
+
import { link, lstat, mkdir, open, rm } from "node:fs/promises";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
const UUID = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/;
|
|
6
|
+
/** Publish once, atomically. Never silently replace a corrupt identity or adopt a symlink. */
|
|
7
|
+
export async function environmentIdentity(dataDir) {
|
|
8
|
+
await mkdir(dataDir, { recursive: true, mode: 0o700 });
|
|
9
|
+
const path = join(dataDir, "environment.json");
|
|
10
|
+
try {
|
|
11
|
+
await lstat(path);
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
if (error.code !== "ENOENT")
|
|
15
|
+
throw error;
|
|
16
|
+
const temporary = join(dataDir, `.environment-${randomUUID()}.tmp`);
|
|
17
|
+
const file = await open(temporary, "wx", 0o600);
|
|
18
|
+
try {
|
|
19
|
+
await file.writeFile(JSON.stringify({ version: 1, id: randomUUID() }));
|
|
20
|
+
await file.sync();
|
|
21
|
+
await file.close();
|
|
22
|
+
try {
|
|
23
|
+
await link(temporary, path);
|
|
24
|
+
}
|
|
25
|
+
catch (cause) {
|
|
26
|
+
if (cause.code !== "EEXIST")
|
|
27
|
+
throw cause;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
await file.close().catch(() => { });
|
|
32
|
+
await rm(temporary, { force: true });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const metadata = await lstat(path);
|
|
36
|
+
if (!metadata.isFile() || metadata.isSymbolicLink() || metadata.size > 256)
|
|
37
|
+
throw new Error("Invalid environment identity file");
|
|
38
|
+
const file = await open(path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
|
|
39
|
+
try {
|
|
40
|
+
const actual = await file.stat();
|
|
41
|
+
if (!actual.isFile() ||
|
|
42
|
+
actual.size > 256 ||
|
|
43
|
+
actual.ino !== metadata.ino ||
|
|
44
|
+
actual.dev !== metadata.dev)
|
|
45
|
+
throw new Error("Environment identity changed while opening");
|
|
46
|
+
const bytes = Buffer.alloc(257);
|
|
47
|
+
const { bytesRead } = await file.read(bytes, 0, bytes.length, 0);
|
|
48
|
+
if (bytesRead > 256)
|
|
49
|
+
throw new Error("Environment identity exceeds size limit");
|
|
50
|
+
const value = JSON.parse(bytes.subarray(0, bytesRead).toString("utf8"));
|
|
51
|
+
if (value?.version !== 1 || typeof value.id !== "string" || !UUID.test(value.id))
|
|
52
|
+
throw new Error("Invalid environment identity");
|
|
53
|
+
return value.id;
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
await file.close();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export function describeEnvironment(id, kind) {
|
|
60
|
+
return {
|
|
61
|
+
version: 1,
|
|
62
|
+
id,
|
|
63
|
+
name: kind === "desktop" ? "我的电脑" : kind === "project-host" ? "云端项目" : "Hub 工作台",
|
|
64
|
+
kind,
|
|
65
|
+
entryPath: kind === "desktop" ? "/mobile" : "/",
|
|
66
|
+
};
|
|
67
|
+
}
|
package/dist/hub/auth-store.d.ts
CHANGED
|
@@ -41,6 +41,8 @@ export declare class HubAuthStore {
|
|
|
41
41
|
login(input: HubLoginInput, replaceToken?: string): Promise<HubSessionGrant>;
|
|
42
42
|
authenticate(token: string | undefined): HubSession | null;
|
|
43
43
|
listSessions(): HubSession[];
|
|
44
|
+
/** Offline restore must not resurrect sessions revoked after the snapshot. */
|
|
45
|
+
revokeAllSessions(): void;
|
|
44
46
|
revoke(id: string): boolean;
|
|
45
47
|
private checkBootstrap;
|
|
46
48
|
private grant;
|
package/dist/hub/auth-store.js
CHANGED
|
@@ -115,6 +115,13 @@ export class HubAuthStore {
|
|
|
115
115
|
.map((session) => this.toPublic(record, session))
|
|
116
116
|
.sort((a, b) => b.lastSeenAt - a.lastSeenAt);
|
|
117
117
|
}
|
|
118
|
+
/** Offline restore must not resurrect sessions revoked after the snapshot. */
|
|
119
|
+
revokeAllSessions() {
|
|
120
|
+
this.mutate((record) => {
|
|
121
|
+
record.sessions = [];
|
|
122
|
+
return { result: undefined, changed: true };
|
|
123
|
+
});
|
|
124
|
+
}
|
|
118
125
|
revoke(id) {
|
|
119
126
|
return this.mutate((record) => {
|
|
120
127
|
const index = record.sessions.findIndex((session) => session.id === id);
|
package/dist/index.links.d.ts
CHANGED
package/dist/index.links.js
CHANGED
package/dist/index.panels.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { createPanelHttp } from "./panels/http.js";
|
|
2
|
+
export * from "./panels/automations.js";
|
|
3
|
+
export * from "./panels/hub-automations.js";
|
|
2
4
|
export { createPanelManagement, PanelManagementError } from "./panels/management.js";
|
|
3
5
|
export { createPanelRuntime, panelWebCompatibility } from "./panels/runtime.js";
|
|
4
6
|
export { PanelRuntimeServices } from "./panels/runtime-services.js";
|
|
@@ -16,3 +18,8 @@ export * from "./panels/connections.js";
|
|
|
16
18
|
export * from "./panels/tool-jobs.js";
|
|
17
19
|
export * from "./panels/tool-executor.js";
|
|
18
20
|
export * from "./panels/directory-bookmarks.js";
|
|
21
|
+
export * from "./panels/shared-tool-jobs.js";
|
|
22
|
+
export * from "./panels/task-cookies.js";
|
|
23
|
+
export * from "./panels/task-cookie-host.js";
|
|
24
|
+
export * from "./panels/execution-gate.js";
|
|
25
|
+
export * from "./panels/package-mutation.js";
|
package/dist/index.panels.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { createPanelHttp } from "./panels/http.js";
|
|
2
|
+
export * from "./panels/automations.js";
|
|
3
|
+
export * from "./panels/hub-automations.js";
|
|
2
4
|
export { createPanelManagement, PanelManagementError } from "./panels/management.js";
|
|
3
5
|
export { createPanelRuntime, panelWebCompatibility } from "./panels/runtime.js";
|
|
4
6
|
export { PanelRuntimeServices } from "./panels/runtime-services.js";
|
|
@@ -15,3 +17,8 @@ export * from "./panels/connections.js";
|
|
|
15
17
|
export * from "./panels/tool-jobs.js";
|
|
16
18
|
export * from "./panels/tool-executor.js";
|
|
17
19
|
export * from "./panels/directory-bookmarks.js";
|
|
20
|
+
export * from "./panels/shared-tool-jobs.js";
|
|
21
|
+
export * from "./panels/task-cookies.js";
|
|
22
|
+
export * from "./panels/task-cookie-host.js";
|
|
23
|
+
export * from "./panels/execution-gate.js";
|
|
24
|
+
export * from "./panels/package-mutation.js";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Authenticated outbound device transport. Does not replace phone authorization. */
|
|
2
|
+
export * from "./remote-relay/protocol.js";
|
|
3
|
+
export * from "./remote-relay/byte-stream.js";
|
|
4
|
+
export * from "./remote-relay/connector.js";
|
|
5
|
+
export { environmentIdentity } from "./environment-identity.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Authenticated outbound device transport. Does not replace phone authorization. */
|
|
2
|
+
export * from "./remote-relay/protocol.js";
|
|
3
|
+
export * from "./remote-relay/byte-stream.js";
|
|
4
|
+
export * from "./remote-relay/connector.js";
|
|
5
|
+
// Enrollment must use the same persisted identity as the Host's Web descriptor.
|
|
6
|
+
export { environmentIdentity } from "./environment-identity.js";
|
package/dist/index.serve.d.ts
CHANGED
|
@@ -2,3 +2,5 @@
|
|
|
2
2
|
export * from "./serve/headless-server.js";
|
|
3
3
|
export { startProjectControlServer, type ProjectControlServerOptions, } from "./project-runtime/control-server.js";
|
|
4
4
|
export { parseServeArgs, resolveWebAppRoot, resolveWorkerCapabilityModules, resolveWorkerEntry, runServeCli, } from "./serve/cli.js";
|
|
5
|
+
export { backupProjectInstallation, restoreProjectInstallation, type ProjectBackupOptions, type ProjectRestoreOptions, } from "./project-runtime/backup.js";
|
|
6
|
+
export { readProjectSeccompProfile, type ProjectSeccompProfile, } from "./project-runtime/seccomp.js";
|
package/dist/index.serve.js
CHANGED
|
@@ -2,3 +2,5 @@
|
|
|
2
2
|
export * from "./serve/headless-server.js";
|
|
3
3
|
export { startProjectControlServer, } from "./project-runtime/control-server.js";
|
|
4
4
|
export { parseServeArgs, resolveWebAppRoot, resolveWorkerCapabilityModules, resolveWorkerEntry, runServeCli, } from "./serve/cli.js";
|
|
5
|
+
export { backupProjectInstallation, restoreProjectInstallation, } from "./project-runtime/backup.js";
|
|
6
|
+
export { readProjectSeccompProfile, } from "./project-runtime/seccomp.js";
|
package/dist/links/http.d.ts
CHANGED
|
@@ -23,6 +23,9 @@ export declare function createLinkHttp(options: LinkHttpOptions): {
|
|
|
23
23
|
rename: (context: LinkOperationContext, id: string, label: string, expectedRevision: string) => Promise<import("@cjhyy/code-shell-link").MaskedLinkConnection>;
|
|
24
24
|
disconnect: (context: LinkOperationContext, id: string, expectedRevision: string) => Promise<void>;
|
|
25
25
|
startDeviceAuth: (context: LinkOperationContext, input: LinkConnectionInput) => Promise<import("@cjhyy/code-shell-link").LinkAuthorization>;
|
|
26
|
+
startRemoteAuth: (context: LinkOperationContext, input: LinkConnectionInput) => Promise<import("@cjhyy/code-shell-link").LinkAuthorization>;
|
|
27
|
+
completeRemoteAuth: (context: LinkOperationContext, id: string, callbackUrl: string) => Promise<import("@cjhyy/code-shell-link").LinkAuthorization>;
|
|
28
|
+
retryRemoteCleanup: () => Promise<void>;
|
|
26
29
|
authorization: (context: LinkOperationContext, id: string) => Promise<import("@cjhyy/code-shell-link").LinkAuthorization>;
|
|
27
30
|
cancelAuthorization: (context: LinkOperationContext, id: string) => Promise<void>;
|
|
28
31
|
cancelOwner: (ownerId: string) => void;
|
package/dist/links/http.js
CHANGED
|
@@ -80,8 +80,14 @@ export function createLinkHttp(options) {
|
|
|
80
80
|
else {
|
|
81
81
|
const cli = /^\/api\/v1\/links\/providers\/([^/]+)\/cli$/.exec(url.pathname);
|
|
82
82
|
const connection = /^\/api\/v1\/links\/connections\/([^/]+)$/.exec(url.pathname);
|
|
83
|
+
const remoteComplete = /^\/api\/v1\/links\/authorizations\/([^/]+)\/complete$/.exec(url.pathname);
|
|
83
84
|
const authorization = /^\/api\/v1\/links\/authorizations\/([^/]+)$/.exec(url.pathname);
|
|
84
|
-
if (
|
|
85
|
+
if (remoteComplete && method === "POST") {
|
|
86
|
+
const input = await body(request);
|
|
87
|
+
fields(input, ["callbackUrl"]);
|
|
88
|
+
result = await service.completeRemoteAuth(owner, decodeURIComponent(remoteComplete[1]), input.callbackUrl);
|
|
89
|
+
}
|
|
90
|
+
else if (cli && method === "GET")
|
|
85
91
|
result = await service.cliStatus(owner, decodeURIComponent(cli[1]));
|
|
86
92
|
else if (authorization && method === "GET")
|
|
87
93
|
result = await service.authorization(owner, decodeURIComponent(authorization[1]));
|
|
@@ -94,6 +100,7 @@ export function createLinkHttp(options) {
|
|
|
94
100
|
ROOT + "/connections/token",
|
|
95
101
|
ROOT + "/connections/cli",
|
|
96
102
|
ROOT + "/authorizations/device",
|
|
103
|
+
ROOT + "/authorizations/remote",
|
|
97
104
|
].includes(url.pathname)) {
|
|
98
105
|
const input = await body(request);
|
|
99
106
|
const token = url.pathname.endsWith("/token");
|
|
@@ -109,7 +116,9 @@ export function createLinkHttp(options) {
|
|
|
109
116
|
? await service.connectToken(owner, input)
|
|
110
117
|
: url.pathname.endsWith("/cli")
|
|
111
118
|
? await service.connectCli(owner, input)
|
|
112
|
-
:
|
|
119
|
+
: url.pathname.endsWith("/remote")
|
|
120
|
+
? await service.startRemoteAuth(owner, input)
|
|
121
|
+
: await service.startDeviceAuth(owner, input);
|
|
113
122
|
}
|
|
114
123
|
else if (connection && (method === "PATCH" || method === "DELETE")) {
|
|
115
124
|
const input = await body(request);
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type RemoteLinkConfiguration } from "@cjhyy/code-shell-core";
|
|
2
|
+
/** Validate trusted deployment input without echoing credentials in startup errors. */
|
|
3
|
+
export declare function remoteLinkHostConfiguration(input: RemoteLinkConfiguration | undefined, publicOrigin: string | undefined, callbackPath?: "/link/callback" | "/mobile/link/callback"): RemoteLinkConfiguration | undefined;
|
|
4
|
+
export declare function remoteLinkFromEnvironment(env: NodeJS.ProcessEnv, publicOrigin: string | undefined, callbackPath?: "/link/callback" | "/mobile/link/callback"): RemoteLinkConfiguration | undefined;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { beginRemoteLinkAuthorization } from "@cjhyy/code-shell-core";
|
|
2
|
+
/** Validate trusted deployment input without echoing credentials in startup errors. */
|
|
3
|
+
export function remoteLinkHostConfiguration(input, publicOrigin, callbackPath = "/link/callback") {
|
|
4
|
+
if (input === undefined)
|
|
5
|
+
return undefined;
|
|
6
|
+
try {
|
|
7
|
+
if (!["/link/callback", "/mobile/link/callback"].includes(callbackPath))
|
|
8
|
+
throw new Error("path");
|
|
9
|
+
if (!publicOrigin || new URL(publicOrigin).origin !== publicOrigin)
|
|
10
|
+
throw new Error("origin");
|
|
11
|
+
const config = beginRemoteLinkAuthorization(input).configuration;
|
|
12
|
+
if (config.redirectUri !== `${publicOrigin}${callbackPath}`)
|
|
13
|
+
throw new Error("callback");
|
|
14
|
+
return Object.freeze(config);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
throw new Error("Remote Link requires a valid service, client ID and the exact public Link callback URL.");
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function remoteLinkFromEnvironment(env, publicOrigin, callbackPath = "/link/callback") {
|
|
21
|
+
const issuer = env.CODE_SHELL_REMOTE_LINK_ISSUER;
|
|
22
|
+
const clientId = env.CODE_SHELL_REMOTE_LINK_CLIENT_ID;
|
|
23
|
+
const clientSecret = env.CODE_SHELL_REMOTE_LINK_CLIENT_SECRET;
|
|
24
|
+
if (!issuer && !clientId && !clientSecret)
|
|
25
|
+
return undefined;
|
|
26
|
+
return remoteLinkHostConfiguration({
|
|
27
|
+
issuer: issuer ?? "",
|
|
28
|
+
clientId: clientId ?? "",
|
|
29
|
+
redirectUri: `${publicOrigin}${callbackPath}`,
|
|
30
|
+
...(clientSecret ? { clientSecret } : {}),
|
|
31
|
+
}, publicOrigin, callbackPath);
|
|
32
|
+
}
|
package/dist/links/service.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CredentialStore, connectCliLink, getCliLinkStatus, validateLocalLinkToken, type LocalLinkValidationResult } from "@cjhyy/code-shell-core";
|
|
1
|
+
import { CredentialStore, type RemoteLinkConfiguration, connectCliLink, getCliLinkStatus, validateLocalLinkToken, type LocalLinkValidationResult } from "@cjhyy/code-shell-core";
|
|
2
2
|
import { LinkDeviceOAuthBroker, type LocalBrowserAuthToken } from "./device-oauth.js";
|
|
3
3
|
import type { LinkAuthorization, LinkConnectionInput, LinkErrorCode, LinkOperationContext, LinkSnapshot, MaskedLinkConnection, TokenConnectionInput } from "./types.js";
|
|
4
4
|
export declare class LinkServiceError extends Error {
|
|
@@ -13,6 +13,8 @@ export interface LinkServiceOptions {
|
|
|
13
13
|
withMutation?: <T>(write: () => Promise<T>) => Promise<T>;
|
|
14
14
|
onChanged?: () => void | Promise<void>;
|
|
15
15
|
now?: () => number;
|
|
16
|
+
/** Trusted Host configuration; never accepted from a browser request. */
|
|
17
|
+
remoteLink?: () => RemoteLinkConfiguration | undefined;
|
|
16
18
|
/** Test seams. Production delegates to Core's fixed provider and CLI executors. */
|
|
17
19
|
validateToken?: typeof validateLocalLinkToken;
|
|
18
20
|
cliStatus?: typeof getCliLinkStatus;
|
|
@@ -46,6 +48,9 @@ export declare function createLinkService(options?: LinkServiceOptions): {
|
|
|
46
48
|
rename: (context: LinkOperationContext, id: string, label: string, expectedRevision: string) => Promise<MaskedLinkConnection>;
|
|
47
49
|
disconnect: (context: LinkOperationContext, id: string, expectedRevision: string) => Promise<void>;
|
|
48
50
|
startDeviceAuth: (context: LinkOperationContext, input: LinkConnectionInput) => Promise<LinkAuthorization>;
|
|
51
|
+
startRemoteAuth: (context: LinkOperationContext, input: LinkConnectionInput) => Promise<LinkAuthorization>;
|
|
52
|
+
completeRemoteAuth: (context: LinkOperationContext, id: string, callbackUrl: string) => Promise<LinkAuthorization>;
|
|
53
|
+
retryRemoteCleanup: () => Promise<void>;
|
|
49
54
|
authorization: (context: LinkOperationContext, id: string) => Promise<LinkAuthorization>;
|
|
50
55
|
cancelAuthorization: (context: LinkOperationContext, id: string) => Promise<void>;
|
|
51
56
|
cancelOwner: (ownerId: string) => void;
|