@rethinkingstudio/clawpilot 1.1.15-beta.0 → 1.1.15-internal.0
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/commands/pair.js +1 -1
- package/dist/commands/pair.js.map +1 -1
- package/dist/config/config.d.ts +2 -2
- package/dist/config/config.js +48 -12
- package/dist/config/config.js.map +1 -1
- package/dist/generated/build-config.d.ts +1 -0
- package/dist/generated/build-config.js +2 -0
- package/dist/generated/build-config.js.map +1 -0
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/media/assistant-attachments.d.ts +35 -0
- package/dist/media/assistant-attachments.js +144 -0
- package/dist/media/assistant-attachments.js.map +1 -0
- package/dist/media/oss-uploader.d.ts +22 -0
- package/dist/media/oss-uploader.js +180 -0
- package/dist/media/oss-uploader.js.map +1 -0
- package/dist/relay/relay-manager.js +18 -4
- package/dist/relay/relay-manager.js.map +1 -1
- package/package.json +5 -2
- package/scripts/write-build-config.mjs +17 -0
- package/src/commands/pair.ts +1 -2
- package/src/config/config.ts +70 -13
- package/src/generated/build-config.ts +1 -0
- package/src/index.ts +2 -1
- package/src/media/assistant-attachments.ts +205 -0
- package/src/media/oss-uploader.ts +280 -0
- package/src/relay/relay-manager.ts +27 -5
|
@@ -2,6 +2,8 @@ import { WebSocket } from "ws";
|
|
|
2
2
|
import { OpenClawGatewayClient } from "./gateway-client.js";
|
|
3
3
|
import { handleLocalCommand } from "../commands/local-handlers.js";
|
|
4
4
|
import { handleProviderCommand } from "../commands/provider-handlers.js";
|
|
5
|
+
import { getServicePlatform } from "../platform/service-manager.js";
|
|
6
|
+
import { uploadAssistantAttachments } from "../media/assistant-attachments.js";
|
|
5
7
|
import { homedir } from "os";
|
|
6
8
|
import { join } from "path";
|
|
7
9
|
import { mkdir, writeFile } from "fs/promises";
|
|
@@ -41,6 +43,7 @@ export async function runRelayManager(opts) {
|
|
|
41
43
|
}
|
|
42
44
|
relayWs.on("open", () => {
|
|
43
45
|
console.log(`Connected to relay server (gatewayId=${opts.gatewayId})`);
|
|
46
|
+
send({ type: "relay_hello", platform: getServicePlatform() });
|
|
44
47
|
opts.onConnected?.();
|
|
45
48
|
// Start the persistent gateway connection as soon as we're connected
|
|
46
49
|
// to the relay server. Its lifetime is tied to this relay session.
|
|
@@ -57,9 +60,8 @@ export async function runRelayManager(opts) {
|
|
|
57
60
|
send({ type: "gateway_disconnected", reason });
|
|
58
61
|
},
|
|
59
62
|
onEvent: (event, payload) => {
|
|
60
|
-
// On chat final, fetch history to get actual content
|
|
61
|
-
// no longer includes message content in
|
|
62
|
-
// This mirrors what the macOS 2026.3.2 client does.
|
|
63
|
+
// On chat final, fetch history to get actual content + extract media attachments.
|
|
64
|
+
// OpenClaw 2026.3.2+ no longer includes message content in chat final payload.
|
|
63
65
|
if (event === "chat") {
|
|
64
66
|
const p = payload;
|
|
65
67
|
if (p?.state === "final" && p?.sessionKey) {
|
|
@@ -79,10 +81,22 @@ export async function runRelayManager(opts) {
|
|
|
79
81
|
await new Promise((resolve) => setTimeout(resolve, 600));
|
|
80
82
|
const retryHistory = await fetchHistory();
|
|
81
83
|
text = extractText(retryHistory);
|
|
84
|
+
history = retryHistory;
|
|
82
85
|
}
|
|
83
86
|
if (text) {
|
|
84
87
|
p.message = { content: [{ type: "text", text }] };
|
|
85
88
|
}
|
|
89
|
+
// Upload any media blocks found in the history
|
|
90
|
+
try {
|
|
91
|
+
const attachments = await uploadAssistantAttachments(history ?? {}, opts.relayServerUrl, opts.gatewayId, opts.relaySecret);
|
|
92
|
+
if (attachments.length > 0) {
|
|
93
|
+
p.attachments = attachments;
|
|
94
|
+
console.log(`[relay] chat final: injected ${attachments.length} attachment(s) runId=${runId}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch (mediaErr) {
|
|
98
|
+
console.error(`[relay] media upload error (non-fatal): ${mediaErr}`);
|
|
99
|
+
}
|
|
86
100
|
console.log(`[relay] chat final (history fetched): runId=${runId} textLength=${text?.length ?? 0}`);
|
|
87
101
|
send({ type: "event", event, payload });
|
|
88
102
|
})
|
|
@@ -90,7 +104,7 @@ export async function runRelayManager(opts) {
|
|
|
90
104
|
console.error(`[relay] chat.history fetch failed: ${err}`);
|
|
91
105
|
send({ type: "event", event, payload });
|
|
92
106
|
});
|
|
93
|
-
return; // will send after history fetch
|
|
107
|
+
return; // will send after history fetch + media upload
|
|
94
108
|
}
|
|
95
109
|
}
|
|
96
110
|
send({ type: "event", event, payload });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relay-manager.js","sourceRoot":"","sources":["../../src/relay/relay-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAC/B,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEpC,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"relay-manager.js","sourceRoot":"","sources":["../../src/relay/relay-manager.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rethinkingstudio/clawpilot",
|
|
3
|
-
"version": "1.1.15-
|
|
3
|
+
"version": "1.1.15-internal.0",
|
|
4
4
|
"description": "ClawAI relay client for Mac mini",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"clawpilot": "dist/index.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"build": "
|
|
10
|
+
"build:config": "node ./scripts/write-build-config.mjs",
|
|
11
|
+
"build": "npm run build:config && tsc",
|
|
12
|
+
"build:prod": "npm run build",
|
|
13
|
+
"build:internal": "CLAWPILOT_DEFAULT_RELAY_SERVER=http://clawpilot-pre.codeaddict.cn npm run build",
|
|
11
14
|
"dev": "tsx src/index.ts",
|
|
12
15
|
"start": "node dist/index.js"
|
|
13
16
|
},
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync } from "fs";
|
|
2
|
+
import { dirname, resolve } from "path";
|
|
3
|
+
|
|
4
|
+
const target = resolve(process.cwd(), "src/generated/build-config.ts");
|
|
5
|
+
const relayServer =
|
|
6
|
+
process.env.CLAWPILOT_DEFAULT_RELAY_SERVER?.trim()
|
|
7
|
+
|| "https://clawpilot.codeaddict.cn";
|
|
8
|
+
|
|
9
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
10
|
+
|
|
11
|
+
writeFileSync(
|
|
12
|
+
target,
|
|
13
|
+
`export const DEFAULT_RELAY_SERVER = ${JSON.stringify(relayServer)};\n`,
|
|
14
|
+
"utf8"
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
console.log(`[build-config] DEFAULT_RELAY_SERVER=${relayServer}`);
|
package/src/commands/pair.ts
CHANGED
|
@@ -26,8 +26,7 @@ import { configExists, readConfig, writeConfig } from "../config/config.js";
|
|
|
26
26
|
import { installCommand } from "./install.js";
|
|
27
27
|
import qrcodeTerminal from "qrcode-terminal";
|
|
28
28
|
import { t } from "../i18n/index.js";
|
|
29
|
-
|
|
30
|
-
const DEFAULT_RELAY_SERVER = "https://clawpilot.codeaddict.cn";
|
|
29
|
+
import { DEFAULT_RELAY_SERVER } from "../generated/build-config.js";
|
|
31
30
|
|
|
32
31
|
interface PairOptions {
|
|
33
32
|
server?: string;
|
package/src/config/config.ts
CHANGED
|
@@ -48,33 +48,90 @@ export function readGatewayUrl(): string {
|
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
function trimToUndefined(value: unknown): string | undefined {
|
|
52
|
+
if (typeof value !== "string") return undefined;
|
|
53
|
+
const trimmed = value.trim();
|
|
54
|
+
return trimmed ? trimmed : undefined;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function readGatewayAuthEnv(): { token?: string; password?: string } {
|
|
58
|
+
const token =
|
|
59
|
+
trimToUndefined(process.env.OPENCLAW_GATEWAY_TOKEN)
|
|
60
|
+
?? trimToUndefined(process.env.CLAWDBOT_GATEWAY_TOKEN);
|
|
61
|
+
const password =
|
|
62
|
+
trimToUndefined(process.env.OPENCLAW_GATEWAY_PASSWORD)
|
|
63
|
+
?? trimToUndefined(process.env.CLAWDBOT_GATEWAY_PASSWORD);
|
|
64
|
+
return { token, password };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function resolveConfiguredSecret(value: unknown): string | undefined {
|
|
68
|
+
const direct = trimToUndefined(value);
|
|
69
|
+
if (direct) {
|
|
70
|
+
const envTemplate = direct.match(/^\$\{([A-Z0-9_]+)\}$/i);
|
|
71
|
+
if (envTemplate) {
|
|
72
|
+
return trimToUndefined(process.env[envTemplate[1]]);
|
|
73
|
+
}
|
|
74
|
+
return direct;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const record = value as Record<string, unknown>;
|
|
82
|
+
const envKey = trimToUndefined(record.$env) ?? trimToUndefined(record.env);
|
|
83
|
+
if (envKey) {
|
|
84
|
+
return trimToUndefined(process.env[envKey]);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
|
|
51
90
|
/**
|
|
52
91
|
* Reads the gateway token or password. Priority order:
|
|
53
92
|
* 1. ~/.clawai/config.json (gatewayToken / gatewayPassword)
|
|
54
|
-
* 2.
|
|
55
|
-
* 3.
|
|
93
|
+
* 2. Environment variables (OPENCLAW_GATEWAY_TOKEN / OPENCLAW_GATEWAY_PASSWORD)
|
|
94
|
+
* 3. ~/.openclaw/openclaw.json (gateway.token / gateway.auth.token)
|
|
56
95
|
*/
|
|
57
96
|
export function readGatewayAuth(cfg: ClawaiConfig): { token?: string; password?: string } {
|
|
58
97
|
if (cfg.gatewayToken || cfg.gatewayPassword) {
|
|
59
|
-
return {
|
|
98
|
+
return {
|
|
99
|
+
token: trimToUndefined(cfg.gatewayToken),
|
|
100
|
+
password: trimToUndefined(cfg.gatewayPassword),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const envAuth = readGatewayAuthEnv();
|
|
105
|
+
if (envAuth.token || envAuth.password) {
|
|
106
|
+
return envAuth;
|
|
60
107
|
}
|
|
61
|
-
|
|
108
|
+
|
|
109
|
+
// Try to read the token from OpenClaw's own config.
|
|
110
|
+
// Accept plaintext, ${ENV_VAR} templates, and simple {$env:"NAME"} refs.
|
|
62
111
|
try {
|
|
63
112
|
const configPath = resolveOpenclawConfigPath();
|
|
64
113
|
const path = existsSync(configPath) ? configPath : LEGACY_OPENCLAW_CONFIG_PATH;
|
|
65
114
|
const raw = readFileSync(path, "utf-8");
|
|
66
|
-
const json = JSON.parse(raw) as {
|
|
67
|
-
|
|
68
|
-
|
|
115
|
+
const json = JSON.parse(raw) as {
|
|
116
|
+
gateway?: {
|
|
117
|
+
token?: unknown;
|
|
118
|
+
password?: unknown;
|
|
119
|
+
auth?: {
|
|
120
|
+
token?: unknown;
|
|
121
|
+
password?: unknown;
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
const token =
|
|
126
|
+
resolveConfiguredSecret(json?.gateway?.token)
|
|
127
|
+
?? resolveConfiguredSecret(json?.gateway?.auth?.token);
|
|
128
|
+
const password =
|
|
129
|
+
resolveConfiguredSecret(json?.gateway?.password)
|
|
130
|
+
?? resolveConfiguredSecret(json?.gateway?.auth?.password);
|
|
69
131
|
if (token || password) return { token, password };
|
|
70
132
|
} catch {
|
|
71
133
|
// ignore
|
|
72
134
|
}
|
|
73
|
-
|
|
74
|
-
const envToken = process.env.OPENCLAW_GATEWAY_TOKEN;
|
|
75
|
-
const envPassword = process.env.OPENCLAW_GATEWAY_PASSWORD;
|
|
76
|
-
if (envToken || envPassword) {
|
|
77
|
-
return { token: envToken, password: envPassword };
|
|
78
|
-
}
|
|
135
|
+
|
|
79
136
|
return {};
|
|
80
137
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const DEFAULT_RELAY_SERVER = "http://clawpilot-pre.codeaddict.cn";
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { runCommand } from "./commands/run.js";
|
|
|
6
6
|
import { installCommand, uninstallCommand, stopCommand, restartCommand, resetCommand } from "./commands/install.js";
|
|
7
7
|
import { statusCommand } from "./commands/status.js";
|
|
8
8
|
import { setTokenCommand } from "./commands/set-token.js";
|
|
9
|
+
import { DEFAULT_RELAY_SERVER } from "./generated/build-config.js";
|
|
9
10
|
|
|
10
11
|
const require = createRequire(import.meta.url);
|
|
11
12
|
const { version } = require("../package.json") as { version: string };
|
|
@@ -20,7 +21,7 @@ program
|
|
|
20
21
|
program
|
|
21
22
|
.command("pair")
|
|
22
23
|
.description("Register with relay server and display QR code for iOS pairing")
|
|
23
|
-
.option("-s, --server <url>", "Relay server URL",
|
|
24
|
+
.option("-s, --server <url>", "Relay server URL", DEFAULT_RELAY_SERVER)
|
|
24
25
|
.option("-n, --name <name>", "Display name for this host")
|
|
25
26
|
.option("--code-only", "Print only the access code and skip QR code output", false)
|
|
26
27
|
.action(async (opts: { server: string; name: string; codeOnly?: boolean }) => {
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Extract media (image / video) blocks from an OpenClaw gateway chat history
|
|
3
|
+
// response, and upload them to OSS via STS credentials from the relay server.
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
|
|
6
|
+
import { uploadMedia, type StsCredentials, type UploadResult } from "./oss-uploader.js";
|
|
7
|
+
import { readFile } from "fs/promises";
|
|
8
|
+
|
|
9
|
+
// Max video size accepted for upload (200 MB)
|
|
10
|
+
const MAX_VIDEO_BYTES = 200 * 1024 * 1024;
|
|
11
|
+
|
|
12
|
+
export interface MediaBlock {
|
|
13
|
+
mimeType: string;
|
|
14
|
+
/** Buffer content (decoded from base64 or read from local path) */
|
|
15
|
+
data: Buffer;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface AssistantAttachment extends UploadResult {
|
|
19
|
+
width?: number;
|
|
20
|
+
height?: number;
|
|
21
|
+
durationMs?: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Extract media blocks from the last assistant message in a history response
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
type ContentBlock = {
|
|
29
|
+
type: string;
|
|
30
|
+
text?: string;
|
|
31
|
+
source?: {
|
|
32
|
+
type?: string; // "base64" | "url" | "file" | null
|
|
33
|
+
media_type?: string; // "image/png" etc.
|
|
34
|
+
data?: string; // base64 payload
|
|
35
|
+
url?: string; // external URL (not handled in Phase 1)
|
|
36
|
+
path?: string; // local file path
|
|
37
|
+
};
|
|
38
|
+
// Some gateways embed media_type / data directly on the block
|
|
39
|
+
media_type?: string;
|
|
40
|
+
data?: string;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
type HistoryMessage = {
|
|
44
|
+
role: string;
|
|
45
|
+
content?: ContentBlock[] | string;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export function extractMediaBlocks(history: { messages?: HistoryMessage[] }): MediaBlock[] {
|
|
49
|
+
const msgs = history.messages ?? [];
|
|
50
|
+
const last = [...msgs].reverse().find((m) => m.role === "assistant");
|
|
51
|
+
if (!last || !last.content) return [];
|
|
52
|
+
|
|
53
|
+
const content: ContentBlock[] =
|
|
54
|
+
typeof last.content === "string" ? [] : last.content;
|
|
55
|
+
|
|
56
|
+
const blocks: MediaBlock[] = [];
|
|
57
|
+
|
|
58
|
+
for (const block of content) {
|
|
59
|
+
if (block.type !== "image" && block.type !== "video") continue;
|
|
60
|
+
|
|
61
|
+
const source = block.source;
|
|
62
|
+
const mimeType =
|
|
63
|
+
source?.media_type ?? block.media_type ?? "image/jpeg";
|
|
64
|
+
|
|
65
|
+
// Only handle base64 and local file paths in Phase 1
|
|
66
|
+
if (source?.type === "base64" || (!source?.type && source?.data)) {
|
|
67
|
+
const raw = source?.data ?? block.data ?? "";
|
|
68
|
+
if (!raw) continue;
|
|
69
|
+
try {
|
|
70
|
+
const buf = Buffer.from(raw.replace(/^data:[^;]+;base64,/, ""), "base64");
|
|
71
|
+
blocks.push({ mimeType, data: buf });
|
|
72
|
+
} catch {
|
|
73
|
+
console.warn("[media] failed to decode base64 block, skipping");
|
|
74
|
+
}
|
|
75
|
+
} else if (source?.type === "file" && source.path) {
|
|
76
|
+
// Local file reference — will be read asynchronously below
|
|
77
|
+
blocks.push({ mimeType, data: Buffer.alloc(0), _localPath: source.path } as MediaBlock & { _localPath: string });
|
|
78
|
+
}
|
|
79
|
+
// URL type is not handled in Phase 1
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return blocks;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
// Load local-path blocks and filter oversized videos
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
|
|
89
|
+
async function resolveLocalPaths(blocks: (MediaBlock & { _localPath?: string })[]): Promise<MediaBlock[]> {
|
|
90
|
+
const resolved: MediaBlock[] = [];
|
|
91
|
+
for (const b of blocks) {
|
|
92
|
+
if ((b as any)._localPath) {
|
|
93
|
+
try {
|
|
94
|
+
const buf = await readFile((b as any)._localPath as string);
|
|
95
|
+
resolved.push({ mimeType: b.mimeType, data: buf });
|
|
96
|
+
} catch (err) {
|
|
97
|
+
console.warn(`[media] failed to read local file ${(b as any)._localPath}: ${err}`);
|
|
98
|
+
}
|
|
99
|
+
} else {
|
|
100
|
+
resolved.push(b);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return resolved.filter((b) => {
|
|
104
|
+
if (b.mimeType.startsWith("video/") && b.data.length > MAX_VIDEO_BYTES) {
|
|
105
|
+
console.warn(`[media] video too large (${b.data.length} bytes), skipping`);
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
return b.data.length > 0;
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
// Fetch STS credentials from the relay server
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
|
|
116
|
+
async function fetchSts(
|
|
117
|
+
relayServerUrl: string,
|
|
118
|
+
gatewayId: string,
|
|
119
|
+
relaySecret: string,
|
|
120
|
+
mimeTypes: string[],
|
|
121
|
+
hasVideo: boolean
|
|
122
|
+
): Promise<StsCredentials> {
|
|
123
|
+
const url = `${relayServerUrl.replace(/\/$/, "")}/api/media/sts`;
|
|
124
|
+
const res = await fetch(url, {
|
|
125
|
+
method: "POST",
|
|
126
|
+
headers: { "Content-Type": "application/json" },
|
|
127
|
+
body: JSON.stringify({
|
|
128
|
+
gatewayId,
|
|
129
|
+
relaySecret,
|
|
130
|
+
count: mimeTypes.length,
|
|
131
|
+
mimeTypes,
|
|
132
|
+
durationSeconds: hasVideo ? 1800 : 600,
|
|
133
|
+
}),
|
|
134
|
+
});
|
|
135
|
+
if (!res.ok) {
|
|
136
|
+
const text = await res.text().catch(() => "");
|
|
137
|
+
throw new Error(`STS fetch failed: ${res.status} ${text}`);
|
|
138
|
+
}
|
|
139
|
+
return res.json() as Promise<StsCredentials>;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
// Main: extract + upload → return attachments[]
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
|
|
146
|
+
export async function uploadAssistantAttachments(
|
|
147
|
+
history: { messages?: HistoryMessage[] },
|
|
148
|
+
relayServerUrl: string,
|
|
149
|
+
gatewayId: string,
|
|
150
|
+
relaySecret: string
|
|
151
|
+
): Promise<AssistantAttachment[]> {
|
|
152
|
+
const rawBlocks = extractMediaBlocks(history);
|
|
153
|
+
if (rawBlocks.length === 0) return [];
|
|
154
|
+
|
|
155
|
+
const blocks = await resolveLocalPaths(rawBlocks as (MediaBlock & { _localPath?: string })[]);
|
|
156
|
+
if (blocks.length === 0) return [];
|
|
157
|
+
|
|
158
|
+
const mimeTypes = blocks.map((b) => b.mimeType);
|
|
159
|
+
const hasVideo = mimeTypes.some((m) => m.startsWith("video/"));
|
|
160
|
+
|
|
161
|
+
let sts: StsCredentials;
|
|
162
|
+
try {
|
|
163
|
+
sts = await fetchSts(relayServerUrl, gatewayId, relaySecret, mimeTypes, hasVideo);
|
|
164
|
+
} catch (err) {
|
|
165
|
+
console.error("[media] failed to fetch STS:", err);
|
|
166
|
+
return [];
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const results: AssistantAttachment[] = [];
|
|
170
|
+
|
|
171
|
+
// Upload images concurrently (max 3), videos serially
|
|
172
|
+
const imageBlocks = blocks.filter((b) => !b.mimeType.startsWith("video/"));
|
|
173
|
+
const videoBlocks = blocks.filter((b) => b.mimeType.startsWith("video/"));
|
|
174
|
+
|
|
175
|
+
// Images: up to 3 concurrent
|
|
176
|
+
const imageBatches: MediaBlock[][] = [];
|
|
177
|
+
for (let i = 0; i < imageBlocks.length; i += 3) {
|
|
178
|
+
imageBatches.push(imageBlocks.slice(i, i + 3));
|
|
179
|
+
}
|
|
180
|
+
for (const batch of imageBatches) {
|
|
181
|
+
const settled = await Promise.allSettled(
|
|
182
|
+
batch.map((b) => uploadMedia(sts, b.data, b.mimeType))
|
|
183
|
+
);
|
|
184
|
+
for (const r of settled) {
|
|
185
|
+
if (r.status === "fulfilled") {
|
|
186
|
+
results.push(r.value);
|
|
187
|
+
} else {
|
|
188
|
+
console.warn("[media] image upload failed:", r.reason);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Videos: serial
|
|
194
|
+
for (const b of videoBlocks) {
|
|
195
|
+
try {
|
|
196
|
+
const r = await uploadMedia(sts, b.data, b.mimeType);
|
|
197
|
+
results.push(r);
|
|
198
|
+
} catch (err) {
|
|
199
|
+
console.warn("[media] video upload failed:", err);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
console.log(`[media] uploaded ${results.length}/${blocks.length} attachments`);
|
|
204
|
+
return results;
|
|
205
|
+
}
|