@axiom-lattice/gateway 10.2.0 → 10.2.2
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/{WechatChannelAdapter-QLPJJD6S.mjs → WechatChannelAdapter-EEETBUR3.mjs} +2 -2
- package/dist/{a2a-standard-OANCQYQ2.mjs → a2a-standard-HZP5F74J.mjs} +2 -2
- package/dist/{chunk-6CUQGDJI.mjs → chunk-ELYWN3TJ.mjs} +8 -4
- package/dist/chunk-ELYWN3TJ.mjs.map +1 -0
- package/dist/{chunk-PAKR5N7U.mjs → chunk-IR46RQWH.mjs} +7 -6
- package/dist/chunk-IR46RQWH.mjs.map +1 -0
- package/dist/{chunk-67CDUQM3.mjs → chunk-JNP5T5N4.mjs} +3 -3
- package/dist/chunk-JNP5T5N4.mjs.map +1 -0
- package/dist/index.js +61 -35
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +54 -34
- package/dist/index.mjs.map +1 -1
- package/dist/{open-files-JPB2NBL6.mjs → open-files-WK4SQHE3.mjs} +5 -4
- package/dist/open-files-WK4SQHE3.mjs.map +1 -0
- package/dist/{sandboxProvider-FILMFVIM.mjs → sandboxProvider-LA6B5SKU.mjs} +2 -2
- package/package.json +3 -3
- package/dist/chunk-67CDUQM3.mjs.map +0 -1
- package/dist/chunk-6CUQGDJI.mjs.map +0 -1
- package/dist/chunk-PAKR5N7U.mjs.map +0 -1
- package/dist/open-files-JPB2NBL6.mjs.map +0 -1
- /package/dist/{WechatChannelAdapter-QLPJJD6S.mjs.map → WechatChannelAdapter-EEETBUR3.mjs.map} +0 -0
- /package/dist/{a2a-standard-OANCQYQ2.mjs.map → a2a-standard-HZP5F74J.mjs.map} +0 -0
- /package/dist/{sandboxProvider-FILMFVIM.mjs.map → sandboxProvider-LA6B5SKU.mjs.map} +0 -0
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
Logger,
|
|
3
3
|
getUpdates,
|
|
4
4
|
sendMessage
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-ELYWN3TJ.mjs";
|
|
6
6
|
import "./chunk-NFEGQTCC.mjs";
|
|
7
7
|
|
|
8
8
|
// src/channels/wechat/WechatChannelAdapter.ts
|
|
@@ -304,4 +304,4 @@ var wechatChannelAdapter = {
|
|
|
304
304
|
export {
|
|
305
305
|
wechatChannelAdapter
|
|
306
306
|
};
|
|
307
|
-
//# sourceMappingURL=WechatChannelAdapter-
|
|
307
|
+
//# sourceMappingURL=WechatChannelAdapter-EEETBUR3.mjs.map
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
resolveRunTaskMaxActivePerTenant,
|
|
11
11
|
saveProjectFile,
|
|
12
12
|
wakeRunTaskQueue
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-JNP5T5N4.mjs";
|
|
14
14
|
import {
|
|
15
15
|
getRateLimitService
|
|
16
16
|
} from "./chunk-4XWI7U3L.mjs";
|
|
@@ -4141,4 +4141,4 @@ data: ${JSON.stringify(errorResponse)}
|
|
|
4141
4141
|
export {
|
|
4142
4142
|
registerA2AStandardRoutes
|
|
4143
4143
|
};
|
|
4144
|
-
//# sourceMappingURL=a2a-standard-
|
|
4144
|
+
//# sourceMappingURL=a2a-standard-HZP5F74J.mjs.map
|
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
import pino from "pino";
|
|
3
3
|
import "pino-pretty";
|
|
4
4
|
import "pino-roll";
|
|
5
|
+
function resolveLegacyLogFile(env = process.env) {
|
|
6
|
+
const file = env.LOG_FILE?.trim();
|
|
7
|
+
return file ? file : null;
|
|
8
|
+
}
|
|
5
9
|
var PinoLoggerFactory = class _PinoLoggerFactory {
|
|
6
10
|
constructor() {
|
|
7
|
-
const isProd = process.env.NODE_ENV === "production";
|
|
8
11
|
const loggerConfig = {
|
|
9
12
|
// 自定义时间戳格式
|
|
10
13
|
timestamp: () => `,"@timestamp":"${(/* @__PURE__ */ new Date()).toISOString()}"`,
|
|
@@ -25,14 +28,15 @@ var PinoLoggerFactory = class _PinoLoggerFactory {
|
|
|
25
28
|
}
|
|
26
29
|
}
|
|
27
30
|
};
|
|
28
|
-
|
|
31
|
+
const logFile = resolveLegacyLogFile(process.env);
|
|
32
|
+
if (logFile) {
|
|
29
33
|
try {
|
|
30
34
|
this.pinoLogger = pino(
|
|
31
35
|
loggerConfig,
|
|
32
36
|
pino.transport({
|
|
33
37
|
target: "pino-roll",
|
|
34
38
|
options: {
|
|
35
|
-
file:
|
|
39
|
+
file: logFile,
|
|
36
40
|
frequency: "daily",
|
|
37
41
|
mkdir: true
|
|
38
42
|
}
|
|
@@ -235,4 +239,4 @@ export {
|
|
|
235
239
|
getQrCode,
|
|
236
240
|
getQrCodeStatus
|
|
237
241
|
};
|
|
238
|
-
//# sourceMappingURL=chunk-
|
|
242
|
+
//# sourceMappingURL=chunk-ELYWN3TJ.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/logger/Logger.ts","../src/channels/wechat/wechat-client.ts"],"sourcesContent":["import pino from \"pino\";\nimport \"pino-pretty\";\nimport \"pino-roll\";\nimport { AsyncLocalStorage } from \"async_hooks\";\n\nexport interface LoggerContext {\n \"x-user-id\"?: string;\n \"x-tenant-id\"?: string;\n \"x-request-id\"?: string;\n \"x-task-id\"?: string;\n \"x-thread-id\"?: string;\n}\n\nexport interface LoggerOptions {\n name?: string;\n serviceName?: string;\n context?: LoggerContext;\n}\n\n/**\n * Resolve the optional file destination for the legacy pino logger.\n *\n * File logging is strictly opt-in through `LOG_FILE`. `NODE_ENV` is ignored so\n * a production process on a read-only filesystem (e.g. Kubernetes) never tries\n * to create a `logs/` directory unless the operator asked for it.\n *\n * @param env Environment variables to read (defaults to `process.env`).\n * @returns The configured log file path, or `null` when file logging is off.\n *\n * @example\n * ```ts\n * resolveLegacyLogFile({ LOG_FILE: \"./logs/app\" }); // → \"./logs/app\"\n * resolveLegacyLogFile({ NODE_ENV: \"production\" }); // → null\n * ```\n */\nexport function resolveLegacyLogFile(\n env: NodeJS.ProcessEnv = process.env\n): string | null {\n const file = env.LOG_FILE?.trim();\n return file ? file : null;\n}\n\n/**\n * 单例的Pino日志工厂类,管理底层pino实例\n */\nclass PinoLoggerFactory {\n private static instance: PinoLoggerFactory;\n private pinoLogger: pino.Logger;\n\n private constructor() {\n const loggerConfig: pino.LoggerOptions = {\n // 自定义时间戳格式\n timestamp: () => `,\"@timestamp\":\"${new Date().toISOString()}\"`,\n\n // 关闭默认的时间戳键\n base: {\n \"@version\": \"1\",\n app_name: \"lattice\",\n service_name: \"lattice/graph-server\",\n thread_name: \"main\",\n logger_name: \"lattice-graph-logger\",\n },\n\n formatters: {\n level: (label, number) => {\n return {\n level: label.toUpperCase(),\n level_value: number * 1000,\n };\n },\n },\n };\n\n // 仅在显式配置 LOG_FILE 时写入文件;默认输出到控制台\n const logFile = resolveLegacyLogFile(process.env);\n if (logFile) {\n try {\n this.pinoLogger = pino(\n loggerConfig,\n pino.transport({\n target: \"pino-roll\",\n options: {\n file: logFile,\n frequency: \"daily\",\n mkdir: true,\n },\n })\n );\n } catch (error) {\n console.error(\n \"无法初始化 pino-roll 日志记录器,回退到控制台日志\",\n error\n );\n // 回退到控制台输出\n this.pinoLogger = pino({\n ...loggerConfig,\n transport: {\n target: \"pino-pretty\",\n options: {\n colorize: true,\n },\n },\n });\n }\n } else {\n // 默认使用格式化输出\n this.pinoLogger = pino({\n ...loggerConfig,\n transport: {\n target: \"pino-pretty\",\n options: {\n colorize: true,\n },\n },\n });\n }\n }\n\n public static getInstance(): PinoLoggerFactory {\n if (!PinoLoggerFactory.instance) {\n PinoLoggerFactory.instance = new PinoLoggerFactory();\n }\n return PinoLoggerFactory.instance;\n }\n\n public getPinoLogger(): pino.Logger {\n return this.pinoLogger;\n }\n}\n\n/**\n * Logger类,可以创建多个实例,每个实例有自己的上下文\n */\nexport class Logger {\n private context: LoggerContext;\n private name: string;\n private serviceName: string;\n\n constructor(options?: LoggerOptions) {\n this.context = options?.context || {};\n this.name = options?.name || \"lattice-graph-logger\";\n this.serviceName = options?.serviceName || \"lattice/graph-server\";\n }\n\n /**\n * 获取合并了上下文的日志对象\n * @param additionalContext 额外的上下文数据\n * @returns 带有上下文的pino日志对象\n */\n private getContextualLogger(additionalContext?: object): pino.Logger {\n const pinoLogger = PinoLoggerFactory.getInstance().getPinoLogger();\n\n // 合并Logger实例的上下文和额外上下文\n const contextObj = {\n \"x-user-id\": this.context[\"x-user-id\"] || \"\",\n \"x-tenant-id\": this.context[\"x-tenant-id\"] || \"\",\n \"x-request-id\": this.context[\"x-request-id\"] || \"\",\n \"x-task-id\": this.context[\"x-task-id\"] || \"\",\n \"x-thread-id\": this.context[\"x-thread-id\"] || \"\",\n service_name: this.serviceName,\n logger_name: this.name,\n ...additionalContext,\n };\n\n // 创建带有上下文的子日志记录器\n return pinoLogger.child(contextObj);\n }\n\n info(msg: string, obj?: object): void {\n this.getContextualLogger(obj).info(msg);\n }\n\n error(msg: string, obj?: object | Error): void {\n this.getContextualLogger(obj).error(msg);\n }\n\n warn(msg: string, obj?: object): void {\n this.getContextualLogger(obj).warn(msg);\n }\n\n debug(msg: string, obj?: object): void {\n this.getContextualLogger(obj).debug(msg);\n }\n\n /**\n * 更新Logger实例的上下文\n */\n updateContext(context: Partial<LoggerContext>): void {\n this.context = {\n ...this.context,\n ...context,\n };\n }\n\n /**\n * 创建一个新的Logger实例,继承当前Logger的上下文\n */\n child(options: Partial<LoggerOptions>): Logger {\n return new Logger({\n name: options.name || this.name,\n serviceName: options.serviceName || this.serviceName,\n context: {\n ...this.context,\n ...options.context,\n },\n });\n }\n}\n","import { randomBytes } from \"crypto\";\n\nconst DEFAULT_BASE_URL = \"https://ilinkai.weixin.qq.com\";\n\nfunction getBaseUrl(): string {\n return process.env.WECHAT_ILINK_BASE_URL || DEFAULT_BASE_URL;\n}\n\nfunction generateWechatUin(): string {\n const buf = Buffer.alloc(4);\n buf.writeUInt32BE(Math.floor(Math.random() * 0xffffffff), 0);\n return buf.toString(\"base64url\");\n}\n\nasync function apiGet<T>(path: string, extraHeaders?: Record<string, string>): Promise<T> {\n const url = `${getBaseUrl()}${path}`;\n const headers: Record<string, string> = { ...extraHeaders };\n const res = await fetch(url, { headers });\n if (!res.ok) {\n throw new Error(`iLink GET ${path} failed: ${res.status} ${res.statusText}`);\n }\n return res.json() as Promise<T>;\n}\n\nasync function apiGetAuth<T>(path: string, botToken: string, extraHeaders?: Record<string, string>): Promise<T> {\n const url = `${getBaseUrl()}${path}`;\n const headers: Record<string, string> = {\n \"AuthorizationType\": \"ilink_bot_token\",\n \"X-WECHAT-UIN\": generateWechatUin(),\n \"Authorization\": `Bearer ${botToken}`,\n ...extraHeaders,\n };\n const res = await fetch(url, { headers });\n if (!res.ok) {\n throw new Error(`iLink GET ${path} failed: ${res.status} ${res.statusText}`);\n }\n return res.json() as Promise<T>;\n}\n\nasync function apiPost<T>(path: string, body: unknown, botToken: string, signal?: AbortSignal): Promise<T> {\n const url = `${getBaseUrl()}${path}`;\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"AuthorizationType\": \"ilink_bot_token\",\n \"X-WECHAT-UIN\": generateWechatUin(),\n \"Authorization\": `Bearer ${botToken}`,\n };\n\n const res = await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n signal,\n });\n if (!res.ok) {\n throw new Error(`iLink POST ${path} failed: ${res.status} ${res.statusText}`);\n }\n return res.json() as Promise<T>;\n}\n\nexport async function getUpdates(\n botToken: string,\n syncBuffer: string,\n signal?: AbortSignal,\n): Promise<{ msgs: Array<{\n message_type?: number;\n from_user_id?: string;\n client_id?: string;\n item_list?: Array<{ type?: number; text_item?: { text?: string } }>;\n context_token?: string;\n create_time_ms?: number;\n}>; syncBuffer: string }> {\n const data = await apiPost<{\n ret?: number;\n errcode?: number;\n errmsg?: string;\n msgs?: Array<{\n message_type?: number;\n from_user_id?: string;\n client_id?: string;\n item_list?: Array<{ type?: number; text_item?: { text?: string } }>;\n context_token?: string;\n create_time_ms?: number;\n }>;\n get_updates_buf?: string;\n }>(\"/ilink/bot/getupdates\", {\n get_updates_buf: syncBuffer,\n base_info: { channel_version: \"1.0.2\" },\n }, botToken, signal);\n\n // Check API-level error codes (HTTP 200 can still be an error)\n if ((data.ret !== undefined && data.ret !== 0) || (data.errcode !== undefined && data.errcode !== 0)) {\n throw new Error(`iLink getUpdates error: ret=${data.ret} errcode=${data.errcode} errmsg=${data.errmsg ?? \"\"}`);\n }\n\n return {\n msgs: data.msgs ?? [],\n syncBuffer: data.get_updates_buf ?? syncBuffer,\n };\n}\n\nfunction generateClientId(): string {\n return `axiom-wechat:${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nexport async function sendMessage(\n botToken: string,\n toUserId: string,\n text: string,\n contextToken: string,\n): Promise<void> {\n await apiPost<unknown>(\"/ilink/bot/sendmessage\", {\n msg: {\n from_user_id: \"\",\n to_user_id: toUserId,\n client_id: generateClientId(),\n message_type: 2, // MSG_TYPE_BOT\n message_state: 2, // MSG_STATE_FINISH\n item_list: [{ type: 1, text_item: { text } }],\n context_token: contextToken,\n },\n base_info: { channel_version: \"1.0.2\" },\n }, botToken);\n}\n\nexport async function getQrCode(): Promise<{ qrcode: string; qrcodeImgUrl: string }> {\n const data = await apiGet<{ qrcode?: string; qrcode_img_content?: string }>(\n \"/ilink/bot/get_bot_qrcode?bot_type=3\",\n );\n if (!data.qrcode || !data.qrcode_img_content) {\n throw new Error(\"Failed to get WeChat QR code\");\n }\n return { qrcode: data.qrcode, qrcodeImgUrl: data.qrcode_img_content };\n}\n\nexport async function getQrCodeStatus(\n qrcode: string,\n signal?: AbortSignal,\n): Promise<{\n status: \"wait\" | \"scaned\" | \"confirmed\" | \"expired\";\n botToken?: string;\n uin?: string;\n botId?: string;\n baseUrl?: string;\n}> {\n const data = await apiGet<{\n status?: string;\n bot_token?: string;\n ilink_bot_id?: string;\n ilink_user_id?: string;\n baseurl?: string;\n }>(`/ilink/bot/get_qrcode_status?qrcode=${encodeURIComponent(qrcode)}`, {\n \"iLink-App-ClientVersion\": \"1\",\n });\n return {\n status: (data.status as \"wait\" | \"scaned\" | \"confirmed\" | \"expired\") || \"wait\",\n botToken: data.bot_token,\n uin: data.ilink_user_id,\n botId: data.ilink_bot_id,\n baseUrl: data.baseurl,\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/services/open/providers/sandboxProvider.ts
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { sandboxLatticeManager } from "@axiom-lattice/core";
|
|
3
|
+
import { resolveSandboxAgentsPathAlias, sandboxLatticeManager } from "@axiom-lattice/core";
|
|
4
4
|
var MAX_OUTPUT_BYTES = 64e3;
|
|
5
5
|
async function resolveContextSandbox(ctx) {
|
|
6
6
|
const sandboxName = ctx.runConfig?.sandboxName;
|
|
@@ -63,20 +63,21 @@ function createOpenSandboxProvider() {
|
|
|
63
63
|
switch (actionName) {
|
|
64
64
|
case "files_read": {
|
|
65
65
|
const { path } = args;
|
|
66
|
-
const file = await sandbox.file.readFile(path);
|
|
66
|
+
const file = await sandbox.file.readFile(resolveSandboxAgentsPathAlias(path));
|
|
67
67
|
return { content: [{ type: "text", text: file.content }] };
|
|
68
68
|
}
|
|
69
69
|
case "files_list": {
|
|
70
70
|
const { path, recursive } = args;
|
|
71
|
-
const listing = await sandbox.file.listPath(path, { recursive });
|
|
71
|
+
const listing = await sandbox.file.listPath(resolveSandboxAgentsPathAlias(path), { recursive });
|
|
72
72
|
return {
|
|
73
73
|
content: [{ type: "text", text: JSON.stringify(listing.files) }]
|
|
74
74
|
};
|
|
75
75
|
}
|
|
76
76
|
case "files_write": {
|
|
77
77
|
const { path, content } = args;
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
const resolvedPath = resolveSandboxAgentsPathAlias(path);
|
|
79
|
+
await sandbox.file.writeFile(resolvedPath, content);
|
|
80
|
+
return { content: [{ type: "text", text: `wrote ${resolvedPath}` }] };
|
|
80
81
|
}
|
|
81
82
|
case "shell_exec": {
|
|
82
83
|
const { command, exec_dir, timeout } = args;
|
|
@@ -113,4 +114,4 @@ export {
|
|
|
113
114
|
resolveContextSandbox,
|
|
114
115
|
createOpenSandboxProvider
|
|
115
116
|
};
|
|
116
|
-
//# sourceMappingURL=chunk-
|
|
117
|
+
//# sourceMappingURL=chunk-IR46RQWH.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/services/open/providers/sandboxProvider.ts"],"sourcesContent":["/**\n * Built-in sandbox provider for the Open surface: project-scoped file\n * read/list and shell execution with self-binding for sandbox tokens.\n *\n * API keys that hold sandbox-domain grants resolve the sandbox by the\n * credential's tenant + project (their project's sandbox). Sandbox tokens\n * with self-binding are resolved by the token's sandboxName field.\n */\n\nimport { z } from \"zod\";\nimport type { OpenExecutionContext, OpenExecutionResult } from \"@axiom-lattice/protocols\";\nimport { resolveSandboxAgentsPathAlias, sandboxLatticeManager } from \"@axiom-lattice/core\";\nimport type { OpenProviderDeclaration } from \"../openLattice\";\n\nconst MAX_OUTPUT_BYTES = 64000;\n\n/** Resolve the credential-addressed sandbox (self-binding when the token names one). */\nexport async function resolveContextSandbox(ctx: OpenExecutionContext) {\n const sandboxName = ctx.runConfig?.sandboxName;\n if (typeof sandboxName === \"string\" && sandboxName.length > 0) {\n try {\n return await sandboxLatticeManager.getSandbox(sandboxName);\n } catch {\n // fall through to config resolution (e.g. sandbox not materialized yet)\n }\n }\n return sandboxLatticeManager.getSandboxFromConfig({\n assistant_id: \"\",\n thread_id: \"\",\n tenantId: ctx.tenantId,\n workspaceId: ctx.workspaceId ?? \"\",\n projectId: ctx.projectId,\n });\n}\n\nexport function createOpenSandboxProvider(): OpenProviderDeclaration {\n return {\n domain: \"sandbox\",\n label: \"Sandbox\",\n source: \"builtin\",\n scopeKind: \"project\",\n actions: [\n {\n name: \"files_read\",\n label: \"Read files\",\n description: \"Read a file from the project sandbox.\",\n schema: { path: z.string().min(1) },\n annotations: { readOnlyHint: true, destructiveHint: false },\n },\n {\n name: \"files_list\",\n label: \"List files\",\n description: \"List files under a directory in the project sandbox.\",\n schema: { path: z.string(), recursive: z.boolean().optional() },\n annotations: { readOnlyHint: true, destructiveHint: false },\n },\n {\n name: \"files_write\",\n label: \"Write files\",\n description: \"Write content to a file in the project sandbox.\",\n schema: { path: z.string().min(1), content: z.string() },\n annotations: { readOnlyHint: false, destructiveHint: true },\n },\n {\n name: \"shell_exec\",\n label: \"Run shell commands\",\n description:\n \"Execute a shell command in the project sandbox. Returns stdout (up to 64KB) and exit code.\",\n schema: {\n command: z.string().min(1),\n exec_dir: z.string().optional(),\n timeout: z.number().int().min(1000).optional(),\n },\n annotations: { readOnlyHint: false, destructiveHint: true },\n },\n ],\n execute: async (\n actionName: string,\n args: unknown,\n ctx: OpenExecutionContext,\n ): Promise<OpenExecutionResult> => {\n // Self-binding is inherent: the sandbox is resolved from the credential's\n // tenant/workspace/project triple (minted server-side, unforgeable), so a\n // sandbox token can only ever reach the sandbox it was issued for.\n const sandbox = await resolveContextSandbox(ctx);\n\n switch (actionName) {\n case \"files_read\": {\n const { path } = args as { path: string };\n const file = await sandbox.file.readFile(resolveSandboxAgentsPathAlias(path));\n return { content: [{ type: \"text\", text: file.content }] };\n }\n case \"files_list\": {\n const { path, recursive } = args as { path: string; recursive?: boolean };\n const listing = await sandbox.file.listPath(resolveSandboxAgentsPathAlias(path), { recursive });\n return {\n content: [{ type: \"text\", text: JSON.stringify(listing.files) }],\n };\n }\n case \"files_write\": {\n const { path, content } = args as { path: string; content: string };\n const resolvedPath = resolveSandboxAgentsPathAlias(path);\n await sandbox.file.writeFile(resolvedPath, content);\n return { content: [{ type: \"text\", text: `wrote ${resolvedPath}` }] };\n }\n case \"shell_exec\": {\n const { command, exec_dir, timeout } = args as {\n command: string;\n exec_dir?: string;\n timeout?: number;\n };\n const result = await sandbox.shell.execCommand({\n command,\n exec_dir,\n timeoutMs: timeout ?? 120_000,\n });\n const truncated =\n result.output.length > MAX_OUTPUT_BYTES\n ? result.output.slice(0, MAX_OUTPUT_BYTES) +\n `\\n...[truncated ${result.output.length - MAX_OUTPUT_BYTES} bytes]`\n : result.output;\n return {\n content: [\n {\n type: \"text\",\n text: `exit_code: ${result.exit_code}\\n${truncated}`,\n },\n ],\n };\n }\n default:\n return {\n content: [\n { type: \"text\", text: `Unknown sandbox action: ${actionName}` },\n ],\n isError: true,\n };\n }\n },\n };\n}"],"mappings":"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","names":[]}
|
|
@@ -67,7 +67,7 @@ function getPlatformSettingsProvider() {
|
|
|
67
67
|
// src/services/workspace-file-service.ts
|
|
68
68
|
import * as fs2 from "fs/promises";
|
|
69
69
|
import * as path2 from "path";
|
|
70
|
-
import { getStoreLattice, getSandBoxManager } from "@axiom-lattice/core";
|
|
70
|
+
import { getStoreLattice, getSandBoxManager, resolveSandboxAgentsPathAlias } from "@axiom-lattice/core";
|
|
71
71
|
|
|
72
72
|
// src/utils/path-containment.ts
|
|
73
73
|
import * as fs from "fs/promises";
|
|
@@ -184,7 +184,7 @@ async function saveProjectFile(params) {
|
|
|
184
184
|
workspaceId,
|
|
185
185
|
projectId: params.projectId
|
|
186
186
|
};
|
|
187
|
-
const relativePath = pathValue ? path2.posix.join(pathValue, filename) : path2.posix.join("/project/uploads", filename);
|
|
187
|
+
const relativePath = pathValue ? path2.posix.join(resolveSandboxAgentsPathAlias(pathValue), filename) : path2.posix.join("/project/uploads", filename);
|
|
188
188
|
const isBinary = isBinaryContentType(filename);
|
|
189
189
|
const volumeBackend = await sandboxManager.getVolumeBackendForPath(
|
|
190
190
|
volumeConfig,
|
|
@@ -262,4 +262,4 @@ export {
|
|
|
262
262
|
saveProjectFile,
|
|
263
263
|
formatFileRefsSection
|
|
264
264
|
};
|
|
265
|
-
//# sourceMappingURL=chunk-
|
|
265
|
+
//# sourceMappingURL=chunk-JNP5T5N4.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/services/run-cancel/runCancelRuntime.ts","../src/services/run-task/runTaskQuota.ts","../src/services/run-task/runTaskRuntimeRegistry.ts","../src/services/settings/settingsRuntime.ts","../src/services/workspace-file-service.ts","../src/utils/path-containment.ts","../src/services/files/task-file-refs.ts"],"sourcesContent":["import type { RunCancelRouter } from \"./RunCancelRouter\";\n\nlet runCancelRouter: RunCancelRouter | undefined;\n\n/**\n * Register the process-wide run cancellation router.\n *\n * @param router - Router available to HTTP controllers, or `undefined` to\n * clear it during shutdown.\n *\n * @remarks\n * The router is only constructed when the gateway uses the Redis chunk source,\n * a Redis URL, and a durable run ledger. Controllers treat a missing router as\n * cancellation being unavailable rather than aborting locally.\n */\nexport function setRunCancelRouter(router?: RunCancelRouter): void {\n runCancelRouter = router;\n}\n\n/**\n * Return the registered run cancellation router, if any.\n *\n * @returns The process-wide router, or `undefined` when cancellation routing\n * is not configured for this replica.\n */\nexport function getRunCancelRouter(): RunCancelRouter | undefined {\n return runCancelRouter;\n}\n\n/**\n * Clear the process-wide run cancellation router between tests.\n *\n * @remarks Test-only helper; production code clears the router through\n * {@link setRunCancelRouter} during shutdown.\n */\nexport function resetRunCancelRouterForTests(): void {\n runCancelRouter = undefined;\n}\n","import type { PlatformSettingsProvider } from \"@axiom-lattice/protocols\";\n\n/**\n * Resolves the optional per-tenant run-queue admission limit.\n *\n * @param env Environment source; defaults to `process.env`.\n * @param settings Optional platform settings provider; its cached snapshot\n * takes precedence over the environment.\n * @returns The configured positive safe integer, or `undefined` when unlimited.\n * @remarks Unset, blank, zero, negative, fractional and unsafe values all mean\n * unlimited. An explicit platform `null` means unlimited and overrides the\n * environment. This is admission control (pending + running `run_api` rows per\n * tenant); it is not fair scheduling or per-tenant execution concurrency.\n */\nexport function resolveRunTaskMaxActivePerTenant(\n env: NodeJS.ProcessEnv = process.env,\n settings?: Pick<PlatformSettingsProvider, \"getRateLimits\">,\n): number | undefined {\n const platform = settings?.getRateLimits().maxActiveRunTasksPerTenant;\n if (platform === null) return undefined;\n if (platform !== undefined) return platform;\n return resolvePositiveInteger(env.RUN_TASK_MAX_ACTIVE_PER_TENANT);\n}\n\n/**\n * Resolves the optional per-tenant execution concurrency cap.\n *\n * @param env Environment source; defaults to `process.env`.\n * @returns The configured positive safe integer, or `undefined` when unlimited.\n * @remarks Unset, blank, zero, negative, fractional and unsafe values all mean\n * unlimited. This caps `running` `run_api` rows per tenant; it does not admit\n * or reject new work (that is {@link resolveRunTaskMaxActivePerTenant}) and it\n * is not strict round-robin fairness across tenants.\n */\nexport function resolveRunTaskMaxRunningPerTenant(\n env: NodeJS.ProcessEnv = process.env,\n): number | undefined {\n return resolvePositiveInteger(env.RUN_TASK_MAX_RUNNING_PER_TENANT);\n}\n\nfunction resolvePositiveInteger(raw: string | undefined): number | undefined {\n if (raw === undefined) return undefined;\n const parsed = Number(raw);\n return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : undefined;\n}\n","import type {\n RunTaskWakePublisher,\n RunTaskWakeStats,\n} from \"./RedisRunTaskWake\";\nimport type { RunTaskRuntime } from \"./RunTaskRuntime\";\n\n/** Structural source of wake transport posture for process metrics. */\nexport interface RunTaskWakeStatsSource {\n /** Returns a point-in-time wake posture snapshot. */\n stats(): RunTaskWakeStats;\n}\n\nlet current: RunTaskRuntime | undefined;\nlet publisher: RunTaskWakePublisher | undefined;\nlet wakeStatsSource: RunTaskWakeStatsSource | undefined;\n\n/**\n * Registers the process-local run task runtime.\n *\n * @param runtime The running runtime, or `undefined` on shutdown.\n * @remarks The registry is intentionally process-local: it can only wake the\n * worker in this process. Other replicas still discover pending runs through DB\n * polling, which remains authoritative.\n */\nexport function setRunTaskRuntime(runtime?: RunTaskRuntime): void {\n current = runtime;\n}\n\n/**\n * Returns the process-local run task runtime, when this replica runs one.\n *\n * @returns The registered runtime, or `undefined`.\n */\nexport function getRunTaskRuntime(): RunTaskRuntime | undefined {\n return current;\n}\n\n/**\n * Registers the best-effort cross-replica wake publisher.\n *\n * @param next The Redis-backed publisher, or `undefined` on shutdown.\n * @remarks API-only replicas may register a publisher without subscribing. A\n * missing publisher leaves the process-local wake (and DB polling) intact.\n */\nexport function setRunTaskWakePublisher(\n next?: RunTaskWakePublisher,\n): void {\n publisher = next;\n}\n\n/**\n * Registers the process-local wake transport used for metrics.\n *\n * @param next The wake transport, or `undefined` on shutdown.\n * @remarks Kept separate from the publish registry so API-only replicas can\n * expose publisher connection posture even without a local worker.\n */\nexport function setRunTaskWakeStatsSource(\n next?: RunTaskWakeStatsSource,\n): void {\n wakeStatsSource = next;\n}\n\n/**\n * Returns the process-local wake transport registered for metrics.\n *\n * @returns The wake stats source, or `undefined` when no Redis wake exists.\n */\nexport function getRunTaskWakeStatsSource(): RunTaskWakeStatsSource | undefined {\n return wakeStatsSource;\n}\n\n/**\n * Best-effort wake for newly created pending runs.\n *\n * @returns A promise that resolves after the local wake attempt; the Redis\n * publish continues in the background.\n * @remarks Never authoritative and never throws. The local runtime is woken\n * first so a hung or failed publisher cannot suppress it. The cross-replica\n * publish is fire-and-forget and wakes other worker replicas; this replica does\n * not depend on round-tripping its own frame. A missing runtime, missing\n * publisher, or transport failure leaves the durable run to polling.\n */\nexport async function wakeRunTaskQueue(): Promise<void> {\n try {\n await current?.wake();\n } catch {\n // Polling is the durable fallback; wake failures are best-effort.\n }\n const next = publisher;\n if (!next) return;\n try {\n void next.publish().catch(() => undefined);\n } catch {\n // Polling is the durable fallback; wake failures are best-effort.\n }\n}\n","import type { PlatformSettingsProvider } from \"@axiom-lattice/protocols\";\n\n/**\n * Composition-root provider used by legacy gateway route modules.\n *\n * New code should accept an injected {@link PlatformSettingsProvider}; this\n * process-local accessor only bridges modules that are not yet wired through\n * dependency injection.\n */\nlet current: PlatformSettingsProvider | undefined;\n\n/** Installs (or clears) the process-local platform settings provider. */\nexport function setPlatformSettingsProvider(\n provider: PlatformSettingsProvider | undefined,\n): void {\n current = provider;\n}\n\n/** Returns the process-local provider, or undefined before composition. */\nexport function getPlatformSettingsProvider():\n | PlatformSettingsProvider\n | undefined {\n return current;\n}\n","import * as fs from \"fs/promises\";\nimport * as path from \"path\";\nimport { getStoreLattice, getSandBoxManager, resolveSandboxAgentsPathAlias } from \"@axiom-lattice/core\";\nimport { isPathWithinRoot } from \"../utils/path-containment\";\n\n/** Binary file extensions handled as raw bytes rather than UTF-8 text. */\nconst BINARY_EXTENSIONS = new Set([\n \"pdf\", \"xlsx\", \"xls\", \"docx\", \"doc\", \"pptx\", \"ppt\",\n \"png\", \"jpg\", \"jpeg\", \"gif\", \"bmp\", \"ico\", \"webp\",\n \"mp3\", \"mp4\", \"webm\", \"ogg\", \"wav\", \"avi\", \"mov\",\n \"m4v\", \"mkv\", \"3gp\", \"m4a\", \"aac\", \"flac\", \"opus\", \"oga\", \"wma\", \"aiff\",\n \"zip\", \"tar\", \"gz\", \"rar\", \"7z\",\n]);\n\n/**\n * Whether a filename denotes a binary (non-UTF-8) payload, used to choose the\n * raw-bytes write path in sandbox volume backends.\n */\nexport function isBinaryContentType(filename: string): boolean {\n const ext = filename.split(\".\").pop()?.toLowerCase() || \"\";\n return BINARY_EXTENSIONS.has(ext);\n}\n\n/**\n * Filesystem root directory for a project, mirroring the layout used by the\n * workspace file backend (`/lattice_store/tenants/:tenant/workspaces/:ws/:project`).\n */\nexport function filesystemRootDirectory(\n tenantId: string,\n workspaceId: string,\n projectId: string,\n): string {\n return `/lattice_store/tenants/${tenantId}/workspaces/${workspaceId}/${projectId}`;\n}\n\n/** Raised when a file write request is rejected for path/name safety reasons. */\nexport class ProjectFileValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ProjectFileValidationError\";\n }\n}\n\n/** Parameters for writing a file into a project's workspace storage. */\nexport interface SaveProjectFileParams {\n tenantId: string;\n projectId: string;\n /**\n * Pre-resolved workspace id. When omitted it is derived from the project via\n * the project store (needed by callers that only know the project, e.g. A2A).\n */\n workspaceId?: string;\n name: string;\n bytes: Uint8Array;\n mimeType?: string;\n /** Relative subdirectory under the project root; sandbox defaults to `/project/uploads`. */\n path?: string;\n assistantId?: string;\n}\n\n/** Result of a successful project-file write. */\nexport interface SavedProjectFile {\n /** Storage path usable as a `TaskFileRef.uri` (e.g. `/project/uploads/a.pdf`). */\n uri: string;\n name: string;\n size: number;\n}\n\n/**\n * Persist file bytes into a project's workspace storage and return a URI to it.\n *\n * Supports both sandbox-backed workspaces (delegating to the sandbox/volume\n * upload API) and filesystem-backed workspaces (writing under the project root).\n * The returned URI is the storage path agents reference via {@link TaskFileRef}.\n */\nexport async function saveProjectFile(\n params: SaveProjectFileParams,\n): Promise<SavedProjectFile> {\n const workspaceStore = getStoreLattice(\"default\", \"workspace\").store;\n const projectStore = getStoreLattice(\"default\", \"project\").store;\n\n let workspaceId = params.workspaceId;\n if (!workspaceId) {\n const project = await projectStore.getProjectById(\n params.tenantId,\n params.projectId,\n );\n if (!project) {\n throw new Error(\"Project not found\");\n }\n workspaceId = project.workspaceId;\n }\n if (!workspaceId) throw new Error(\"Workspace not found\");\n\n const workspace = await workspaceStore.getWorkspaceById(\n params.tenantId,\n workspaceId,\n );\n if (!workspace) {\n throw new Error(\"Workspace not found\");\n }\n\n const buffer = Buffer.from(params.bytes);\n const filename = params.name || \"file\";\n const pathValue = params.path;\n\n // Validate the untrusted filename before any path join or disk I/O, equally\n // for sandbox and filesystem branches: reject anything that is not a bare\n // basename on either path flavor (blocks \"../../etc/passwd\", \"a\\\\b\", \"..\").\n if (\n path.basename(filename) !== filename ||\n path.posix.basename(filename) !== filename ||\n filename === \"..\"\n ) {\n throw new ProjectFileValidationError(\"Invalid filename\");\n }\n\n if (workspace.storageType === \"sandbox\") {\n const sandboxManager = getSandBoxManager();\n const volumeConfig = {\n assistant_id: params.assistantId || \"\",\n thread_id: \"\",\n tenantId: params.tenantId,\n workspaceId,\n projectId: params.projectId,\n };\n\n const relativePath = pathValue\n ? path.posix.join(resolveSandboxAgentsPathAlias(pathValue), filename)\n : path.posix.join(\"/project/uploads\", filename);\n\n const isBinary = isBinaryContentType(filename);\n const volumeBackend = await sandboxManager.getVolumeBackendForPath(\n volumeConfig,\n relativePath,\n );\n if (volumeBackend && !isBinary) {\n await volumeBackend.write(relativePath, buffer.toString(\"utf-8\"));\n } else if (volumeBackend && volumeBackend.writeBinary) {\n await volumeBackend.writeBinary(relativePath, buffer);\n } else {\n const sandbox = await sandboxManager.getSandboxFromConfig(volumeConfig);\n await sandbox.file.uploadFile({ file: relativePath, data: buffer });\n }\n\n return { uri: relativePath, name: filename, size: buffer.length };\n }\n\n const rootDir = filesystemRootDirectory(\n params.tenantId,\n workspaceId,\n params.projectId,\n );\n await fs.mkdir(rootDir, { recursive: true });\n const relativeDirectory = pathValue?.replace(/^[/\\\\]+/, \"\") || \".\";\n const targetDir = path.resolve(rootDir, relativeDirectory);\n if (!isPathWithinRoot(path.resolve(rootDir), targetDir)) {\n throw new ProjectFileValidationError(\n \"Upload path must stay within the project directory\",\n );\n }\n await fs.mkdir(targetDir, { recursive: true });\n\n const [resolvedRoot, resolvedTargetDir] = await Promise.all([\n fs.realpath(rootDir),\n fs.realpath(targetDir),\n ]);\n if (!isPathWithinRoot(resolvedRoot, resolvedTargetDir)) {\n throw new ProjectFileValidationError(\n \"Upload path must stay within the project directory\",\n );\n }\n\n const targetPath = path.join(resolvedTargetDir, filename);\n await fs.writeFile(targetPath, buffer);\n const stat = await fs.stat(targetPath);\n\n const uri = path.posix.join(\"/\", relativeDirectory, filename);\n return { uri, name: filename, size: stat.size };\n}\n","import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nexport class PathOutsideRootError extends Error {\n constructor(\n public readonly rootPath: string,\n public readonly targetPath: string,\n ) {\n super(`Path is outside the allowed root: ${targetPath}`);\n this.name = \"PathOutsideRootError\";\n }\n}\n\nexport function isPathWithinRoot(rootPath: string, targetPath: string): boolean {\n const relative = path.relative(rootPath, targetPath);\n return (\n relative === \"\" ||\n (relative !== \"..\" &&\n !relative.startsWith(`..${path.sep}`) &&\n !path.isAbsolute(relative))\n );\n}\n\nexport async function resolvePathWithinRoot(\n rootPath: string,\n targetPath: string,\n): Promise<{ rootPath: string; targetPath: string }> {\n const [resolvedRoot, resolvedTarget] = await Promise.all([\n fs.realpath(rootPath),\n fs.realpath(targetPath),\n ]);\n\n if (!isPathWithinRoot(resolvedRoot, resolvedTarget)) {\n throw new PathOutsideRootError(resolvedRoot, resolvedTarget);\n }\n\n return { rootPath: resolvedRoot, targetPath: resolvedTarget };\n}\n","import type { TaskFileRef } from \"@axiom-lattice/protocols\";\n\n/** Render task file references for Agent-visible message text. */\nexport function formatFileRefsSection(refs: TaskFileRef[]): string {\n const lines = refs.map((ref) => {\n const label = ref.name ?? ref.uri;\n const mime = ref.mimeType ? ` (${ref.mimeType})` : \"\";\n return `- ${label}${mime}: ${ref.uri}`;\n });\n return [\"[附件]\", ...lines].join(\"\\n\");\n}\n"],"mappings":"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","names":["current","fs","path","relative","stat"]}
|
package/dist/index.js
CHANGED
|
@@ -115,6 +115,10 @@ var init_settingsRuntime = __esm({
|
|
|
115
115
|
});
|
|
116
116
|
|
|
117
117
|
// src/logger/Logger.ts
|
|
118
|
+
function resolveLegacyLogFile(env = process.env) {
|
|
119
|
+
const file = env.LOG_FILE?.trim();
|
|
120
|
+
return file ? file : null;
|
|
121
|
+
}
|
|
118
122
|
var import_pino, import_pino_pretty, import_pino_roll, PinoLoggerFactory, Logger;
|
|
119
123
|
var init_Logger = __esm({
|
|
120
124
|
"src/logger/Logger.ts"() {
|
|
@@ -124,7 +128,6 @@ var init_Logger = __esm({
|
|
|
124
128
|
import_pino_roll = require("pino-roll");
|
|
125
129
|
PinoLoggerFactory = class _PinoLoggerFactory {
|
|
126
130
|
constructor() {
|
|
127
|
-
const isProd = process.env.NODE_ENV === "production";
|
|
128
131
|
const loggerConfig = {
|
|
129
132
|
// 自定义时间戳格式
|
|
130
133
|
timestamp: () => `,"@timestamp":"${(/* @__PURE__ */ new Date()).toISOString()}"`,
|
|
@@ -145,14 +148,15 @@ var init_Logger = __esm({
|
|
|
145
148
|
}
|
|
146
149
|
}
|
|
147
150
|
};
|
|
148
|
-
|
|
151
|
+
const logFile = resolveLegacyLogFile(process.env);
|
|
152
|
+
if (logFile) {
|
|
149
153
|
try {
|
|
150
154
|
this.pinoLogger = (0, import_pino.default)(
|
|
151
155
|
loggerConfig,
|
|
152
156
|
import_pino.default.transport({
|
|
153
157
|
target: "pino-roll",
|
|
154
158
|
options: {
|
|
155
|
-
file:
|
|
159
|
+
file: logFile,
|
|
156
160
|
frequency: "daily",
|
|
157
161
|
mkdir: true
|
|
158
162
|
}
|
|
@@ -691,7 +695,7 @@ async function saveProjectFile(params) {
|
|
|
691
695
|
workspaceId,
|
|
692
696
|
projectId: params.projectId
|
|
693
697
|
};
|
|
694
|
-
const relativePath = pathValue ? path3.posix.join(pathValue, filename) : path3.posix.join("/project/uploads", filename);
|
|
698
|
+
const relativePath = pathValue ? path3.posix.join((0, import_core38.resolveSandboxAgentsPathAlias)(pathValue), filename) : path3.posix.join("/project/uploads", filename);
|
|
695
699
|
const isBinary = isBinaryContentType(filename);
|
|
696
700
|
const volumeBackend = await sandboxManager.getVolumeBackendForPath(
|
|
697
701
|
volumeConfig,
|
|
@@ -9434,20 +9438,21 @@ function createOpenSandboxProvider() {
|
|
|
9434
9438
|
switch (actionName) {
|
|
9435
9439
|
case "files_read": {
|
|
9436
9440
|
const { path: path7 } = args;
|
|
9437
|
-
const file = await sandbox.file.readFile(path7);
|
|
9441
|
+
const file = await sandbox.file.readFile((0, import_core120.resolveSandboxAgentsPathAlias)(path7));
|
|
9438
9442
|
return { content: [{ type: "text", text: file.content }] };
|
|
9439
9443
|
}
|
|
9440
9444
|
case "files_list": {
|
|
9441
9445
|
const { path: path7, recursive } = args;
|
|
9442
|
-
const listing = await sandbox.file.listPath(path7, { recursive });
|
|
9446
|
+
const listing = await sandbox.file.listPath((0, import_core120.resolveSandboxAgentsPathAlias)(path7), { recursive });
|
|
9443
9447
|
return {
|
|
9444
9448
|
content: [{ type: "text", text: JSON.stringify(listing.files) }]
|
|
9445
9449
|
};
|
|
9446
9450
|
}
|
|
9447
9451
|
case "files_write": {
|
|
9448
9452
|
const { path: path7, content } = args;
|
|
9449
|
-
|
|
9450
|
-
|
|
9453
|
+
const resolvedPath = (0, import_core120.resolveSandboxAgentsPathAlias)(path7);
|
|
9454
|
+
await sandbox.file.writeFile(resolvedPath, content);
|
|
9455
|
+
return { content: [{ type: "text", text: `wrote ${resolvedPath}` }] };
|
|
9451
9456
|
}
|
|
9452
9457
|
case "shell_exec": {
|
|
9453
9458
|
const { command, exec_dir, timeout } = args;
|
|
@@ -9502,7 +9507,8 @@ function extractBearer2(request) {
|
|
|
9502
9507
|
function safePath(raw) {
|
|
9503
9508
|
if (typeof raw !== "string" || raw.length === 0) return null;
|
|
9504
9509
|
if (raw.includes("..")) return null;
|
|
9505
|
-
|
|
9510
|
+
const aliased = (0, import_core121.resolveSandboxAgentsPathAlias)(raw);
|
|
9511
|
+
return aliased.startsWith("/") ? aliased : `/${aliased}`;
|
|
9506
9512
|
}
|
|
9507
9513
|
function registerOpenFilesRoutes(app2, deps = {}) {
|
|
9508
9514
|
const openAuth = deps.openAuth ?? new OpenCredentialService(
|
|
@@ -21275,7 +21281,8 @@ function registerSandboxProxyRoutes(app2) {
|
|
|
21275
21281
|
vmIsolation
|
|
21276
21282
|
});
|
|
21277
21283
|
try {
|
|
21278
|
-
const
|
|
21284
|
+
const aliasedPath = (0, import_core37.resolveSandboxAgentsPathAlias)(filePath);
|
|
21285
|
+
const resolvedPath = aliasedPath.startsWith("/") ? aliasedPath : `/${aliasedPath}`;
|
|
21279
21286
|
const filename = getFilenameFromPath(resolvedPath);
|
|
21280
21287
|
const inferredContentType = getContentTypeFromFilename(filename);
|
|
21281
21288
|
try {
|
|
@@ -22035,7 +22042,10 @@ var WorkspaceController = class {
|
|
|
22035
22042
|
if (!workspace) {
|
|
22036
22043
|
throw new Error("Workspace not found");
|
|
22037
22044
|
}
|
|
22038
|
-
|
|
22045
|
+
const resolvedPath = filePath !== void 0 && workspace.storageType === "sandbox" ? (0, import_core43.resolveSandboxAgentsPathAlias)(filePath) : filePath;
|
|
22046
|
+
console.log(
|
|
22047
|
+
`[getBackend] storageType=${workspace.storageType} filePath=${filePath} resolvedPath=${resolvedPath}`
|
|
22048
|
+
);
|
|
22039
22049
|
if (workspace.storageType === "sandbox") {
|
|
22040
22050
|
const sandboxManager = (0, import_core43.getSandBoxManager)();
|
|
22041
22051
|
const volumeConfig = {
|
|
@@ -22045,11 +22055,11 @@ var WorkspaceController = class {
|
|
|
22045
22055
|
workspaceId,
|
|
22046
22056
|
projectId
|
|
22047
22057
|
};
|
|
22048
|
-
console.log(`[getBackend] trying volume backend for path=${
|
|
22049
|
-
const volumeBackend = await sandboxManager.getVolumeBackendForPath(volumeConfig,
|
|
22058
|
+
console.log(`[getBackend] trying volume backend for path=${resolvedPath} assistant_id=${assistantId}`);
|
|
22059
|
+
const volumeBackend = await sandboxManager.getVolumeBackendForPath(volumeConfig, resolvedPath || "/project");
|
|
22050
22060
|
if (volumeBackend) {
|
|
22051
22061
|
console.log(`[getBackend] using VolumeFilesystem`);
|
|
22052
|
-
return { backend: volumeBackend, workspace };
|
|
22062
|
+
return { backend: volumeBackend, workspace, resolvedPath };
|
|
22053
22063
|
}
|
|
22054
22064
|
console.log(`[getBackend] volume not found, falling back to SandboxFilesystem`);
|
|
22055
22065
|
const sandbox = await sandboxManager.getSandboxFromConfig(volumeConfig);
|
|
@@ -22058,7 +22068,8 @@ var WorkspaceController = class {
|
|
|
22058
22068
|
backend: new import_core42.SandboxFilesystem({
|
|
22059
22069
|
sandboxInstance: sandbox
|
|
22060
22070
|
}),
|
|
22061
|
-
workspace
|
|
22071
|
+
workspace,
|
|
22072
|
+
resolvedPath
|
|
22062
22073
|
};
|
|
22063
22074
|
}
|
|
22064
22075
|
const rootDir = filesystemRootDirectory(
|
|
@@ -22072,7 +22083,8 @@ var WorkspaceController = class {
|
|
|
22072
22083
|
rootDir,
|
|
22073
22084
|
virtualMode: true
|
|
22074
22085
|
}),
|
|
22075
|
-
workspace
|
|
22086
|
+
workspace,
|
|
22087
|
+
resolvedPath
|
|
22076
22088
|
};
|
|
22077
22089
|
}
|
|
22078
22090
|
async downloadFile(request, reply) {
|
|
@@ -22084,8 +22096,14 @@ var WorkspaceController = class {
|
|
|
22084
22096
|
return reply.status(400).send({ success: false, error: "Path is required" });
|
|
22085
22097
|
}
|
|
22086
22098
|
try {
|
|
22087
|
-
const { workspace } = await this.getBackend(
|
|
22088
|
-
|
|
22099
|
+
const { workspace, resolvedPath: aliasedPath } = await this.getBackend(
|
|
22100
|
+
tenantId,
|
|
22101
|
+
workspaceId,
|
|
22102
|
+
projectId,
|
|
22103
|
+
assistantId,
|
|
22104
|
+
filePath
|
|
22105
|
+
);
|
|
22106
|
+
const resolvedPath = aliasedPath ?? filePath;
|
|
22089
22107
|
if (workspace.storageType === "sandbox") {
|
|
22090
22108
|
const sandboxManager = (0, import_core43.getSandBoxManager)();
|
|
22091
22109
|
const volumeConfig = {
|
|
@@ -22147,6 +22165,7 @@ var WorkspaceController = class {
|
|
|
22147
22165
|
if (!project || project.workspaceId !== workspaceId) {
|
|
22148
22166
|
throw new FolderDownloadError(404, "PROJECT_NOT_FOUND", "Project not found");
|
|
22149
22167
|
}
|
|
22168
|
+
const resolvedFolderPath = storedWorkspace.storageType === "sandbox" ? (0, import_core43.resolveSandboxAgentsPathAlias)(folderPath) : folderPath;
|
|
22150
22169
|
if (storedWorkspace.storageType === "sandbox") {
|
|
22151
22170
|
const sandboxManager = (0, import_core43.getSandBoxManager)();
|
|
22152
22171
|
const volumeConfig = {
|
|
@@ -22162,7 +22181,7 @@ var WorkspaceController = class {
|
|
|
22162
22181
|
}
|
|
22163
22182
|
let packed;
|
|
22164
22183
|
try {
|
|
22165
|
-
packed = await sandbox.packDirectory(
|
|
22184
|
+
packed = await sandbox.packDirectory(resolvedFolderPath);
|
|
22166
22185
|
if (packed.size === void 0) {
|
|
22167
22186
|
throw new FolderDownloadError(502, "BACKEND_UNSUPPORTED", "Backend does not report packed archive size");
|
|
22168
22187
|
}
|
|
@@ -22186,7 +22205,7 @@ var WorkspaceController = class {
|
|
|
22186
22205
|
workspaceId,
|
|
22187
22206
|
projectId,
|
|
22188
22207
|
assistantId,
|
|
22189
|
-
|
|
22208
|
+
resolvedFolderPath
|
|
22190
22209
|
);
|
|
22191
22210
|
if (!backend.lsInfo) {
|
|
22192
22211
|
throw new FolderDownloadError(502, "BACKEND_UNSUPPORTED", "Backend does not support folder downloads");
|
|
@@ -22194,7 +22213,7 @@ var WorkspaceController = class {
|
|
|
22194
22213
|
let listEntries2;
|
|
22195
22214
|
if (workspace.storageType !== "sandbox") {
|
|
22196
22215
|
const root = filesystemRootDirectory(tenantId, workspaceId, projectId);
|
|
22197
|
-
const target = path4.resolve(root, `.${
|
|
22216
|
+
const target = path4.resolve(root, `.${resolvedFolderPath.startsWith("/") ? resolvedFolderPath : `/${resolvedFolderPath}`}`);
|
|
22198
22217
|
const contained = await resolvePathWithinRoot(root, target).catch((error) => {
|
|
22199
22218
|
if (error instanceof Error && error.code === "ENOENT") {
|
|
22200
22219
|
throw new FolderDownloadError(404, "FOLDER_NOT_FOUND", "Folder not found");
|
|
@@ -22213,7 +22232,7 @@ var WorkspaceController = class {
|
|
|
22213
22232
|
if (!stat3.isDirectory()) {
|
|
22214
22233
|
throw new FolderDownloadError(400, "NOT_A_FOLDER", "Path is not a folder");
|
|
22215
22234
|
}
|
|
22216
|
-
} else if (
|
|
22235
|
+
} else if (resolvedFolderPath) {
|
|
22217
22236
|
const sandboxManager = (0, import_core43.getSandBoxManager)();
|
|
22218
22237
|
const volumeConfig = {
|
|
22219
22238
|
assistant_id: assistantId || "",
|
|
@@ -22222,11 +22241,11 @@ var WorkspaceController = class {
|
|
|
22222
22241
|
workspaceId,
|
|
22223
22242
|
projectId
|
|
22224
22243
|
};
|
|
22225
|
-
const volume = await sandboxManager.getVolumeBackendForPath(volumeConfig,
|
|
22244
|
+
const volume = await sandboxManager.getVolumeBackendForPath(volumeConfig, resolvedFolderPath);
|
|
22226
22245
|
if (!volume) {
|
|
22227
22246
|
const sandbox = await sandboxManager.getSandboxFromConfig(volumeConfig);
|
|
22228
22247
|
try {
|
|
22229
|
-
await sandbox.file.listPath(
|
|
22248
|
+
await sandbox.file.listPath(resolvedFolderPath, { recursive: false });
|
|
22230
22249
|
} catch (error) {
|
|
22231
22250
|
const code = error instanceof Error ? error.code : void 0;
|
|
22232
22251
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -22246,7 +22265,7 @@ var WorkspaceController = class {
|
|
|
22246
22265
|
if (!listEntries2) {
|
|
22247
22266
|
listEntries2 = async (directoryPath) => backend.lsInfo(directoryPath);
|
|
22248
22267
|
}
|
|
22249
|
-
const folderName = path4.basename(
|
|
22268
|
+
const folderName = path4.basename(resolvedFolderPath.replace(/\/$/, "")) || "folder";
|
|
22250
22269
|
const prefix = `${folderName}/`;
|
|
22251
22270
|
const entries = [{ name: prefix, data: Buffer.alloc(0), directory: true }];
|
|
22252
22271
|
let totalSize = 0;
|
|
@@ -22358,7 +22377,7 @@ var WorkspaceController = class {
|
|
|
22358
22377
|
throw new FolderDownloadError(400, "ENTRY_LIMIT", "Folder contains too many entries");
|
|
22359
22378
|
}
|
|
22360
22379
|
const childPath = child.path;
|
|
22361
|
-
if (childPath.includes("\\") || childPath.split("/").includes("..") || !childPath.startsWith(
|
|
22380
|
+
if (childPath.includes("\\") || childPath.split("/").includes("..") || !childPath.startsWith(resolvedFolderPath.endsWith("/") ? resolvedFolderPath : `${resolvedFolderPath}/`)) {
|
|
22362
22381
|
throw new FolderDownloadError(400, "INVALID_PATH", "Folder contains an unsafe child path");
|
|
22363
22382
|
}
|
|
22364
22383
|
if (workspace.storageType !== "sandbox") {
|
|
@@ -22395,10 +22414,10 @@ var WorkspaceController = class {
|
|
|
22395
22414
|
}
|
|
22396
22415
|
};
|
|
22397
22416
|
if (workspace.storageType === "sandbox") {
|
|
22398
|
-
await collect(
|
|
22417
|
+
await collect(resolvedFolderPath, prefix);
|
|
22399
22418
|
} else {
|
|
22400
22419
|
const root = filesystemRoot;
|
|
22401
|
-
const target = path4.resolve(root, `.${
|
|
22420
|
+
const target = path4.resolve(root, `.${resolvedFolderPath.startsWith("/") ? resolvedFolderPath : `/${resolvedFolderPath}`}`);
|
|
22402
22421
|
await collectFilesystem(target, prefix);
|
|
22403
22422
|
}
|
|
22404
22423
|
const zip = createZip(entries);
|
|
@@ -22426,8 +22445,14 @@ var WorkspaceController = class {
|
|
|
22426
22445
|
return reply.status(400).send({ success: false, error: "Path is required" });
|
|
22427
22446
|
}
|
|
22428
22447
|
try {
|
|
22429
|
-
const { workspace } = await this.getBackend(
|
|
22430
|
-
|
|
22448
|
+
const { workspace, resolvedPath: aliasedPath } = await this.getBackend(
|
|
22449
|
+
tenantId,
|
|
22450
|
+
workspaceId,
|
|
22451
|
+
projectId,
|
|
22452
|
+
assistantId,
|
|
22453
|
+
filePath
|
|
22454
|
+
);
|
|
22455
|
+
const resolvedPath = aliasedPath ?? filePath;
|
|
22431
22456
|
if (workspace.storageType === "sandbox") {
|
|
22432
22457
|
const sandboxManager = (0, import_core43.getSandBoxManager)();
|
|
22433
22458
|
const volumeConfig = {
|
|
@@ -22525,9 +22550,10 @@ var WorkspaceController = class {
|
|
|
22525
22550
|
const path7 = request.query.path || "/";
|
|
22526
22551
|
const assistantId = request.query.assistantId;
|
|
22527
22552
|
console.log(`[listPath] tenantId=${tenantId} workspaceId=${workspaceId} projectId=${projectId} path=${path7} assistantId=${assistantId}`);
|
|
22528
|
-
const { backend } = await this.getBackend(tenantId, workspaceId, projectId, assistantId, path7);
|
|
22529
|
-
|
|
22530
|
-
|
|
22553
|
+
const { backend, resolvedPath } = await this.getBackend(tenantId, workspaceId, projectId, assistantId, path7);
|
|
22554
|
+
const targetPath = resolvedPath ?? path7;
|
|
22555
|
+
console.log(`[listPath] backend type=${backend.constructor.name} calling lsInfo(${targetPath})`);
|
|
22556
|
+
const files = await backend.lsInfo(targetPath);
|
|
22531
22557
|
console.log(`[listPath] result count=${files.length}`);
|
|
22532
22558
|
return { success: true, data: files };
|
|
22533
22559
|
}
|
|
@@ -22535,8 +22561,8 @@ var WorkspaceController = class {
|
|
|
22535
22561
|
const tenantId = this.getTenantId(request);
|
|
22536
22562
|
const { workspaceId, projectId } = request.params;
|
|
22537
22563
|
const { path: path7, offset = 0, limit = 1e3, assistantId } = request.query;
|
|
22538
|
-
const { backend } = await this.getBackend(tenantId, workspaceId, projectId, assistantId, path7);
|
|
22539
|
-
const content = await backend.read(path7, Number(offset), Number(limit));
|
|
22564
|
+
const { backend, resolvedPath } = await this.getBackend(tenantId, workspaceId, projectId, assistantId, path7);
|
|
22565
|
+
const content = await backend.read(resolvedPath ?? path7, Number(offset), Number(limit));
|
|
22540
22566
|
return { success: true, data: { content, offset, limit } };
|
|
22541
22567
|
}
|
|
22542
22568
|
/**
|