@cjhyy/code-shell-server 0.9.10 → 0.9.12
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/index.panels.d.ts +8 -0
- package/dist/index.panels.js +8 -0
- package/dist/panels/bridge-contract.d.ts +72 -0
- package/dist/panels/bridge-contract.js +92 -0
- package/dist/panels/connections.d.ts +31 -0
- package/dist/panels/connections.js +118 -0
- package/dist/panels/process-entry.d.ts +2 -0
- package/dist/panels/process-entry.js +52 -0
- package/dist/panels/process-service.d.ts +61 -0
- package/dist/panels/process-service.js +367 -33
- package/dist/panels/process-state.d.ts +58 -0
- package/dist/panels/process-state.js +87 -0
- package/dist/panels/resources/directories.d.ts +12 -0
- package/dist/panels/resources/directories.js +89 -0
- package/dist/panels/resources/library.d.ts +63 -0
- package/dist/panels/resources/library.js +423 -0
- package/dist/panels/resources/service.d.ts +45 -0
- package/dist/panels/resources/service.js +317 -0
- package/dist/panels/resources/storage.d.ts +10 -0
- package/dist/panels/resources/storage.js +102 -0
- package/dist/panels/resources/types.d.ts +16 -0
- package/dist/panels/resources/types.js +1 -0
- package/dist/panels/resources/uploads.d.ts +64 -0
- package/dist/panels/resources/uploads.js +527 -0
- package/dist/panels/runtime.d.ts +1 -1
- package/dist/panels/runtime.js +137 -15
- package/dist/panels/tool-executor.d.ts +58 -0
- package/dist/panels/tool-executor.js +421 -0
- package/dist/panels/tool-jobs-storage.d.ts +16 -0
- package/dist/panels/tool-jobs-storage.js +169 -0
- package/dist/panels/tool-jobs.d.ts +103 -0
- package/dist/panels/tool-jobs.js +530 -0
- package/package.json +5 -5
package/dist/index.panels.d.ts
CHANGED
|
@@ -7,3 +7,11 @@ export * from "./panels/agent-task-service.js";
|
|
|
7
7
|
export * from "./panels/agent-task-models.js";
|
|
8
8
|
export * from "./panels/agent-task-host.js";
|
|
9
9
|
export type * from "./panels/types.js";
|
|
10
|
+
export * from "./panels/resources/types.js";
|
|
11
|
+
export * from "./panels/resources/library.js";
|
|
12
|
+
export * from "./panels/resources/service.js";
|
|
13
|
+
export { MAX_MEDIA_JSON_BYTES, normalizeMediaScope, mediaScopeKey, prepareMediaRoot, mediaDirectory, cloneMediaJson, readMediaJson, writeMediaJson, } from "./panels/resources/storage.js";
|
|
14
|
+
export * from "./panels/bridge-contract.js";
|
|
15
|
+
export * from "./panels/connections.js";
|
|
16
|
+
export * from "./panels/tool-jobs.js";
|
|
17
|
+
export * from "./panels/tool-executor.js";
|
package/dist/index.panels.js
CHANGED
|
@@ -6,3 +6,11 @@ export * from "./panels/process-service.js";
|
|
|
6
6
|
export * from "./panels/agent-task-service.js";
|
|
7
7
|
export * from "./panels/agent-task-models.js";
|
|
8
8
|
export * from "./panels/agent-task-host.js";
|
|
9
|
+
export * from "./panels/resources/types.js";
|
|
10
|
+
export * from "./panels/resources/library.js";
|
|
11
|
+
export * from "./panels/resources/service.js";
|
|
12
|
+
export { MAX_MEDIA_JSON_BYTES, normalizeMediaScope, mediaScopeKey, prepareMediaRoot, mediaDirectory, cloneMediaJson, readMediaJson, writeMediaJson, } from "./panels/resources/storage.js";
|
|
13
|
+
export * from "./panels/bridge-contract.js";
|
|
14
|
+
export * from "./panels/connections.js";
|
|
15
|
+
export * from "./panels/tool-jobs.js";
|
|
16
|
+
export * from "./panels/tool-executor.js";
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export declare const panelRuntimeApiVersion = 14;
|
|
2
|
+
export declare const panelProcessMethods: string[];
|
|
3
|
+
export declare const panelResourceMethods: string[];
|
|
4
|
+
export declare const panelToolJobMethods: string[];
|
|
5
|
+
export declare const panelBridgeLimits: Readonly<{
|
|
6
|
+
maxParamsBytes: number;
|
|
7
|
+
maxResultBytes: number;
|
|
8
|
+
rateWindowMs: 10000;
|
|
9
|
+
maxCallsPerWindow: 30;
|
|
10
|
+
maxTransferCallsPerWindow: 512;
|
|
11
|
+
callTimeoutMs: 15000;
|
|
12
|
+
consentTimeoutMs: number;
|
|
13
|
+
}>;
|
|
14
|
+
export type PanelBridgeErrorCode = "PERMISSION_DENIED" | "REVOKED" | "NOT_SUPPORTED" | "RATE_LIMITED" | "PARAMS_TOO_LARGE" | "RESULT_TOO_LARGE" | "TIMEOUT" | "INVALID_ARGUMENT" | "OPERATION_FAILED";
|
|
15
|
+
export declare class PanelBridgeError extends Error {
|
|
16
|
+
readonly code: PanelBridgeErrorCode;
|
|
17
|
+
readonly retryAfterMs?: number | undefined;
|
|
18
|
+
constructor(code: PanelBridgeErrorCode, message: string, retryAfterMs?: number | undefined);
|
|
19
|
+
}
|
|
20
|
+
/** IPC otherwise strips custom Error properties. Only the preload unwraps this envelope. */
|
|
21
|
+
export declare function panelBridgeFailure(error: unknown): {
|
|
22
|
+
__codeshellPanelError: {
|
|
23
|
+
retryAfterMs?: number | undefined;
|
|
24
|
+
code: PanelBridgeErrorCode;
|
|
25
|
+
message: string;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
export declare function panelRuntimeCapabilities(input: {
|
|
29
|
+
process: boolean;
|
|
30
|
+
resources?: unknown;
|
|
31
|
+
tasks?: unknown;
|
|
32
|
+
limits?: Partial<Record<keyof typeof panelBridgeLimits, number>>;
|
|
33
|
+
}): {
|
|
34
|
+
tasks?: {} | undefined;
|
|
35
|
+
resources?: {
|
|
36
|
+
materialize: boolean;
|
|
37
|
+
capture: boolean;
|
|
38
|
+
} | undefined;
|
|
39
|
+
process?: Readonly<{
|
|
40
|
+
maxArguments: 256;
|
|
41
|
+
maxArgumentLength: 8192;
|
|
42
|
+
maxArgumentBytes: number;
|
|
43
|
+
maxConcurrentProcesses: 3;
|
|
44
|
+
maxFileArguments: 8;
|
|
45
|
+
maxEventChars: 16384;
|
|
46
|
+
maxOutputBytes: number;
|
|
47
|
+
maxLifetimeMs: number;
|
|
48
|
+
receiptTtlMs: number;
|
|
49
|
+
maxRetainedProcesses: 128;
|
|
50
|
+
maxRetainedProcessesPerGuest: 32;
|
|
51
|
+
maxEventsPerProcess: 256;
|
|
52
|
+
maxRetainedOutputBytes: number;
|
|
53
|
+
maxStdinChunkBytes: number;
|
|
54
|
+
maxStdinBytes: number;
|
|
55
|
+
maxStdinPendingBytes: number;
|
|
56
|
+
maxStdinPendingWrites: 4;
|
|
57
|
+
stdinWriteTimeoutMs: 10000;
|
|
58
|
+
maxEntryBytes: number;
|
|
59
|
+
maxEntryHandlesPerGuest: 64;
|
|
60
|
+
}> | undefined;
|
|
61
|
+
bridge: {
|
|
62
|
+
structuredErrors: boolean;
|
|
63
|
+
maxParamsBytes: number;
|
|
64
|
+
maxResultBytes: number;
|
|
65
|
+
rateWindowMs: number;
|
|
66
|
+
maxCallsPerWindow: number;
|
|
67
|
+
maxTransferCallsPerWindow: number;
|
|
68
|
+
callTimeoutMs: number;
|
|
69
|
+
consentTimeoutMs: number;
|
|
70
|
+
};
|
|
71
|
+
errors: string[];
|
|
72
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { processLimits } from "./process-state.js";
|
|
2
|
+
export const panelRuntimeApiVersion = 14;
|
|
3
|
+
export const panelProcessMethods = [
|
|
4
|
+
"process.find",
|
|
5
|
+
"process.info",
|
|
6
|
+
"process.resolveEntry",
|
|
7
|
+
"process.spawn",
|
|
8
|
+
"process.get",
|
|
9
|
+
"process.cancel",
|
|
10
|
+
"process.write",
|
|
11
|
+
"process.end",
|
|
12
|
+
"filesystem.getKnownDirectory",
|
|
13
|
+
"filesystem.pickDirectory",
|
|
14
|
+
"filesystem.openDirectory",
|
|
15
|
+
];
|
|
16
|
+
export const panelResourceMethods = [
|
|
17
|
+
"resources.list",
|
|
18
|
+
"resources.get",
|
|
19
|
+
"resources.read",
|
|
20
|
+
"resources.upload.begin",
|
|
21
|
+
"resources.upload.write",
|
|
22
|
+
"resources.upload.get",
|
|
23
|
+
"resources.upload.finish",
|
|
24
|
+
"resources.upload.cancel",
|
|
25
|
+
"resources.materialize",
|
|
26
|
+
"resources.capture",
|
|
27
|
+
];
|
|
28
|
+
export const panelToolJobMethods = [
|
|
29
|
+
"tasks.start",
|
|
30
|
+
"tasks.list",
|
|
31
|
+
"tasks.get",
|
|
32
|
+
"tasks.cancel",
|
|
33
|
+
"tasks.retry",
|
|
34
|
+
];
|
|
35
|
+
export const panelBridgeLimits = Object.freeze({
|
|
36
|
+
maxParamsBytes: 64 * 1024,
|
|
37
|
+
maxResultBytes: 256 * 1024,
|
|
38
|
+
rateWindowMs: 10000,
|
|
39
|
+
maxCallsPerWindow: 30,
|
|
40
|
+
maxTransferCallsPerWindow: 512,
|
|
41
|
+
callTimeoutMs: 15000,
|
|
42
|
+
consentTimeoutMs: 30 * 60 * 1000,
|
|
43
|
+
});
|
|
44
|
+
export class PanelBridgeError extends Error {
|
|
45
|
+
code;
|
|
46
|
+
retryAfterMs;
|
|
47
|
+
constructor(code, message, retryAfterMs) {
|
|
48
|
+
super(message);
|
|
49
|
+
this.code = code;
|
|
50
|
+
this.retryAfterMs = retryAfterMs;
|
|
51
|
+
this.name = "PanelBridgeError";
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/** IPC otherwise strips custom Error properties. Only the preload unwraps this envelope. */
|
|
55
|
+
export function panelBridgeFailure(error) {
|
|
56
|
+
return {
|
|
57
|
+
__codeshellPanelError: {
|
|
58
|
+
code: error instanceof PanelBridgeError ? error.code : "OPERATION_FAILED",
|
|
59
|
+
message: error instanceof Error ? error.message : String(error),
|
|
60
|
+
...(error instanceof PanelBridgeError && error.retryAfterMs !== undefined
|
|
61
|
+
? { retryAfterMs: error.retryAfterMs }
|
|
62
|
+
: {}),
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export function panelRuntimeCapabilities(input) {
|
|
67
|
+
return {
|
|
68
|
+
bridge: { ...panelBridgeLimits, ...input.limits, structuredErrors: true },
|
|
69
|
+
errors: [
|
|
70
|
+
"PERMISSION_DENIED",
|
|
71
|
+
"REVOKED",
|
|
72
|
+
"NOT_SUPPORTED",
|
|
73
|
+
"RATE_LIMITED",
|
|
74
|
+
"PARAMS_TOO_LARGE",
|
|
75
|
+
"RESULT_TOO_LARGE",
|
|
76
|
+
"TIMEOUT",
|
|
77
|
+
"INVALID_ARGUMENT",
|
|
78
|
+
"OPERATION_FAILED",
|
|
79
|
+
],
|
|
80
|
+
...(input.process ? { process: processLimits } : {}),
|
|
81
|
+
...(input.resources
|
|
82
|
+
? {
|
|
83
|
+
resources: {
|
|
84
|
+
...input.resources,
|
|
85
|
+
materialize: input.process,
|
|
86
|
+
capture: input.process,
|
|
87
|
+
},
|
|
88
|
+
}
|
|
89
|
+
: {}),
|
|
90
|
+
...(input.tasks ? { tasks: input.tasks } : {}),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { SettingsManager } from "@cjhyy/code-shell-core";
|
|
2
|
+
import { type CatalogEntry } from "@cjhyy/code-shell-core/internal";
|
|
3
|
+
/** Resolve existing credentials only; provider workflows and request formats stay in tools. */
|
|
4
|
+
export declare function panelConnections(cwd: string, selectedIds?: string[]): {
|
|
5
|
+
connections: Record<string, any>[];
|
|
6
|
+
defaults: {
|
|
7
|
+
text?: string | undefined;
|
|
8
|
+
image?: string | undefined;
|
|
9
|
+
video?: string | undefined;
|
|
10
|
+
audio?: string | undefined;
|
|
11
|
+
speech?: string | undefined;
|
|
12
|
+
auxText?: string | undefined;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
export declare function resolvePanelConnections(settings: Pick<ReturnType<SettingsManager["get"]>, "modelConnections" | "credentials" | "defaults">, catalog: CatalogEntry[], selectedIds?: string[]): {
|
|
16
|
+
connections: Record<string, any>[];
|
|
17
|
+
defaults: {
|
|
18
|
+
text?: string | undefined;
|
|
19
|
+
image?: string | undefined;
|
|
20
|
+
video?: string | undefined;
|
|
21
|
+
audio?: string | undefined;
|
|
22
|
+
speech?: string | undefined;
|
|
23
|
+
auxText?: string | undefined;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
export declare function panelConnectionIds(input: unknown): string[];
|
|
27
|
+
/** Sealed launch argument, never sent to a Guest or persisted in job input. */
|
|
28
|
+
export declare function materializePanelConnections(root: string, cwd: string, selectedIds: string[]): Promise<{
|
|
29
|
+
path: string;
|
|
30
|
+
cleanup: () => void;
|
|
31
|
+
}>;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { SettingsManager } from "@cjhyy/code-shell-core";
|
|
3
|
+
import { getMergedCatalog, resolveInstance, } from "@cjhyy/code-shell-core/internal";
|
|
4
|
+
import { mkdir, mkdtemp, writeFile, rm } from "node:fs/promises";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
/** Resolve existing credentials only; provider workflows and request formats stay in tools. */
|
|
7
|
+
export function panelConnections(cwd, selectedIds) {
|
|
8
|
+
const settings = new SettingsManager(cwd, "full").get();
|
|
9
|
+
return resolvePanelConnections(settings, getMergedCatalog(), selectedIds);
|
|
10
|
+
}
|
|
11
|
+
export function resolvePanelConnections(settings, catalog, selectedIds) {
|
|
12
|
+
const connections = (settings.modelConnections ?? []).flatMap((connection) => {
|
|
13
|
+
if (selectedIds && !selectedIds.includes(connection.id))
|
|
14
|
+
return [];
|
|
15
|
+
const value = resolveInstance(connection, settings.credentials ?? [], catalog);
|
|
16
|
+
if (!value)
|
|
17
|
+
return [];
|
|
18
|
+
const identity = {
|
|
19
|
+
id: connection.id,
|
|
20
|
+
catalogId: connection.catalogId,
|
|
21
|
+
tag: connection.tag,
|
|
22
|
+
adapterKind: value.adapterKind,
|
|
23
|
+
model: value.model,
|
|
24
|
+
hasCredentials: !!value.apiKey || !value.needsKey,
|
|
25
|
+
};
|
|
26
|
+
if (selectedIds)
|
|
27
|
+
return [
|
|
28
|
+
{
|
|
29
|
+
...identity,
|
|
30
|
+
baseUrl: value.baseUrl,
|
|
31
|
+
apiKey: value.apiKey ?? "",
|
|
32
|
+
entry: value.entry,
|
|
33
|
+
preset: value.preset,
|
|
34
|
+
paramValues: value.paramValues,
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
const secretName = /(?:authorization|api.?key|token|secret|password|credential|header|cookie)/i;
|
|
38
|
+
const parameters = (value.preset?.params ?? [])
|
|
39
|
+
.filter((param) => !secretName.test(param.name))
|
|
40
|
+
.map((param) => ({
|
|
41
|
+
name: param.name,
|
|
42
|
+
control: param.control,
|
|
43
|
+
...(param.control === "enum"
|
|
44
|
+
? {
|
|
45
|
+
options: param.options ?? [],
|
|
46
|
+
...(typeof param.default === "string" && param.options?.includes(param.default)
|
|
47
|
+
? { default: param.default }
|
|
48
|
+
: {}),
|
|
49
|
+
}
|
|
50
|
+
: {}),
|
|
51
|
+
...(param.control === "number"
|
|
52
|
+
? {
|
|
53
|
+
min: param.min,
|
|
54
|
+
max: param.max,
|
|
55
|
+
...(typeof param.default === "number" ? { default: param.default } : {}),
|
|
56
|
+
}
|
|
57
|
+
: {}),
|
|
58
|
+
...(param.control === "toggle" && typeof param.default === "boolean"
|
|
59
|
+
? { default: param.default }
|
|
60
|
+
: {}),
|
|
61
|
+
}));
|
|
62
|
+
const paramValues = Object.fromEntries(parameters.flatMap((param) => {
|
|
63
|
+
const current = value.paramValues[param.name];
|
|
64
|
+
return (param.control === "enum" &&
|
|
65
|
+
typeof current === "string" &&
|
|
66
|
+
param.options?.includes(current)) ||
|
|
67
|
+
(param.control === "number" && typeof current === "number" && Number.isFinite(current)) ||
|
|
68
|
+
(param.control === "toggle" && typeof current === "boolean")
|
|
69
|
+
? [[param.name, current]]
|
|
70
|
+
: [];
|
|
71
|
+
}));
|
|
72
|
+
return [
|
|
73
|
+
{
|
|
74
|
+
...identity,
|
|
75
|
+
fingerprint: createHash("sha256")
|
|
76
|
+
.update(JSON.stringify([connection.id, connection.catalogId, value.model, value.baseUrl]))
|
|
77
|
+
.digest("hex")
|
|
78
|
+
.slice(0, 32),
|
|
79
|
+
providerName: value.entry.displayName,
|
|
80
|
+
entry: { displayName: value.entry.displayName, tag: value.entry.tag },
|
|
81
|
+
preset: value.preset
|
|
82
|
+
? { value: value.preset.value, label: value.preset.label, params: parameters }
|
|
83
|
+
: undefined,
|
|
84
|
+
paramValues,
|
|
85
|
+
},
|
|
86
|
+
];
|
|
87
|
+
});
|
|
88
|
+
if (selectedIds?.some((id) => !connections.some((connection) => connection.id === id)))
|
|
89
|
+
throw new Error("Selected connection is unavailable");
|
|
90
|
+
return { connections, defaults: settings.defaults ?? {} };
|
|
91
|
+
}
|
|
92
|
+
export function panelConnectionIds(input) {
|
|
93
|
+
if (!Array.isArray(input) ||
|
|
94
|
+
!input.length ||
|
|
95
|
+
input.length > 8 ||
|
|
96
|
+
input.some((id) => typeof id !== "string" || !id || id.length > 200) ||
|
|
97
|
+
new Set(input).size !== input.length)
|
|
98
|
+
throw new Error("Select between one and eight configured connections");
|
|
99
|
+
return input;
|
|
100
|
+
}
|
|
101
|
+
/** Sealed launch argument, never sent to a Guest or persisted in job input. */
|
|
102
|
+
export async function materializePanelConnections(root, cwd, selectedIds) {
|
|
103
|
+
const value = panelConnections(cwd, panelConnectionIds(selectedIds));
|
|
104
|
+
await mkdir(root, { recursive: true, mode: 0o700 });
|
|
105
|
+
const directory = await mkdtemp(join(root, "connections-"));
|
|
106
|
+
const path = join(directory, "connections.json");
|
|
107
|
+
const cleanup = () => {
|
|
108
|
+
void rm(directory, { recursive: true, force: true }).catch(() => { });
|
|
109
|
+
};
|
|
110
|
+
try {
|
|
111
|
+
await writeFile(path, JSON.stringify(value), { mode: 0o600, flag: "wx" });
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
cleanup();
|
|
115
|
+
throw error;
|
|
116
|
+
}
|
|
117
|
+
return { path, cleanup };
|
|
118
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { constants } from "node:fs";
|
|
3
|
+
import { lstat, open, realpath } from "node:fs/promises";
|
|
4
|
+
import { isAbsolute } from "node:path";
|
|
5
|
+
import { processLimits } from "./process-state.js";
|
|
6
|
+
function identity(info) {
|
|
7
|
+
return [info.dev, info.ino, info.size, info.mtimeMs, info.ctimeMs, info.mode].join(":");
|
|
8
|
+
}
|
|
9
|
+
/** The callback supplies an installed, reviewed entry, never a Guest-provided path. */
|
|
10
|
+
export async function verifyProcessEntry(path, sha256) {
|
|
11
|
+
if (!isAbsolute(path) || !/^[a-f0-9]{64}$/.test(sha256))
|
|
12
|
+
throw new Error("invalid reviewed package entry");
|
|
13
|
+
if ((await realpath(path)) !== path)
|
|
14
|
+
throw new Error("package entry path contains a symbolic link");
|
|
15
|
+
const before = await lstat(path);
|
|
16
|
+
if (!before.isFile() || before.isSymbolicLink() || before.nlink !== 1)
|
|
17
|
+
throw new Error("package entry must be an unlinked regular file");
|
|
18
|
+
if (before.size > processLimits.maxEntryBytes)
|
|
19
|
+
throw new Error("package entry exceeds size limit");
|
|
20
|
+
const file = await open(path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
|
|
21
|
+
try {
|
|
22
|
+
const opened = await file.stat();
|
|
23
|
+
if (identity(opened) !== identity(before))
|
|
24
|
+
throw new Error("package entry changed while opening");
|
|
25
|
+
const hash = createHash("sha256");
|
|
26
|
+
const buffer = Buffer.alloc(64 * 1024);
|
|
27
|
+
let total = 0;
|
|
28
|
+
while (true) {
|
|
29
|
+
const { bytesRead } = await file.read(buffer, 0, buffer.length, null);
|
|
30
|
+
if (!bytesRead)
|
|
31
|
+
break;
|
|
32
|
+
total += bytesRead;
|
|
33
|
+
if (total > processLimits.maxEntryBytes)
|
|
34
|
+
throw new Error("package entry exceeds size limit");
|
|
35
|
+
hash.update(buffer.subarray(0, bytesRead));
|
|
36
|
+
}
|
|
37
|
+
const after = await file.stat();
|
|
38
|
+
const current = await lstat(path);
|
|
39
|
+
if (identity(opened) !== identity(after) ||
|
|
40
|
+
identity(opened) !== identity(current) ||
|
|
41
|
+
current.isSymbolicLink() ||
|
|
42
|
+
current.nlink !== 1 ||
|
|
43
|
+
(await realpath(path)) !== path ||
|
|
44
|
+
total !== opened.size ||
|
|
45
|
+
hash.digest("hex") !== sha256)
|
|
46
|
+
throw new Error("reviewed package entry changed or failed its hash check");
|
|
47
|
+
return identity(opened);
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
await file.close();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { processLimits } from "./process-state.js";
|
|
1
2
|
export interface PanelProcessApprovalScope {
|
|
2
3
|
appId: string;
|
|
3
4
|
revision: string;
|
|
@@ -32,6 +33,13 @@ export interface PanelAppProcessServiceOptions {
|
|
|
32
33
|
}): Promise<boolean>;
|
|
33
34
|
isExecutionApproved?(scope: PanelProcessApprovalScope): Promise<boolean>;
|
|
34
35
|
rememberExecutionApproval?(scope: PanelProcessApprovalScope): Promise<void>;
|
|
36
|
+
/** Resolve a declared entry in this app revision; return a canonical, reviewed file. */
|
|
37
|
+
resolvePackageEntry?(owner: PanelProcessOwner, name: string): Promise<{
|
|
38
|
+
path: string;
|
|
39
|
+
sha256: string;
|
|
40
|
+
}>;
|
|
41
|
+
/** Clock injection for bounded receipt retention; does not alter process deadlines. */
|
|
42
|
+
now?: () => number;
|
|
35
43
|
}
|
|
36
44
|
export declare function panelProcessInfo(platform?: NodeJS.Platform, arch?: string, report?: unknown): {
|
|
37
45
|
platform: NodeJS.Platform;
|
|
@@ -53,12 +61,16 @@ export declare class PanelAppProcessService {
|
|
|
53
61
|
private readonly executables;
|
|
54
62
|
private readonly directories;
|
|
55
63
|
private readonly fileArguments;
|
|
64
|
+
private readonly entries;
|
|
56
65
|
private readonly processes;
|
|
66
|
+
private readonly retiring;
|
|
67
|
+
private readonly receipts;
|
|
57
68
|
private readonly approvedExecutables;
|
|
58
69
|
private readonly guestEpochs;
|
|
59
70
|
private readonly reservations;
|
|
60
71
|
private closed;
|
|
61
72
|
constructor(options: PanelAppProcessServiceOptions);
|
|
73
|
+
private now;
|
|
62
74
|
private epoch;
|
|
63
75
|
private assertLive;
|
|
64
76
|
private authorize;
|
|
@@ -81,7 +93,20 @@ export declare class PanelAppProcessService {
|
|
|
81
93
|
}): Promise<{
|
|
82
94
|
handle: string;
|
|
83
95
|
}>;
|
|
96
|
+
/** Seal an already authorized directory as a long-option argument for one executable. */
|
|
97
|
+
grantDirectoryArgument(owner: PanelProcessOwner, input: {
|
|
98
|
+
executableHandle: unknown;
|
|
99
|
+
argumentName: unknown;
|
|
100
|
+
directoryHandle: unknown;
|
|
101
|
+
}): Promise<{
|
|
102
|
+
handle: string;
|
|
103
|
+
}>;
|
|
84
104
|
directoryPath(owner: PanelProcessOwner, handle: unknown): string;
|
|
105
|
+
resolveEntry(owner: PanelProcessOwner, params: unknown): Promise<{
|
|
106
|
+
handle: string;
|
|
107
|
+
name: string;
|
|
108
|
+
sha256: string;
|
|
109
|
+
}>;
|
|
85
110
|
start(owner: PanelProcessOwner, params: unknown): Promise<{
|
|
86
111
|
processId: string;
|
|
87
112
|
executable: string;
|
|
@@ -89,6 +114,42 @@ export declare class PanelAppProcessService {
|
|
|
89
114
|
cancel(owner: PanelProcessOwner, params: unknown): {
|
|
90
115
|
cancelled: boolean;
|
|
91
116
|
};
|
|
117
|
+
/** Host-only wait captured while the owner is live; never exposes output after revocation. */
|
|
118
|
+
waitForExit(owner: PanelProcessOwner, params: {
|
|
119
|
+
processId: string;
|
|
120
|
+
}): Promise<{
|
|
121
|
+
code: number | null;
|
|
122
|
+
signal: string | null;
|
|
123
|
+
}>;
|
|
124
|
+
get(owner: PanelProcessOwner, params: unknown): Promise<{
|
|
125
|
+
found: false;
|
|
126
|
+
processId: string;
|
|
127
|
+
} | {
|
|
128
|
+
cancelRequested: boolean;
|
|
129
|
+
sequence: number;
|
|
130
|
+
nextSequence: number;
|
|
131
|
+
events: import("./process-state.js").PanelProcessEvent[];
|
|
132
|
+
truncated: boolean;
|
|
133
|
+
hasMore: boolean;
|
|
134
|
+
signal?: string | null | undefined;
|
|
135
|
+
code?: number | null | undefined;
|
|
136
|
+
expiresAt?: number | undefined;
|
|
137
|
+
exitedAt?: number | undefined;
|
|
138
|
+
found: true;
|
|
139
|
+
processId: string;
|
|
140
|
+
status: "running" | "stopping" | "exited";
|
|
141
|
+
startedAt: number;
|
|
142
|
+
}>;
|
|
143
|
+
private writableProcess;
|
|
144
|
+
write(owner: PanelProcessOwner, params: unknown): Promise<{
|
|
145
|
+
bytesWritten: number;
|
|
146
|
+
totalBytes: number;
|
|
147
|
+
}>;
|
|
148
|
+
end(owner: PanelProcessOwner, params: unknown): Promise<{
|
|
149
|
+
ended: true;
|
|
150
|
+
totalBytes: number;
|
|
151
|
+
}>;
|
|
152
|
+
private sendInput;
|
|
92
153
|
revokeGuest(guestId: number): void;
|
|
93
154
|
/** Permanently shut down all guests, including operations awaiting approval. */
|
|
94
155
|
close(): void;
|