@ganglion/xacpx 0.17.0-beta.2 → 0.17.0-beta.4
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/cli.js
CHANGED
|
@@ -25585,6 +25585,10 @@ function terminalIdleTimeoutSeconds(config4) {
|
|
|
25585
25585
|
const v = config4.terminal?.idleTimeoutSeconds;
|
|
25586
25586
|
return typeof v === "number" && v > 0 ? v : 900;
|
|
25587
25587
|
}
|
|
25588
|
+
function terminalShell(config4) {
|
|
25589
|
+
const v = config4.terminal?.shell;
|
|
25590
|
+
return typeof v === "string" && v.trim() ? v : undefined;
|
|
25591
|
+
}
|
|
25588
25592
|
function filesWriteEnabled(config4) {
|
|
25589
25593
|
return config4.files?.writeEnabled === true;
|
|
25590
25594
|
}
|
|
@@ -33889,6 +33893,7 @@ class WorkspaceFs {
|
|
|
33889
33893
|
path: rel,
|
|
33890
33894
|
content: binary ? "" : slice.toString("utf8"),
|
|
33891
33895
|
size: info.size,
|
|
33896
|
+
mtimeMs: info.mtimeMs,
|
|
33892
33897
|
truncated: info.size > FILE_READ_CAP,
|
|
33893
33898
|
binary
|
|
33894
33899
|
};
|
|
@@ -33896,6 +33901,23 @@ class WorkspaceFs {
|
|
|
33896
33901
|
await fh.close();
|
|
33897
33902
|
}
|
|
33898
33903
|
}
|
|
33904
|
+
async writeFile(workspace3, relPath, content, expected) {
|
|
33905
|
+
const { abs, rel } = await this.resolve(workspace3, relPath);
|
|
33906
|
+
const info = await stat3(abs);
|
|
33907
|
+
if (!info.isFile())
|
|
33908
|
+
throw new Error("not-a-file");
|
|
33909
|
+
if (info.size > FILE_READ_CAP)
|
|
33910
|
+
throw new Error("file-too-large");
|
|
33911
|
+
if (content.includes("\x00"))
|
|
33912
|
+
throw new Error("is-binary");
|
|
33913
|
+
if (Buffer.byteLength(content, "utf8") > FILE_READ_CAP)
|
|
33914
|
+
throw new Error("file-too-large");
|
|
33915
|
+
if (info.mtimeMs !== expected.mtimeMs || info.size !== expected.size)
|
|
33916
|
+
throw new Error("stale-write");
|
|
33917
|
+
await import_write_file_atomic3.default(abs, content);
|
|
33918
|
+
const after = await stat3(abs);
|
|
33919
|
+
return { path: rel, mtimeMs: after.mtimeMs, size: after.size };
|
|
33920
|
+
}
|
|
33899
33921
|
async search(workspace3, opts) {
|
|
33900
33922
|
const { root, abs: base, rel: scopeRel } = await this.resolve(workspace3, opts.path);
|
|
33901
33923
|
const query = opts.query.trim();
|
|
@@ -34276,8 +34298,9 @@ function globToRegExp(glob) {
|
|
|
34276
34298
|
return /$^/;
|
|
34277
34299
|
}
|
|
34278
34300
|
}
|
|
34279
|
-
var execFileAsync, MAX_ENTRIES = 2000, FILE_READ_CAP, DIFF_CAP, GIT_MAX_BUFFER, SEARCH_MAX_RESULTS = 200, SEARCH_MAX_SCAN = 20000, SEARCH_SKIP_DIRS, SEARCH_CONTENT_MAX_HITS = 500, SEARCH_CONTENT_MAX_FILE, LINE_MATCH_CAP = 2000, DOWNLOAD_MAX, GIT_TIMEOUT_MS = 1e4, SEARCH_DEADLINE_MS = 4000, MIME_BY_EXT;
|
|
34301
|
+
var import_write_file_atomic3, execFileAsync, MAX_ENTRIES = 2000, FILE_READ_CAP, DIFF_CAP, GIT_MAX_BUFFER, SEARCH_MAX_RESULTS = 200, SEARCH_MAX_SCAN = 20000, SEARCH_SKIP_DIRS, SEARCH_CONTENT_MAX_HITS = 500, SEARCH_CONTENT_MAX_FILE, LINE_MATCH_CAP = 2000, DOWNLOAD_MAX, GIT_TIMEOUT_MS = 1e4, SEARCH_DEADLINE_MS = 4000, MIME_BY_EXT;
|
|
34280
34302
|
var init_workspace_fs = __esm(() => {
|
|
34303
|
+
import_write_file_atomic3 = __toESM(require_lib(), 1);
|
|
34281
34304
|
execFileAsync = promisify(execFile);
|
|
34282
34305
|
FILE_READ_CAP = 256 * 1024;
|
|
34283
34306
|
DIFF_CAP = 512 * 1024;
|
|
@@ -34348,6 +34371,11 @@ class ControlService {
|
|
|
34348
34371
|
throw new Error("files-write-disabled");
|
|
34349
34372
|
return this.workspaceFs.duplicate(workspace3, path16);
|
|
34350
34373
|
}
|
|
34374
|
+
async fsWrite(workspace3, path16, content, expected) {
|
|
34375
|
+
if (!this.deps.filesWriteEnabled())
|
|
34376
|
+
throw new Error("files-write-disabled");
|
|
34377
|
+
return this.workspaceFs.writeFile(workspace3, path16, content, expected);
|
|
34378
|
+
}
|
|
34351
34379
|
async fsDownload(workspace3, path16) {
|
|
34352
34380
|
return this.workspaceFs.readFileBytes(workspace3, path16);
|
|
34353
34381
|
}
|
|
@@ -34816,6 +34844,11 @@ ${chunk}` : chunk
|
|
|
34816
34844
|
throw new Error("session-not-found");
|
|
34817
34845
|
return this.deps.terminal.create({ cwd: session3.cwd, cols, rows });
|
|
34818
34846
|
}
|
|
34847
|
+
attachTerminal(terminalId) {
|
|
34848
|
+
if (!this.deps.terminalEnabled())
|
|
34849
|
+
throw new Error("terminal-disabled");
|
|
34850
|
+
return this.deps.terminal.attach(terminalId);
|
|
34851
|
+
}
|
|
34819
34852
|
writeTerminal(terminalId, data) {
|
|
34820
34853
|
this.deps.terminal.write(terminalId, data);
|
|
34821
34854
|
}
|
|
@@ -34862,6 +34895,7 @@ var init_control_service = __esm(() => {
|
|
|
34862
34895
|
// src/control/terminal-service.ts
|
|
34863
34896
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
34864
34897
|
import { createRequire as createRequire6 } from "node:module";
|
|
34898
|
+
import { statSync as statSync3 } from "node:fs";
|
|
34865
34899
|
import { spawn as spawnPty2 } from "node-pty";
|
|
34866
34900
|
function scrubEnv() {
|
|
34867
34901
|
const out = {};
|
|
@@ -34876,9 +34910,33 @@ function scrubEnv() {
|
|
|
34876
34910
|
out.LANG = out.LANG ?? "en_US.UTF-8";
|
|
34877
34911
|
return out;
|
|
34878
34912
|
}
|
|
34879
|
-
function
|
|
34880
|
-
|
|
34881
|
-
return
|
|
34913
|
+
function defaultExists(p) {
|
|
34914
|
+
try {
|
|
34915
|
+
return statSync3(p).isFile();
|
|
34916
|
+
} catch {
|
|
34917
|
+
return false;
|
|
34918
|
+
}
|
|
34919
|
+
}
|
|
34920
|
+
function resolveShell(args) {
|
|
34921
|
+
const { platform, env, shellOverride } = args;
|
|
34922
|
+
const exists = args.exists ?? defaultExists;
|
|
34923
|
+
if (shellOverride && shellOverride.trim())
|
|
34924
|
+
return shellOverride;
|
|
34925
|
+
if (platform === "win32") {
|
|
34926
|
+
const pathValue = env.PATH ?? env.Path ?? "";
|
|
34927
|
+
const dirs = pathValue.split(";").filter(Boolean);
|
|
34928
|
+
for (const name of ["pwsh.exe", "powershell.exe"]) {
|
|
34929
|
+
for (const dir of dirs) {
|
|
34930
|
+
const clean = dir.replace(/^"|"$/g, "");
|
|
34931
|
+
const full = `${clean}\\${name}`;
|
|
34932
|
+
if (exists(full))
|
|
34933
|
+
return full;
|
|
34934
|
+
}
|
|
34935
|
+
}
|
|
34936
|
+
return env.ComSpec ?? "cmd.exe";
|
|
34937
|
+
}
|
|
34938
|
+
if (env.SHELL)
|
|
34939
|
+
return env.SHELL;
|
|
34882
34940
|
return platform === "darwin" ? "/bin/zsh" : "/bin/bash";
|
|
34883
34941
|
}
|
|
34884
34942
|
function realPtySpawn(file, args, opts) {
|
|
@@ -34886,6 +34944,26 @@ function realPtySpawn(file, args, opts) {
|
|
|
34886
34944
|
ensureNodePtyHelperExecutable(helperPath);
|
|
34887
34945
|
return spawnPty2(file, args, opts);
|
|
34888
34946
|
}
|
|
34947
|
+
function appendToBuffer(session3, data) {
|
|
34948
|
+
session3.buffer += data;
|
|
34949
|
+
session3.bufBytes += Buffer.byteLength(data, "utf8");
|
|
34950
|
+
if (session3.bufBytes <= MAX_BUFFER_BYTES)
|
|
34951
|
+
return;
|
|
34952
|
+
while (session3.bufBytes > MAX_BUFFER_BYTES) {
|
|
34953
|
+
const nl = session3.buffer.indexOf(`
|
|
34954
|
+
`);
|
|
34955
|
+
if (nl === -1)
|
|
34956
|
+
break;
|
|
34957
|
+
const removed = session3.buffer.slice(0, nl + 1);
|
|
34958
|
+
session3.buffer = session3.buffer.slice(nl + 1);
|
|
34959
|
+
session3.bufBytes -= Buffer.byteLength(removed, "utf8");
|
|
34960
|
+
}
|
|
34961
|
+
while (session3.bufBytes > MAX_BUFFER_BYTES && session3.buffer.length > 0) {
|
|
34962
|
+
const ch = session3.buffer[0];
|
|
34963
|
+
session3.buffer = session3.buffer.slice(1);
|
|
34964
|
+
session3.bufBytes -= Buffer.byteLength(ch, "utf8");
|
|
34965
|
+
}
|
|
34966
|
+
}
|
|
34889
34967
|
function createTerminalService(deps) {
|
|
34890
34968
|
const spawn12 = deps.spawn ?? realPtySpawn;
|
|
34891
34969
|
const platform = deps.platform ?? process.platform;
|
|
@@ -34910,13 +34988,13 @@ function createTerminalService(deps) {
|
|
|
34910
34988
|
};
|
|
34911
34989
|
return {
|
|
34912
34990
|
create({ cwd, cols, rows }) {
|
|
34913
|
-
|
|
34914
|
-
throw new Error("terminal-unsupported-platform");
|
|
34991
|
+
const shell = resolveShell({ platform, env: process.env, shellOverride: deps.shell?.() });
|
|
34915
34992
|
const terminalId = randomUUID4();
|
|
34916
|
-
const handle = spawn12(
|
|
34917
|
-
const session3 = { handle, seq: 0, idleTimer: null };
|
|
34993
|
+
const handle = spawn12(shell, [], { name: "xterm-256color", cols, rows, cwd, env: scrubEnv() });
|
|
34994
|
+
const session3 = { handle, seq: 0, idleTimer: null, buffer: "", bufBytes: 0 };
|
|
34918
34995
|
sessions.set(terminalId, session3);
|
|
34919
34996
|
handle.onData((data) => {
|
|
34997
|
+
appendToBuffer(session3, data);
|
|
34920
34998
|
deps.events.emit({ type: "terminal-output", terminalId, seq: session3.seq++, data });
|
|
34921
34999
|
});
|
|
34922
35000
|
handle.onExit(({ exitCode }) => {
|
|
@@ -34928,6 +35006,13 @@ function createTerminalService(deps) {
|
|
|
34928
35006
|
resetIdle(terminalId);
|
|
34929
35007
|
return { terminalId };
|
|
34930
35008
|
},
|
|
35009
|
+
attach(terminalId) {
|
|
35010
|
+
const s = sessions.get(terminalId);
|
|
35011
|
+
if (!s)
|
|
35012
|
+
return { ok: false };
|
|
35013
|
+
resetIdle(terminalId);
|
|
35014
|
+
return { ok: true, buffer: s.buffer, lastSeq: s.seq - 1 };
|
|
35015
|
+
},
|
|
34931
35016
|
write(terminalId, data) {
|
|
34932
35017
|
const s = sessions.get(terminalId);
|
|
34933
35018
|
if (!s)
|
|
@@ -34966,7 +35051,7 @@ function createTerminalService(deps) {
|
|
|
34966
35051
|
}
|
|
34967
35052
|
};
|
|
34968
35053
|
}
|
|
34969
|
-
var require5, SENSITIVE_ENV_KEYS;
|
|
35054
|
+
var require5, SENSITIVE_ENV_KEYS, MAX_BUFFER_BYTES;
|
|
34970
35055
|
var init_terminal_service = __esm(() => {
|
|
34971
35056
|
init_node_pty_helper();
|
|
34972
35057
|
require5 = createRequire6(import.meta.url);
|
|
@@ -34984,6 +35069,7 @@ var init_terminal_service = __esm(() => {
|
|
|
34984
35069
|
"GITHUB_TOKEN",
|
|
34985
35070
|
"NPM_TOKEN"
|
|
34986
35071
|
];
|
|
35072
|
+
MAX_BUFFER_BYTES = 256 * 1024;
|
|
34987
35073
|
});
|
|
34988
35074
|
|
|
34989
35075
|
// src/control/upload-store.ts
|
|
@@ -35661,7 +35747,8 @@ async function buildApp(paths, deps = {}) {
|
|
|
35661
35747
|
const controlEvents = createControlEventBus(logger2);
|
|
35662
35748
|
const terminalService = createTerminalService({
|
|
35663
35749
|
events: controlEvents,
|
|
35664
|
-
idleTimeoutSeconds: () => terminalIdleTimeoutSeconds(config4)
|
|
35750
|
+
idleTimeoutSeconds: () => terminalIdleTimeoutSeconds(config4),
|
|
35751
|
+
shell: () => terminalShell(config4)
|
|
35665
35752
|
});
|
|
35666
35753
|
const uploadStore = new UploadStore;
|
|
35667
35754
|
uploadStore.cleanup();
|
package/dist/config/types.d.ts
CHANGED
|
@@ -47,6 +47,8 @@ export interface TerminalConfig {
|
|
|
47
47
|
enabled: boolean;
|
|
48
48
|
/** Idle seconds before a terminal PTY is auto-killed. Defaults to 900 (15 min). */
|
|
49
49
|
idleTimeoutSeconds?: number;
|
|
50
|
+
/** Explicit shell override (absolute path or bare name). Cross-platform: wins over SHELL / platform default. */
|
|
51
|
+
shell?: string;
|
|
50
52
|
}
|
|
51
53
|
export interface FilesConfig {
|
|
52
54
|
/** Default false. When false, all fs write ops (new/rename/delete/copy) are rejected
|
|
@@ -119,4 +121,5 @@ export interface AppConfig {
|
|
|
119
121
|
}
|
|
120
122
|
export declare function terminalEnabled(config: AppConfig): boolean;
|
|
121
123
|
export declare function terminalIdleTimeoutSeconds(config: AppConfig): number;
|
|
124
|
+
export declare function terminalShell(config: AppConfig): string | undefined;
|
|
122
125
|
export declare function filesWriteEnabled(config: AppConfig): boolean;
|
|
@@ -152,6 +152,14 @@ export declare class ControlService {
|
|
|
152
152
|
fsCopy(workspace: string, path: string): Promise<{
|
|
153
153
|
path: string;
|
|
154
154
|
}>;
|
|
155
|
+
fsWrite(workspace: string, path: string, content: string, expected: {
|
|
156
|
+
mtimeMs: number;
|
|
157
|
+
size: number;
|
|
158
|
+
}): Promise<{
|
|
159
|
+
path: string;
|
|
160
|
+
mtimeMs: number;
|
|
161
|
+
size: number;
|
|
162
|
+
}>;
|
|
155
163
|
fsDownload(workspace: string, path: string): Promise<{
|
|
156
164
|
path: string;
|
|
157
165
|
base64: string;
|
|
@@ -233,6 +241,7 @@ export declare class ControlService {
|
|
|
233
241
|
createTerminal(chatKey: string, sessionAlias: string, cols: number, rows: number): Promise<{
|
|
234
242
|
terminalId: string;
|
|
235
243
|
}>;
|
|
244
|
+
attachTerminal(terminalId: string): import("./terminal-service").TerminalAttachResult;
|
|
236
245
|
writeTerminal(terminalId: string, data: string): void;
|
|
237
246
|
resizeTerminal(terminalId: string, cols: number, rows: number): void;
|
|
238
247
|
closeTerminal(terminalId: string): void;
|
|
@@ -27,10 +27,18 @@ export interface TerminalCreateInput {
|
|
|
27
27
|
cols: number;
|
|
28
28
|
rows: number;
|
|
29
29
|
}
|
|
30
|
+
export type TerminalAttachResult = {
|
|
31
|
+
ok: false;
|
|
32
|
+
} | {
|
|
33
|
+
ok: true;
|
|
34
|
+
buffer: string;
|
|
35
|
+
lastSeq: number;
|
|
36
|
+
};
|
|
30
37
|
export interface TerminalService {
|
|
31
38
|
create(input: TerminalCreateInput): {
|
|
32
39
|
terminalId: string;
|
|
33
40
|
};
|
|
41
|
+
attach(terminalId: string): TerminalAttachResult;
|
|
34
42
|
write(terminalId: string, data: string): void;
|
|
35
43
|
resize(terminalId: string, cols: number, rows: number): void;
|
|
36
44
|
close(terminalId: string): void;
|
|
@@ -39,10 +47,25 @@ export interface TerminalService {
|
|
|
39
47
|
export interface TerminalServiceDeps {
|
|
40
48
|
events: ControlEventBus;
|
|
41
49
|
idleTimeoutSeconds: () => number;
|
|
50
|
+
/** Optional explicit shell override from config (terminal.shell). */
|
|
51
|
+
shell?: () => string | undefined;
|
|
42
52
|
spawn?: PtySpawn;
|
|
43
53
|
platform?: NodeJS.Platform;
|
|
44
54
|
/** Injectable timer primitives; defaults to global setTimeout/clearTimeout. */
|
|
45
55
|
setTimer?: (fn: () => void, ms: number) => unknown;
|
|
46
56
|
clearTimer?: (id: unknown) => void;
|
|
47
57
|
}
|
|
58
|
+
export interface ResolveShellArgs {
|
|
59
|
+
platform: NodeJS.Platform;
|
|
60
|
+
env: NodeJS.ProcessEnv;
|
|
61
|
+
/** Explicit override from config terminal.shell — wins on every platform. */
|
|
62
|
+
shellOverride?: string;
|
|
63
|
+
/** Injectable executable-existence predicate (defaults to fs statSync). */
|
|
64
|
+
exists?: (p: string) => boolean;
|
|
65
|
+
}
|
|
66
|
+
/** Pick the shell to spawn. Priority: explicit override -> platform default.
|
|
67
|
+
* win32 deliberately ignores SHELL (git-bash sets it to an MSYS path that
|
|
68
|
+
* node-pty can't spawn on Windows) and scans PATH for pwsh -> powershell,
|
|
69
|
+
* falling back to %ComSpec% (cmd.exe). Never throws — always returns a value. */
|
|
70
|
+
export declare function resolveShell(args: ResolveShellArgs): string;
|
|
48
71
|
export declare function createTerminalService(deps: TerminalServiceDeps): TerminalService;
|
|
@@ -19,6 +19,7 @@ export interface FileContent {
|
|
|
19
19
|
path: string;
|
|
20
20
|
content: string;
|
|
21
21
|
size: number;
|
|
22
|
+
mtimeMs: number;
|
|
22
23
|
truncated: boolean;
|
|
23
24
|
binary: boolean;
|
|
24
25
|
}
|
|
@@ -109,6 +110,18 @@ export declare class WorkspaceFs {
|
|
|
109
110
|
* "nothing ignored". Never throws. */
|
|
110
111
|
private gitCheckIgnore;
|
|
111
112
|
readFile(workspace: string, relPath: string): Promise<FileContent>;
|
|
113
|
+
/** Overwrite an EXISTING file's content, guarded by a stale-write token. The target must
|
|
114
|
+
* resolve inside the workspace (realpath containment), be a regular file, and match the
|
|
115
|
+
* caller's `{mtimeMs,size}` token — otherwise `stale-write`. Content must be UTF-8 text
|
|
116
|
+
* (no NUL) and within the read cap. Writes atomically. Returns a fresh token. */
|
|
117
|
+
writeFile(workspace: string, relPath: string, content: string, expected: {
|
|
118
|
+
mtimeMs: number;
|
|
119
|
+
size: number;
|
|
120
|
+
}): Promise<{
|
|
121
|
+
path: string;
|
|
122
|
+
mtimeMs: number;
|
|
123
|
+
size: number;
|
|
124
|
+
}>;
|
|
112
125
|
/** Find files by name (default) or content. Name mode walks relative paths
|
|
113
126
|
* breadth-first, skipping `.git`/`node_modules` and never following symlinks (so it
|
|
114
127
|
* stays contained), applying the query plus any include/exclude globs. Content mode
|