@cjhyy/code-shell-server 0.9.44 → 0.9.46
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/dist/attachment-service.d.ts +2 -0
- package/dist/attachment-service.js +40 -27
- package/dist/bin/code-shell-link-cleanup.d.ts +2 -0
- package/dist/bin/code-shell-link-cleanup.js +3 -0
- package/dist/desktop-web/http-api.d.ts +3 -0
- package/dist/desktop-web/http-api.js +16 -1
- package/dist/hub/configuration.d.ts +6 -3
- package/dist/hub/configuration.js +19 -6
- package/dist/hub/mcp-probe.d.ts +4 -24
- package/dist/hub/mcp-probe.js +18 -389
- package/dist/hub/model-probe.d.ts +2 -20
- package/dist/hub/model-probe.js +2 -193
- package/dist/hub/run-replay.d.ts +7 -1
- package/dist/hub/run-replay.js +10 -2
- package/dist/hub/session-management.js +18 -53
- package/dist/hub/skills-management.js +6 -8
- package/dist/hub/uploads.d.ts +10 -0
- package/dist/hub/uploads.js +107 -15
- package/dist/index.desktop-web.d.ts +1 -0
- package/dist/index.desktop-web.js +1 -0
- package/dist/index.links.d.ts +3 -0
- package/dist/index.links.js +2 -0
- package/dist/index.panels.d.ts +1 -0
- package/dist/index.panels.js +1 -0
- package/dist/index.serve.d.ts +1 -0
- package/dist/index.serve.js +1 -0
- package/dist/index.storage.d.ts +1 -0
- package/dist/index.storage.js +1 -0
- package/dist/links/catalog.js +2 -0
- package/dist/links/diagnostics.d.ts +60 -0
- package/dist/links/diagnostics.js +184 -0
- package/dist/links/http.d.ts +10 -8
- package/dist/links/http.js +144 -125
- package/dist/links/legacy-cleanup-cli.d.ts +3 -0
- package/dist/links/legacy-cleanup-cli.js +64 -0
- package/dist/links/providers/defaults.d.ts +5 -0
- package/dist/links/providers/defaults.js +782 -0
- package/dist/links/providers/index.d.ts +7 -0
- package/dist/links/providers/index.js +22 -0
- package/dist/links/remote-catalog.d.ts +10 -3
- package/dist/links/remote-catalog.js +76 -29
- package/dist/links/runtime/operation.d.ts +9 -0
- package/dist/links/runtime/operation.js +16 -0
- package/dist/links/service.d.ts +18 -2
- package/dist/links/service.js +593 -62
- package/dist/links/types.d.ts +21 -1
- package/dist/mobile-remote/codex-parse.d.ts +3 -1
- package/dist/mobile-remote/codex-parse.js +45 -11
- package/dist/mobile-remote/mobile-chat-turn.d.ts +1 -0
- package/dist/mobile-remote/mobile-chat-turn.js +3 -0
- package/dist/mobile-remote/mobile-client-event-validator.js +13 -0
- package/dist/mobile-remote/mobile-history.d.ts +10 -1
- package/dist/mobile-remote/mobile-history.js +24 -16
- package/dist/mobile-remote/remote-host-manager.d.ts +8 -0
- package/dist/mobile-remote/remote-host-manager.js +23 -8
- package/dist/mobile-remote/resident-agent.js +17 -4
- package/dist/mobile-remote/room-manager.d.ts +23 -13
- package/dist/mobile-remote/room-manager.js +102 -20
- package/dist/mobile-remote/room-model-tracing.d.ts +18 -0
- package/dist/mobile-remote/room-model-tracing.js +196 -0
- package/dist/model-trace-history.d.ts +29 -0
- package/dist/model-trace-history.js +198 -0
- package/dist/model-trace-http.d.ts +14 -0
- package/dist/model-trace-http.js +105 -0
- package/dist/panels/agent-task-host.js +1 -0
- package/dist/panels/directory-bookmarks.js +62 -29
- package/dist/panels/http.d.ts +1 -0
- package/dist/panels/hub-binding.js +5 -5
- package/dist/panels/model-trace-channel.d.ts +21 -0
- package/dist/panels/model-trace-channel.js +110 -0
- package/dist/panels/model-traces.d.ts +39 -0
- package/dist/panels/model-traces.js +222 -0
- package/dist/panels/process-service.d.ts +12 -2
- package/dist/panels/process-service.js +49 -6
- package/dist/panels/runtime.d.ts +1 -0
- package/dist/panels/runtime.js +54 -6
- package/dist/panels/tool-jobs.d.ts +2 -0
- package/dist/panels/tool-jobs.js +21 -1
- package/dist/serve/cli.js +8 -1
- package/dist/serve/headless-server.js +68 -14
- package/dist/sessions-service.js +17 -27
- package/dist/worker-bridge-core.js +11 -0
- package/package.json +8 -7
|
@@ -65,6 +65,8 @@ export declare const DRAFT_ATTACHMENT_TTL_MS: number;
|
|
|
65
65
|
export declare const SENT_ATTACHMENT_TTL_MS: number;
|
|
66
66
|
export declare const MAX_STAGED_IMAGE_BYTES: number;
|
|
67
67
|
export declare const MAX_STAGED_FILE_BYTES: number;
|
|
68
|
+
/** Whether image staging accepts this MIME type (after normalization). */
|
|
69
|
+
export declare function isStageableImageMime(mime: string): boolean;
|
|
68
70
|
export declare function stageImageDataUrl(input: StageImageDataUrlInput): Promise<InputAttachmentMeta>;
|
|
69
71
|
/** Stage already-transferred bytes without expanding them back through base64. */
|
|
70
72
|
export declare function stageImageBytes(input: StageImageBytesInput): Promise<InputAttachmentMeta>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import { constants as fsConstants } from "node:fs";
|
|
3
3
|
import { appendFile, lstat, mkdir, open, readdir, realpath, rename, rm, stat, writeFile, } from "node:fs/promises";
|
|
4
4
|
import { basename, extname, join, relative, resolve, sep } from "node:path";
|
|
@@ -49,7 +49,10 @@ const IMAGE_MIME_EXT = {
|
|
|
49
49
|
"image/gif": ".gif",
|
|
50
50
|
};
|
|
51
51
|
const ALLOWED_IMAGE_MIMES = new Set(Object.keys(IMAGE_MIME_EXT));
|
|
52
|
-
|
|
52
|
+
/** Whether image staging accepts this MIME type (after normalization). */
|
|
53
|
+
export function isStageableImageMime(mime) {
|
|
54
|
+
return ALLOWED_IMAGE_MIMES.has(normalizeMime(mime));
|
|
55
|
+
}
|
|
53
56
|
export async function stageImageDataUrl(input) {
|
|
54
57
|
const parsed = parseDataUrl(input.dataUrl);
|
|
55
58
|
if (!parsed || !ALLOWED_IMAGE_MIMES.has(parsed.mime)) {
|
|
@@ -128,9 +131,8 @@ async function stageImageBuffer(input, mime, buffer) {
|
|
|
128
131
|
}
|
|
129
132
|
const relPath = normalizePath(relative(cwd, absPath));
|
|
130
133
|
const createdAt = Date.now();
|
|
131
|
-
idCounter += 1;
|
|
132
134
|
const meta = {
|
|
133
|
-
id: `att_${sha16}_${
|
|
135
|
+
id: `att_${sha16}_${randomUUID()}`,
|
|
134
136
|
sessionId: input.sessionId,
|
|
135
137
|
kind: "image",
|
|
136
138
|
origin: input.origin,
|
|
@@ -181,9 +183,8 @@ export async function stageFileBytes(input) {
|
|
|
181
183
|
}
|
|
182
184
|
const relPath = normalizePath(relative(cwd, absPath));
|
|
183
185
|
const createdAt = Date.now();
|
|
184
|
-
idCounter += 1;
|
|
185
186
|
const meta = {
|
|
186
|
-
id: `att_${sha16}_${
|
|
187
|
+
id: `att_${sha16}_${randomUUID()}`,
|
|
187
188
|
sessionId: input.sessionId,
|
|
188
189
|
kind: "file",
|
|
189
190
|
origin: input.origin,
|
|
@@ -286,29 +287,41 @@ export async function cleanupAttachments(input) {
|
|
|
286
287
|
sessionsRemoved.push(sessionId);
|
|
287
288
|
continue;
|
|
288
289
|
}
|
|
289
|
-
const
|
|
290
|
-
for (const state of
|
|
291
|
-
|
|
292
|
-
const sentAt = state.sentAt;
|
|
293
|
-
const isSent = typeof sentAt === "number";
|
|
294
|
-
const ttl = isSent ? SENT_ATTACHMENT_TTL_MS : DRAFT_ATTACHMENT_TTL_MS;
|
|
295
|
-
const basis = isSent ? sentAt : createdAt;
|
|
296
|
-
if (!basis || now - basis < ttl)
|
|
297
|
-
continue;
|
|
298
|
-
if (!state.absPath || !(await pathExistsInside(state.absPath, root)))
|
|
290
|
+
const byPath = new Map();
|
|
291
|
+
for (const state of latestAttachmentState(records).values()) {
|
|
292
|
+
if (!state.absPath)
|
|
299
293
|
continue;
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
cleanedAt: now,
|
|
294
|
+
const references = byPath.get(state.absPath) ?? [];
|
|
295
|
+
references.push(state);
|
|
296
|
+
byPath.set(state.absPath, references);
|
|
297
|
+
}
|
|
298
|
+
for (const [absPath, references] of byPath) {
|
|
299
|
+
const expired = references.filter((state) => {
|
|
300
|
+
const isSent = typeof state.sentAt === "number";
|
|
301
|
+
const ttl = isSent ? SENT_ATTACHMENT_TTL_MS : DRAFT_ATTACHMENT_TTL_MS;
|
|
302
|
+
const basis = isSent ? state.sentAt : (state.createdAt ?? 0);
|
|
303
|
+
return !!basis && now - basis >= ttl;
|
|
311
304
|
});
|
|
305
|
+
if (expired.length === 0 || !(await pathExistsInside(absPath, root)))
|
|
306
|
+
continue;
|
|
307
|
+
// Staging deduplicates image bytes across IDs at this same canonical path.
|
|
308
|
+
// Retire expired IDs independently; a live or unknown-age reference keeps the file.
|
|
309
|
+
if (expired.length === references.length) {
|
|
310
|
+
await rm(absPath, { force: true }).catch(() => undefined);
|
|
311
|
+
removed.push(absPath);
|
|
312
|
+
}
|
|
313
|
+
for (const state of expired) {
|
|
314
|
+
await appendManifest(sessionDir, {
|
|
315
|
+
event: "cleanup",
|
|
316
|
+
id: state.id,
|
|
317
|
+
sessionId,
|
|
318
|
+
absPath: state.absPath,
|
|
319
|
+
relPath: state.relPath,
|
|
320
|
+
path: state.path,
|
|
321
|
+
sha256: state.sha256,
|
|
322
|
+
cleanedAt: now,
|
|
323
|
+
});
|
|
324
|
+
}
|
|
312
325
|
}
|
|
313
326
|
}
|
|
314
327
|
return { removed, sessionsRemoved };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
import type { ModelTraceListQuery } from "@cjhyy/code-shell-core/internal";
|
|
2
3
|
import type { TrustedDeviceStore } from "../mobile-remote/trusted-device-store.js";
|
|
3
4
|
export interface DesktopWebRequestContext {
|
|
4
5
|
deviceId: string;
|
|
@@ -14,6 +15,8 @@ export interface DesktopWebApiOptions {
|
|
|
14
15
|
sessionRootDir: string;
|
|
15
16
|
/** Input is untrusted. Return only a workspace already known to Desktop. */
|
|
16
17
|
resolveWorkspace: (input: string | undefined, deviceId: string) => string | undefined | Promise<string | undefined>;
|
|
18
|
+
/** Related diagnostic scopes are resolved by Desktop's project authority, per device. */
|
|
19
|
+
resolveModelTraceScopes?: (cwd: string, deviceId: string) => ModelTraceListQuery["workspaceScopes"] | Promise<ModelTraceListQuery["workspaceScopes"]>;
|
|
17
20
|
/**
|
|
18
21
|
* The existing Desktop AgentBridge owns this run/configuration admission lock.
|
|
19
22
|
* Reject active runs with status 409, hold the lock through write + hot reload,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { describeEnvironment, environmentIdentity } from "../environment-identity.js";
|
|
2
2
|
import { randomBytes, randomUUID } from "node:crypto";
|
|
3
3
|
import { isAbsolute } from "node:path";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { createModelTraceHttp } from "../model-trace-http.js";
|
|
4
6
|
import { createHubConfiguration, HubConfigurationError } from "../hub/configuration.js";
|
|
5
7
|
import { createHubFiles } from "../hub/files.js";
|
|
6
8
|
import { createHubMcpConfiguration } from "../hub/mcp-configuration.js";
|
|
@@ -180,6 +182,8 @@ export function createDesktopWebApi(options) {
|
|
|
180
182
|
function createServices(cwd) {
|
|
181
183
|
const shared = {
|
|
182
184
|
cwd,
|
|
185
|
+
traceStorageDir: join(options.sessionRootDir, ".model-traces"),
|
|
186
|
+
traceSource: "desktop-web",
|
|
183
187
|
isAuthorized: authorized,
|
|
184
188
|
ownerId: owner,
|
|
185
189
|
withMutation: async (write) => {
|
|
@@ -213,12 +217,23 @@ export function createDesktopWebApi(options) {
|
|
|
213
217
|
isRunning: options.isRunning,
|
|
214
218
|
onChanged: (id) => options.onSessionsChanged?.(cwd, id),
|
|
215
219
|
});
|
|
220
|
+
const traces = createModelTraceHttp({
|
|
221
|
+
storageDir: join(options.sessionRootDir, ".model-traces"),
|
|
222
|
+
workspacePath: cwd,
|
|
223
|
+
isAuthorized: authorized,
|
|
224
|
+
resolveWorkspaceScopes: async (request) => {
|
|
225
|
+
const identity = identities.get(request);
|
|
226
|
+
if (!identity || !(await authorized(request)))
|
|
227
|
+
return undefined;
|
|
228
|
+
return options.resolveModelTraceScopes?.(cwd, identity.session.deviceId);
|
|
229
|
+
},
|
|
230
|
+
});
|
|
216
231
|
return {
|
|
217
232
|
configuration,
|
|
218
233
|
mcp,
|
|
219
234
|
active: 0,
|
|
220
235
|
async handle(request, response) {
|
|
221
|
-
for (const service of [configuration, skills, mcp, files, history])
|
|
236
|
+
for (const service of [configuration, skills, mcp, files, history, traces])
|
|
222
237
|
if (await service.handle(request, response))
|
|
223
238
|
return true;
|
|
224
239
|
return false;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
-
import { probeConfiguredModel } from "
|
|
2
|
+
import { probeConfiguredModel } from "@cjhyy/code-shell-host";
|
|
3
3
|
export declare class HubConfigurationError extends Error {
|
|
4
4
|
readonly status: number;
|
|
5
5
|
constructor(status: number, message: string);
|
|
@@ -13,6 +13,9 @@ export interface HubConfigurationOptions {
|
|
|
13
13
|
ownerId?: (request: IncomingMessage) => Promise<string | undefined>;
|
|
14
14
|
/** Test seam; production always uses the shared Core client. */
|
|
15
15
|
probe?: typeof probeConfiguredModel;
|
|
16
|
+
/** Trusted Host route for auxiliary connection probes; never browser-selected. */
|
|
17
|
+
traceStorageDir?: string;
|
|
18
|
+
traceSource?: string;
|
|
16
19
|
}
|
|
17
20
|
/** The caller must authenticate and check Origin before dispatching any request here. */
|
|
18
21
|
export declare function createHubConfiguration(options: HubConfigurationOptions): {
|
|
@@ -31,7 +34,7 @@ export declare function createHubConfiguration(options: HubConfigurationOptions)
|
|
|
31
34
|
id: string;
|
|
32
35
|
revision: string;
|
|
33
36
|
catalogId: string;
|
|
34
|
-
tag: "image" | "
|
|
37
|
+
tag: "image" | "speech" | "audio" | "text" | "video";
|
|
35
38
|
model: string;
|
|
36
39
|
baseUrl: string;
|
|
37
40
|
hasApiKey: boolean;
|
|
@@ -48,7 +51,7 @@ export declare function createHubConfiguration(options: HubConfigurationOptions)
|
|
|
48
51
|
disabledReason?: string | undefined;
|
|
49
52
|
name: string;
|
|
50
53
|
description: string;
|
|
51
|
-
source: "
|
|
54
|
+
source: "project" | "user" | "plugin" | "panel-app";
|
|
52
55
|
enabled: boolean;
|
|
53
56
|
}[];
|
|
54
57
|
mcpServers: {
|
|
@@ -2,7 +2,8 @@ import { createHash, randomUUID } from "node:crypto";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { SettingsManager, invalidateSkillCache, mergePluginMcpServers, scanSkills, userHome, } from "@cjhyy/code-shell-core";
|
|
4
4
|
import { computeEffectiveDisabledLists, getMergedCatalog } from "@cjhyy/code-shell-core/internal";
|
|
5
|
-
import {
|
|
5
|
+
import { withModelTraceContext } from "@cjhyy/code-shell-core/internal";
|
|
6
|
+
import { probeConfiguredModel } from "@cjhyy/code-shell-host";
|
|
6
7
|
const ROOT = "/api/v1/configuration";
|
|
7
8
|
const MAX_BODY_BYTES = 64 * 1024;
|
|
8
9
|
const MAX_SKILL_BYTES = 2 * 1024 * 1024;
|
|
@@ -173,19 +174,26 @@ export function createHubConfiguration(options) {
|
|
|
173
174
|
}
|
|
174
175
|
function updateDefaults(input) {
|
|
175
176
|
knownFields(input, ["text", "auxText"]);
|
|
176
|
-
|
|
177
|
+
// Each field is optional so a form submits only what it changed; the merge
|
|
178
|
+
// happens here under the configuration lock, and a stale form can no longer
|
|
179
|
+
// revert a default another device saved meanwhile.
|
|
180
|
+
if (input.text === undefined && input.auxText === undefined) {
|
|
181
|
+
throw new HubConfigurationError(400, "text or auxText is required");
|
|
182
|
+
}
|
|
183
|
+
const text = input.text === undefined ? undefined : boundedString(input.text, "text", 128);
|
|
177
184
|
const auxText = input.auxText === undefined || input.auxText === null
|
|
178
185
|
? input.auxText
|
|
179
186
|
: boundedString(input.auxText, "auxText", 128);
|
|
180
187
|
const manager = settingsManager();
|
|
181
188
|
manager.mutateSettingsForScope("local", options.cwd, (current) => {
|
|
182
189
|
const effective = settingsManager().get();
|
|
183
|
-
|
|
190
|
+
if (text !== undefined)
|
|
191
|
+
requireTextConnection(effective, text);
|
|
184
192
|
if (auxText)
|
|
185
193
|
requireTextConnection(effective, auxText);
|
|
186
194
|
current.defaults = {
|
|
187
195
|
...objectOrEmpty(current.defaults),
|
|
188
|
-
text,
|
|
196
|
+
...(text !== undefined ? { text } : {}),
|
|
189
197
|
// Empty means the core background-model resolver follows the main model.
|
|
190
198
|
...(auxText !== undefined ? { auxText: auxText ?? "" } : {}),
|
|
191
199
|
};
|
|
@@ -377,9 +385,14 @@ export function createHubConfiguration(options) {
|
|
|
377
385
|
try {
|
|
378
386
|
// Register before this final auth await, so a concurrent revoke also aborts this probe.
|
|
379
387
|
await authorize(req);
|
|
380
|
-
const result = await (
|
|
388
|
+
const result = await withModelTraceContext({
|
|
389
|
+
...(options.traceStorageDir ? { storageDir: options.traceStorageDir } : {}),
|
|
390
|
+
workspacePath: options.cwd,
|
|
391
|
+
source: options.traceSource ?? "server",
|
|
392
|
+
purpose: "connection_probe",
|
|
393
|
+
}, () => (options.probe ?? probeConfiguredModel)(settings, id, {
|
|
381
394
|
signal: controller.signal,
|
|
382
|
-
});
|
|
395
|
+
}));
|
|
383
396
|
await authorize(req);
|
|
384
397
|
json(res, 200, result);
|
|
385
398
|
}
|
package/dist/hub/mcp-probe.d.ts
CHANGED
|
@@ -1,24 +1,4 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
checkedAt: string;
|
|
6
|
-
durationMs: number;
|
|
7
|
-
toolCount?: number;
|
|
8
|
-
truncated?: boolean;
|
|
9
|
-
tools?: {
|
|
10
|
-
name: string;
|
|
11
|
-
description?: string;
|
|
12
|
-
allowed: boolean;
|
|
13
|
-
}[];
|
|
14
|
-
error?: {
|
|
15
|
-
code: string;
|
|
16
|
-
message: string;
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
/** Explicit, bounded sibling connection; configuration reads never invoke this function. */
|
|
20
|
-
export declare function probeHubMcp(config: MCPServerConfig, options: {
|
|
21
|
-
cwd: string;
|
|
22
|
-
signal: AbortSignal;
|
|
23
|
-
timeoutMs?: number;
|
|
24
|
-
}): Promise<HubMcpProbeResult>;
|
|
1
|
+
import { probeMcp, type McpProbeResult } from "@cjhyy/code-shell-host";
|
|
2
|
+
export type HubMcpProbeResult = McpProbeResult;
|
|
3
|
+
/** HTTP authority and cancellation stay in mcp-configuration; only server-facing wording lives here. */
|
|
4
|
+
export declare function probeHubMcp(...args: Parameters<typeof probeMcp>): Promise<HubMcpProbeResult>;
|