@cjhyy/code-shell-server 0.9.51 → 0.10.1

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.
Files changed (93) hide show
  1. package/dist/account/cli.d.ts +23 -0
  2. package/dist/account/cli.js +249 -0
  3. package/dist/account/cloud-contract.d.ts +39 -0
  4. package/dist/account/cloud-contract.js +134 -0
  5. package/dist/account/cloud-types.d.ts +47 -0
  6. package/dist/account/cloud-types.js +1 -0
  7. package/dist/account/feature-service.d.ts +47 -0
  8. package/dist/account/feature-service.js +184 -0
  9. package/dist/account/http.d.ts +17 -0
  10. package/dist/account/http.js +158 -0
  11. package/dist/account/links.d.ts +13 -0
  12. package/dist/account/links.js +137 -0
  13. package/dist/account/manager.d.ts +96 -0
  14. package/dist/account/manager.js +976 -0
  15. package/dist/account/native-lock.d.ts +6 -0
  16. package/dist/account/native-lock.js +26 -0
  17. package/dist/account/native-store.d.ts +32 -0
  18. package/dist/account/native-store.js +162 -0
  19. package/dist/account/profile.d.ts +4 -0
  20. package/dist/account/profile.js +46 -0
  21. package/dist/account/store.d.ts +34 -0
  22. package/dist/account/store.js +118 -0
  23. package/dist/account/types.d.ts +138 -0
  24. package/dist/account/types.js +2 -0
  25. package/dist/desktop-web/http-api.d.ts +4 -0
  26. package/dist/desktop-web/http-api.js +12 -4
  27. package/dist/environment-identity.d.ts +1 -1
  28. package/dist/environment-identity.js +2 -1
  29. package/dist/headless-worker-env.d.ts +2 -0
  30. package/dist/headless-worker-env.js +15 -0
  31. package/dist/hub/approval-lease.js +6 -1
  32. package/dist/hub/configuration.d.ts +11 -0
  33. package/dist/hub/configuration.js +34 -1
  34. package/dist/hub/model-parameter-metadata.d.ts +26 -0
  35. package/dist/hub/model-parameter-metadata.js +92 -0
  36. package/dist/hub/run-replay.js +1 -1
  37. package/dist/hub/session-management.js +1 -1
  38. package/dist/index.account.d.ts +12 -0
  39. package/dist/index.account.js +12 -0
  40. package/dist/index.mobile-remote.d.ts +2 -0
  41. package/dist/index.mobile-remote.js +1 -0
  42. package/dist/index.worker.d.ts +1 -0
  43. package/dist/index.worker.js +1 -0
  44. package/dist/mobile-remote/account-relay-access.d.ts +59 -0
  45. package/dist/mobile-remote/account-relay-access.js +174 -0
  46. package/dist/mobile-remote/approval-clock.d.ts +3 -0
  47. package/dist/mobile-remote/approval-clock.js +15 -0
  48. package/dist/mobile-remote/codex-room-agent.d.ts +6 -0
  49. package/dist/mobile-remote/codex-room-agent.js +17 -1
  50. package/dist/mobile-remote/mobile-chat-turn.d.ts +4 -0
  51. package/dist/mobile-remote/mobile-chat-turn.js +3 -0
  52. package/dist/mobile-remote/mobile-client-event-validator.js +47 -2
  53. package/dist/mobile-remote/mobile-upload-service.d.ts +9 -0
  54. package/dist/mobile-remote/mobile-upload-service.js +25 -0
  55. package/dist/mobile-remote/remote-host-manager.d.ts +21 -4
  56. package/dist/mobile-remote/remote-host-manager.js +142 -25
  57. package/dist/mobile-remote/resident-agent.d.ts +14 -0
  58. package/dist/mobile-remote/resident-agent.js +38 -11
  59. package/dist/mobile-remote/room-history.d.ts +7 -0
  60. package/dist/mobile-remote/room-history.js +56 -1
  61. package/dist/mobile-remote/room-manager.d.ts +216 -9
  62. package/dist/mobile-remote/room-manager.js +700 -65
  63. package/dist/mobile-remote/room-process-stop.d.ts +15 -0
  64. package/dist/mobile-remote/room-process-stop.js +41 -0
  65. package/dist/mobile-remote/trusted-device-store.d.ts +12 -0
  66. package/dist/mobile-remote/trusted-device-store.js +54 -1
  67. package/dist/mobile-remote/turn-queue.d.ts +183 -0
  68. package/dist/mobile-remote/turn-queue.js +422 -0
  69. package/dist/mobile-remote/types.d.ts +1 -1
  70. package/dist/panels/agent-task-host.js +6 -12
  71. package/dist/panels/agent-task-service.d.ts +2 -0
  72. package/dist/panels/agent-task-service.js +7 -0
  73. package/dist/panels/browser-capture.d.ts +1 -15
  74. package/dist/panels/browser-capture.js +1 -35
  75. package/dist/panels/directory-picker.d.ts +48 -0
  76. package/dist/panels/directory-picker.js +247 -0
  77. package/dist/panels/http.d.ts +15 -15
  78. package/dist/panels/management-http.d.ts +9 -9
  79. package/dist/panels/process-service.d.ts +5 -4
  80. package/dist/panels/process-service.js +54 -16
  81. package/dist/panels/runtime.js +148 -30
  82. package/dist/panels/tool-jobs-storage.d.ts +4 -0
  83. package/dist/panels/tool-jobs-storage.js +111 -34
  84. package/dist/panels/types.d.ts +1 -97
  85. package/dist/remote-relay/connector.d.ts +8 -0
  86. package/dist/remote-relay/connector.js +29 -2
  87. package/dist/remote-relay/protocol.d.ts +21 -0
  88. package/dist/remote-relay/protocol.js +31 -0
  89. package/dist/serve/cli.js +6 -0
  90. package/dist/serve/headless-server.js +24 -19
  91. package/dist/sessions-service.d.ts +4 -0
  92. package/dist/sessions-service.js +14 -1
  93. package/package.json +10 -6
@@ -1,12 +1,13 @@
1
1
  import { WebSocket } from "ws";
2
2
  import { openRelayStream } from "./local-stream.js";
3
- import { isRelayHostId, isRelayOrigin, isRelayToken, parseRelayControlMessage, RELAY_CONNECT_PATH, RELAY_CONTROL_MAX_BYTES, RELAY_MAX_STREAMS, RELAY_PING_INTERVAL_MS, RELAY_PONG_TIMEOUT_MS, RELAY_SETUP_TIMEOUT_MS, } from "./protocol.js";
3
+ import { isRelayHostId, isRelayOrigin, isRelayToken, parseRelayControlMessage, RELAY_CONNECT_PATH, RELAY_CONTROL_MAX_BYTES, RELAY_FEATURE_PAIRING, RELAY_FEATURES_HEADER, RELAY_MAX_STREAMS, RELAY_PING_INTERVAL_MS, RELAY_PONG_TIMEOUT_MS, RELAY_SETUP_TIMEOUT_MS, } from "./protocol.js";
4
4
  export function createDeviceRelayConnector(options) {
5
5
  if (!isRelayOrigin(options.relayOrigin) ||
6
6
  !isRelayOrigin(options.publicOrigin) ||
7
7
  !isRelayHostId(options.hostId) ||
8
8
  (!options.getCredential && !isRelayToken(options.credential)) ||
9
9
  (options.getCredential !== undefined && typeof options.getCredential !== "function") ||
10
+ (options.issuePairing !== undefined && typeof options.issuePairing !== "function") ||
10
11
  !options.localHost ||
11
12
  !Number.isInteger(options.localHost.port) ||
12
13
  options.localHost.port < 1 ||
@@ -96,7 +97,10 @@ export function createDeviceRelayConnector(options) {
96
97
  const url = new URL(RELAY_CONNECT_PATH, config.relayOrigin);
97
98
  url.protocol = "wss:";
98
99
  const ws = new WebSocket(url, {
99
- headers: { Authorization: `Bearer ${credential}` },
100
+ headers: {
101
+ Authorization: `Bearer ${credential}`,
102
+ ...(config.issuePairing ? { [RELAY_FEATURES_HEADER]: RELAY_FEATURE_PAIRING } : {}),
103
+ },
100
104
  ca: config.ca,
101
105
  followRedirects: false,
102
106
  perMessageDeflate: false,
@@ -180,6 +184,29 @@ export function createDeviceRelayConnector(options) {
180
184
  state("ready");
181
185
  return;
182
186
  }
187
+ if (message.type === "pair") {
188
+ if (!config.issuePairing || !leaseId || message.leaseId !== leaseId)
189
+ return ws.terminate();
190
+ let reply = {
191
+ type: "pair-failed",
192
+ v: 1,
193
+ leaseId,
194
+ requestId: message.requestId,
195
+ };
196
+ try {
197
+ const issued = config.issuePairing();
198
+ if (isRelayToken(issued?.token) &&
199
+ Number.isSafeInteger(issued.expiresAt) &&
200
+ issued.expiresAt > Date.now())
201
+ reply = { ...reply, type: "paired", token: issued.token, expiresAt: issued.expiresAt };
202
+ }
203
+ catch {
204
+ /* Refused or unavailable: the directory reports failure to its owner. */
205
+ }
206
+ if (current() && ws.readyState === ws.OPEN)
207
+ ws.send(JSON.stringify(reply));
208
+ return;
209
+ }
183
210
  if (message.type !== "open" ||
184
211
  !leaseId ||
185
212
  message.leaseId !== leaseId ||
@@ -9,6 +9,10 @@ export declare const RELAY_STREAMS_PATH = "/api/v1/remote-hosts/streams/";
9
9
  export declare const RELAY_SETUP_TIMEOUT_MS = 10000;
10
10
  export declare const RELAY_PING_INTERVAL_MS = 20000;
11
11
  export declare const RELAY_PONG_TIMEOUT_MS = 60000;
12
+ /** Control-upgrade header listing optional features; older directories ignore it. */
13
+ export declare const RELAY_FEATURES_HEADER = "X-CodeShell-Relay-Features";
14
+ /** The directory may ask this computer for a one-time phone pairing token. */
15
+ export declare const RELAY_FEATURE_PAIRING = "pairing";
12
16
  export type RelayControlMessage = {
13
17
  type: "welcome";
14
18
  v: 1;
@@ -30,6 +34,23 @@ export type RelayControlMessage = {
30
34
  v: 1;
31
35
  leaseId: string;
32
36
  streamId: string;
37
+ } | {
38
+ type: "pair";
39
+ v: 1;
40
+ leaseId: string;
41
+ requestId: string;
42
+ } | {
43
+ type: "paired";
44
+ v: 1;
45
+ leaseId: string;
46
+ requestId: string;
47
+ token: string;
48
+ expiresAt: number;
49
+ } | {
50
+ type: "pair-failed";
51
+ v: 1;
52
+ leaseId: string;
53
+ requestId: string;
33
54
  };
34
55
  export declare function isRelayToken(value: unknown): value is string;
35
56
  export declare function isRelayHostId(value: unknown): value is string;
@@ -9,6 +9,10 @@ export const RELAY_STREAMS_PATH = "/api/v1/remote-hosts/streams/";
9
9
  export const RELAY_SETUP_TIMEOUT_MS = 10_000;
10
10
  export const RELAY_PING_INTERVAL_MS = 20_000;
11
11
  export const RELAY_PONG_TIMEOUT_MS = 60_000;
12
+ /** Control-upgrade header listing optional features; older directories ignore it. */
13
+ export const RELAY_FEATURES_HEADER = "X-CodeShell-Relay-Features";
14
+ /** The directory may ask this computer for a one-time phone pairing token. */
15
+ export const RELAY_FEATURE_PAIRING = "pairing";
12
16
  export function isRelayToken(value) {
13
17
  return typeof value === "string" && /^[A-Za-z0-9_-]{43}$/.test(value);
14
18
  }
@@ -60,5 +64,32 @@ export function parseRelayControlMessage(value) {
60
64
  case "failed":
61
65
  if (isRelayToken(message.streamId))
62
66
  return { type: "failed", v: 1, leaseId: message.leaseId, streamId: message.streamId };
67
+ return;
68
+ case "pair":
69
+ if (isRelayToken(message.requestId))
70
+ return { type: "pair", v: 1, leaseId: message.leaseId, requestId: message.requestId };
71
+ return;
72
+ case "paired":
73
+ if (isRelayToken(message.requestId) &&
74
+ isRelayToken(message.token) &&
75
+ Number.isSafeInteger(message.expiresAt) &&
76
+ message.expiresAt > 0)
77
+ return {
78
+ type: "paired",
79
+ v: 1,
80
+ leaseId: message.leaseId,
81
+ requestId: message.requestId,
82
+ token: message.token,
83
+ expiresAt: message.expiresAt,
84
+ };
85
+ return;
86
+ case "pair-failed":
87
+ if (isRelayToken(message.requestId))
88
+ return {
89
+ type: "pair-failed",
90
+ v: 1,
91
+ leaseId: message.leaseId,
92
+ requestId: message.requestId,
93
+ };
63
94
  }
64
95
  }
package/dist/serve/cli.js CHANGED
@@ -12,6 +12,7 @@ import { startHeadlessServer } from "./headless-server.js";
12
12
  import { startProjectControlServer } from "../project-runtime/control-server.js";
13
13
  import { configureModelTracing } from "@cjhyy/code-shell-core/internal";
14
14
  const SERVE_HELP = `Usage: code-shell-serve [options]
15
+ code-shell-serve account <login|status|features|links|logout|register> [options]
15
16
 
16
17
  Run the CodeShell headless Web host for a workspace.
17
18
 
@@ -146,6 +147,11 @@ export function resolveWebAppRoot() {
146
147
  }
147
148
  }
148
149
  export async function runServeCli(argv = process.argv.slice(2)) {
150
+ if (argv[0] === "account") {
151
+ const { runAccountCli } = await import("../account/cli.js");
152
+ await runAccountCli(argv.slice(1));
153
+ return;
154
+ }
149
155
  if (argv.includes("--help") || argv.includes("-h")) {
150
156
  console.log(SERVE_HELP);
151
157
  return;
@@ -42,6 +42,7 @@ import { createHubSessions, hubSessionPreview, readHubSessionState, readHubTrans
42
42
  import { createHubFiles } from "../hub/files.js";
43
43
  import { createSessionHttpResponses } from "../session-http-responses.js";
44
44
  import { WorkerBridgeCore, previewLine } from "../worker-bridge-core.js";
45
+ import { headlessWorkerEnv } from "../headless-worker-env.js";
45
46
  import { HostRuntime, interceptHostRequest } from "@cjhyy/code-shell-host";
46
47
  import { createModelTraceHttp } from "../model-trace-http.js";
47
48
  export async function startHeadlessServer(opts) {
@@ -440,16 +441,8 @@ export async function startHeadlessServer(opts) {
440
441
  entryPath: opts.workerEntryPath,
441
442
  execPath: opts.execPath,
442
443
  fallbackCwd: () => opts.cwd,
443
- buildEnv: () => ({
444
- ...process.env,
445
- CODE_SHELL_REMOTE_LINK_CLIENT_SECRET: undefined,
446
- // These switch on Desktop-main-only IPC clients in the worker.
447
- CODE_SHELL_OPERATION_BATCH_ACCESS: undefined,
448
- CODE_SHELL_MODEL_REQUEST_SIGNING: undefined,
449
- CODE_SHELL_HOST_MCP_SERVERS: undefined,
450
- CODE_SHELL_WORKSPACE_ACCESS: undefined,
444
+ buildEnv: () => headlessWorkerEnv(process.env, {
451
445
  CODE_SHELL_DATA_ROOT: workerDataRoot,
452
- CODE_SHELL_CREDENTIAL_ACCESS: "local",
453
446
  CODE_SHELL_CAPABILITY_MODULES: opts.workerCapabilityModules ??
454
447
  `${import.meta.resolve("@cjhyy/code-shell-server/links")}#createLinkProviderModule`,
455
448
  }),
@@ -557,16 +550,8 @@ export async function startHeadlessServer(opts) {
557
550
  publicPathPrefix: opts.publicPathPrefix,
558
551
  agentTaskOptions: {
559
552
  workerEntryPath: opts.workerEntryPath,
560
- buildEnv: () => ({
561
- ...process.env,
562
- CODE_SHELL_REMOTE_LINK_CLIENT_SECRET: undefined,
563
- // These switch on Desktop-main-only IPC clients in the worker.
564
- CODE_SHELL_OPERATION_BATCH_ACCESS: undefined,
565
- CODE_SHELL_MODEL_REQUEST_SIGNING: undefined,
566
- CODE_SHELL_HOST_MCP_SERVERS: undefined,
567
- CODE_SHELL_WORKSPACE_ACCESS: undefined,
553
+ buildEnv: () => headlessWorkerEnv(process.env, {
568
554
  CODE_SHELL_DATA_ROOT: workerDataRoot,
569
- CODE_SHELL_CREDENTIAL_ACCESS: "local",
570
555
  CODE_SHELL_CAPABILITY_MODULES: opts.workerCapabilityModules ??
571
556
  `${import.meta.resolve("@cjhyy/code-shell-server/links")}#createLinkProviderModule`,
572
557
  }),
@@ -895,6 +880,21 @@ export async function startHeadlessServer(opts) {
895
880
  }
896
881
  let approvalId;
897
882
  let approvalRouted = false;
883
+ if (hubAuth && parsed.method === "agent/approvalKeepWaiting") {
884
+ const requestId = parsed.params?.requestId;
885
+ const approval = typeof requestId === "string" ? leases.get(requestId) : undefined;
886
+ if (!approval || approval.sessionId !== parsed.params?.sessionId) {
887
+ sendToTab(ws, hostQueryError(parsed.id ?? null, -32009, "approval is no longer pending"), "approval");
888
+ return;
889
+ }
890
+ // Route fields belong to the worker's request, never to browser input.
891
+ parsed.params = {
892
+ sessionId: approval.sessionId,
893
+ requestId: approval.requestId,
894
+ ...(approval.connectionId !== undefined ? { connectionId: approval.connectionId } : {}),
895
+ ...(approval.generation !== undefined ? { generation: approval.generation } : {}),
896
+ };
897
+ }
898
898
  if (hubAuth && parsed.method === "agent/approve") {
899
899
  const requestId = parsed.params?.requestId;
900
900
  const approval = typeof requestId === "string" ? leases.get(requestId) : undefined;
@@ -1431,7 +1431,12 @@ function readOptionalHubSessionState(sessionManager, sessionId) {
1431
1431
  throw error;
1432
1432
  }
1433
1433
  }
1434
- const SERVE_ALLOWED_WORKER_METHODS = new Set(["agent/run", "agent/approve", "agent/cancel"]);
1434
+ const SERVE_ALLOWED_WORKER_METHODS = new Set([
1435
+ "agent/run",
1436
+ "agent/approve",
1437
+ "agent/approvalKeepWaiting",
1438
+ "agent/cancel",
1439
+ ]);
1435
1440
  /**
1436
1441
  * The no-account Web host deliberately exposes only the methods used by its
1437
1442
  * bundled SPA. Passcode possession grants control of this workspace, not raw
@@ -40,6 +40,8 @@ export interface DiskSessionMeta {
40
40
  title: string;
41
41
  model?: string;
42
42
  provider?: string;
43
+ /** Explicit configured text connection persisted by the Session's last native run. */
44
+ modelConnectionId?: string;
43
45
  updatedAt: number;
44
46
  /** Durable creation time, when supplied by the engine's state schema. */
45
47
  createdAt?: number;
@@ -73,6 +75,8 @@ export declare function listDiskSessions(opts: {
73
75
  cursor?: string;
74
76
  /** Display-only project roots filter, applied before pagination. */
75
77
  cwds?: readonly string[];
78
+ /** Check one candidate against the same visibility rules without reading unrelated states. */
79
+ sessionId?: string;
76
80
  includeArchived?: boolean;
77
81
  /** Host read models may include children; ordinary sidebar queries stay top-level. */
78
82
  includeSubagents?: boolean;
@@ -205,6 +205,14 @@ function decodeDiskSessionCursor(value) {
205
205
  * - non-empty → sub-agent → show only for the matching parent query
206
206
  */
207
207
  export async function listDiskSessions(opts, baseDir = sessionsRoot()) {
208
+ if (opts.sessionId !== undefined &&
209
+ (typeof opts.sessionId !== "string" ||
210
+ !SAFE_ID.test(opts.sessionId) ||
211
+ opts.sessionId.length > 128 ||
212
+ opts.sessionId === "." ||
213
+ opts.sessionId.includes(".."))) {
214
+ throw new Error("invalid session id");
215
+ }
208
216
  const parentSessionId = opts.parentSessionId;
209
217
  if (parentSessionId !== undefined &&
210
218
  (typeof parentSessionId !== "string" ||
@@ -224,7 +232,7 @@ export async function listDiskSessions(opts, baseDir = sessionsRoot()) {
224
232
  : normalized;
225
233
  };
226
234
  const cwdRoots = opts.cwds?.map(pathKey);
227
- const dirs = (await scanSessionDirectories(baseDir)).filter(({ id }) => !isInternalEphemeralSessionId(id));
235
+ const dirs = (await scanSessionDirectories(baseDir)).filter(({ id }) => !isInternalEphemeralSessionId(id) && (opts.sessionId === undefined || id === opts.sessionId));
228
236
  dirs.sort(compareDiskSessionPosition);
229
237
  const decodedCursor = decodeDiskSessionCursor(opts.cursor);
230
238
  if (decodedCursor === null)
@@ -342,6 +350,11 @@ export async function listDiskSessions(opts, baseDir = sessionsRoot()) {
342
350
  engineSessionId: id,
343
351
  ...(typeof state.model === "string" ? { model: state.model } : {}),
344
352
  ...(typeof state.provider === "string" ? { provider: state.provider } : {}),
353
+ ...(typeof state.modelConnectionId === "string" &&
354
+ state.modelConnectionId.length > 0 &&
355
+ state.modelConnectionId.length <= 256
356
+ ? { modelConnectionId: state.modelConnectionId }
357
+ : {}),
345
358
  cwd: typeof state.cwd === "string" ? state.cwd : "",
346
359
  // Prefer the LLM-generated title (now persisted to state); fall back to
347
360
  // the raw first-message summary, then the id. This lets the disk rebuild
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cjhyy/code-shell-server",
3
- "version": "0.9.51",
3
+ "version": "0.10.1",
4
4
  "description": "CodeShell server transport layer — mobile remote HTTP/WS host, pairing, passcode gate, tunnel, rooms, uploads, and session/attachment services. Electron-free; precursor of the auth gateway.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -45,6 +45,10 @@
45
45
  "./panels": {
46
46
  "types": "./dist/index.panels.d.ts",
47
47
  "import": "./dist/index.panels.js"
48
+ },
49
+ "./account": {
50
+ "types": "./dist/index.account.d.ts",
51
+ "import": "./dist/index.account.js"
48
52
  }
49
53
  },
50
54
  "bin": {
@@ -62,14 +66,14 @@
62
66
  "clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\""
63
67
  },
64
68
  "dependencies": {
65
- "@cjhyy/code-shell-capability-coding": "0.9.51",
66
- "@cjhyy/code-shell-core": "0.9.51",
67
- "@cjhyy/code-shell-web": "0.9.51",
69
+ "@cjhyy/code-shell-capability-coding": "0.10.1",
70
+ "@cjhyy/code-shell-core": "0.10.1",
71
+ "@cjhyy/code-shell-web": "0.10.1",
68
72
  "@modelcontextprotocol/sdk": "^1.31.0",
69
73
  "@types/ws": "^8.18.1",
70
74
  "ws": "^8.21.0",
71
- "@cjhyy/code-shell-link": "0.9.51",
72
- "@cjhyy/code-shell-host": "0.9.51"
75
+ "@cjhyy/code-shell-link": "0.10.1",
76
+ "@cjhyy/code-shell-host": "0.10.1"
73
77
  },
74
78
  "engines": {
75
79
  "node": ">=20.10"