@blade-hq/agent-client 2608.0.8 → 2608.0.9
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/README.md +122 -26
- package/dist/index.d.ts +6 -7
- package/dist/index.js +180 -141
- package/dist/index.js.map +1 -1
- package/dist/resources/auth.d.ts +26 -0
- package/dist/resources/models.d.ts +22 -0
- package/dist/resources/sessions.d.ts +5 -3
- package/dist/schemas/message-utils.d.ts +11 -1
- package/dist/schemas/session.d.ts +18 -1
- package/dist/session/agent-session.d.ts +21 -1
- package/dist/session/state.d.ts +14 -1
- package/dist/shared/projection/helpers.d.ts +8 -0
- package/package.json +1 -1
- package/public-api.md +74 -34
- package/dist/platform-endpoints.d.ts +0 -14
package/dist/index.js
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
function isAuthCallbackMessage(value) {
|
|
3
3
|
return typeof value === "object" && value !== null && value.type === "blade-agent:sdk-auth";
|
|
4
4
|
}
|
|
5
|
+
var AUTH_ERROR_HINTS = {
|
|
6
|
+
pat_unavailable: "\u6CA1\u80FD\u4E3A\u4F60\u51C6\u5907\u8BBF\u95EE\u51ED\u636E\u3002\u8BF7\u9000\u51FA\u540E\u91CD\u65B0\u767B\u5F55 Blade \u518D\u8BD5\u4E00\u6B21\uFF1B\u82E5\u4ECD\u7136\u5931\u8D25\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u3002"
|
|
7
|
+
};
|
|
5
8
|
function randomState() {
|
|
6
9
|
const bytes = new Uint8Array(24);
|
|
7
10
|
crypto.getRandomValues(bytes);
|
|
@@ -79,18 +82,22 @@ async function loginWithPopup(client, options = {}) {
|
|
|
79
82
|
if (event.data.state !== state) return;
|
|
80
83
|
const message = event.data;
|
|
81
84
|
stopCloseWatch();
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
85
|
+
const closePopup = () => {
|
|
86
|
+
try {
|
|
87
|
+
popup.close();
|
|
88
|
+
} catch {
|
|
89
|
+
}
|
|
90
|
+
};
|
|
86
91
|
if (message.status === "denied") {
|
|
92
|
+
closePopup();
|
|
87
93
|
settle(() => reject(new Error("\u4F60\u53D6\u6D88\u4E86\u6388\u6743\uFF0C\u672A\u5B8C\u6210\u767B\u5F55\u3002")));
|
|
88
94
|
return;
|
|
89
95
|
}
|
|
90
96
|
if (message.status !== "success" || !message.code) {
|
|
91
|
-
settle(() => reject(new Error(message.error
|
|
97
|
+
settle(() => reject(new Error(AUTH_ERROR_HINTS[message.error ?? ""] ?? message.error ?? "\u767B\u5F55\u5931\u8D25\uFF0C\u672A\u53D6\u5F97\u6388\u6743\u7801\u3002")));
|
|
92
98
|
return;
|
|
93
99
|
}
|
|
100
|
+
closePopup();
|
|
94
101
|
client.json("POST", "/api/auth/sdk/token", {
|
|
95
102
|
code: message.code,
|
|
96
103
|
state,
|
|
@@ -174,6 +181,24 @@ var AuthResource = class {
|
|
|
174
181
|
login(options) {
|
|
175
182
|
return this.client.login(options);
|
|
176
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* 用一次性 code 换 PAT。给**服务端**用:浏览器把 code 交给你自己的后端,
|
|
186
|
+
* 后端换到的 PAT 不进浏览器。浏览器里直接用 `login()` 就够了。
|
|
187
|
+
*
|
|
188
|
+
* 服务端调用时会自己补上 `Origin` 头(浏览器会自动带,服务端不会,缺了
|
|
189
|
+
* 后端报 `missing_request_origin`)。
|
|
190
|
+
*/
|
|
191
|
+
exchangeCode(params) {
|
|
192
|
+
return this.client.jsonFromInit("/api/auth/sdk/token", {
|
|
193
|
+
method: "POST",
|
|
194
|
+
headers: { Origin: params.clientOrigin },
|
|
195
|
+
body: JSON.stringify({
|
|
196
|
+
code: params.code,
|
|
197
|
+
state: params.state,
|
|
198
|
+
client_origin: params.clientOrigin
|
|
199
|
+
})
|
|
200
|
+
});
|
|
201
|
+
}
|
|
177
202
|
/** 清除 login() 存储的令牌。 */
|
|
178
203
|
logoutToken() {
|
|
179
204
|
this.client.logoutToken();
|
|
@@ -407,6 +432,47 @@ var ModelsResource = class {
|
|
|
407
432
|
}
|
|
408
433
|
};
|
|
409
434
|
|
|
435
|
+
// src/schemas/session.ts
|
|
436
|
+
import { type } from "arktype";
|
|
437
|
+
var SessionStatus = type(
|
|
438
|
+
"'created' | 'running' | 'completed' | 'failed' | 'interrupted' | 'waiting_for_input'"
|
|
439
|
+
);
|
|
440
|
+
var DEFAULT_REPLAY_SPEED = 5;
|
|
441
|
+
var SessionInfo = type({
|
|
442
|
+
id: "string",
|
|
443
|
+
intent: "string",
|
|
444
|
+
status: SessionStatus,
|
|
445
|
+
created_at: "string",
|
|
446
|
+
updated_at: "string",
|
|
447
|
+
"shared?": "boolean",
|
|
448
|
+
"memory_enabled?": "boolean",
|
|
449
|
+
"is_persistent?": "boolean",
|
|
450
|
+
"is_headless?": "boolean",
|
|
451
|
+
"viewer_role?": "'owner' | 'viewer'",
|
|
452
|
+
"template_id?": "string | null",
|
|
453
|
+
"model?": "string | null",
|
|
454
|
+
"enable_thinking?": "boolean | null",
|
|
455
|
+
"solution_id?": "string | null",
|
|
456
|
+
"solution_ref?": "unknown",
|
|
457
|
+
"biz_role_id?": "string | null",
|
|
458
|
+
"solution?": "unknown",
|
|
459
|
+
"plan_summary?": "string | null",
|
|
460
|
+
"primary_skill_id?": "string | null",
|
|
461
|
+
// ship-attack v2:后端 engine.set_bound_skill 写入的"绑定 skill id"。
|
|
462
|
+
// 未绑定 / 非 ship-attack session 为 null。
|
|
463
|
+
"bound_skill_id?": "string | null",
|
|
464
|
+
"replay_state?": "unknown",
|
|
465
|
+
"is_pinned?": "boolean",
|
|
466
|
+
"pinned_at?": "string | null",
|
|
467
|
+
"is_example?": "boolean",
|
|
468
|
+
"ports?": "unknown",
|
|
469
|
+
"disable_tools?": "string[]",
|
|
470
|
+
"runtime_type?": "string | null",
|
|
471
|
+
"daemon_id?": "string | null",
|
|
472
|
+
"workspace_path?": "string | null",
|
|
473
|
+
"match?": "unknown"
|
|
474
|
+
});
|
|
475
|
+
|
|
410
476
|
// src/session/definition.ts
|
|
411
477
|
var SessionSetupError = class extends Error {
|
|
412
478
|
constructor(message, stage, sessionId, options) {
|
|
@@ -827,6 +893,28 @@ function sourceLoopFor(loopId, loopDescriptions) {
|
|
|
827
893
|
if (!description) return null;
|
|
828
894
|
return { loop_name: loopId, description };
|
|
829
895
|
}
|
|
896
|
+
var MAX_FINAL_ARTIFACTS = 3;
|
|
897
|
+
function normalizeFinalArtifacts(raw) {
|
|
898
|
+
if (!Array.isArray(raw)) return [];
|
|
899
|
+
const artifacts = [];
|
|
900
|
+
const seen = /* @__PURE__ */ new Set();
|
|
901
|
+
for (const item of raw) {
|
|
902
|
+
if (typeof item !== "object" || item === null) continue;
|
|
903
|
+
const record = item;
|
|
904
|
+
const target = String(record.target ?? "").trim();
|
|
905
|
+
if (!target || seen.has(target)) continue;
|
|
906
|
+
const isLink = /^https?:\/\//i.test(target);
|
|
907
|
+
if (!isLink && target.split("/").includes("..")) continue;
|
|
908
|
+
seen.add(target);
|
|
909
|
+
artifacts.push({
|
|
910
|
+
kind: isLink ? "link" : "file",
|
|
911
|
+
target,
|
|
912
|
+
label: String(record.label ?? "").trim()
|
|
913
|
+
});
|
|
914
|
+
if (artifacts.length === MAX_FINAL_ARTIFACTS) break;
|
|
915
|
+
}
|
|
916
|
+
return artifacts;
|
|
917
|
+
}
|
|
830
918
|
function normalizePostChatFollowup(data, assistantEntryId) {
|
|
831
919
|
const raw = data.suggestions;
|
|
832
920
|
const suggestions = [];
|
|
@@ -838,11 +926,13 @@ function normalizePostChatFollowup(data, assistantEntryId) {
|
|
|
838
926
|
}
|
|
839
927
|
}
|
|
840
928
|
const recaption = String(data.recaption ?? "");
|
|
841
|
-
|
|
929
|
+
const finalArtifacts = normalizeFinalArtifacts(data.final_artifacts);
|
|
930
|
+
if (suggestions.length === 0 && !recaption && finalArtifacts.length === 0) return null;
|
|
842
931
|
return {
|
|
843
932
|
assistant_entry_id: assistantEntryId,
|
|
844
933
|
recaption,
|
|
845
|
-
suggestions: suggestions.slice(0, 3)
|
|
934
|
+
suggestions: suggestions.slice(0, 3),
|
|
935
|
+
final_artifacts: finalArtifacts
|
|
846
936
|
};
|
|
847
937
|
}
|
|
848
938
|
function normalizeChildPause(pausePayload) {
|
|
@@ -2130,12 +2220,16 @@ var SessionsResource = class {
|
|
|
2130
2220
|
pinSession(sessionId, pinned) {
|
|
2131
2221
|
return this.client.json("PATCH", `/api/sessions/${sessionId}/pin`, { pinned });
|
|
2132
2222
|
}
|
|
2133
|
-
startReplaySession(sourceSessionId, speed =
|
|
2223
|
+
startReplaySession(sourceSessionId, speed = DEFAULT_REPLAY_SPEED) {
|
|
2134
2224
|
return this.client.json("POST", `/api/sessions/${sourceSessionId}/replay`, { speed });
|
|
2135
2225
|
}
|
|
2136
2226
|
updateReplaySession(sessionId, payload) {
|
|
2137
2227
|
return this.client.json("PATCH", `/api/sessions/${sessionId}/replay`, payload);
|
|
2138
2228
|
}
|
|
2229
|
+
/** 查询会话能否作为回放来源;不支持时 reason 可直接展示,用来提前禁用入口。 */
|
|
2230
|
+
getReplayPreview(sessionId) {
|
|
2231
|
+
return this.client.json("GET", `/api/sessions/${sessionId}/replay/preview`);
|
|
2232
|
+
}
|
|
2139
2233
|
updateSharing(sessionId, shared) {
|
|
2140
2234
|
return this.client.json("PATCH", `/api/sessions/${sessionId}/sharing`, { shared });
|
|
2141
2235
|
}
|
|
@@ -2589,10 +2683,15 @@ function contentPreview(content, maxLen = 80) {
|
|
|
2589
2683
|
const text = getTextContent(content).trim();
|
|
2590
2684
|
return text.length > maxLen ? `${text.slice(0, maxLen)}\u2026` : text;
|
|
2591
2685
|
}
|
|
2592
|
-
function transformSlashCommand(skillName, rawInput) {
|
|
2686
|
+
function transformSlashCommand(skillName, rawInput, { local = true, installed = true } = {}) {
|
|
2593
2687
|
const prompt = rawInput.replace(`<skill>${skillName}</skill>`, "").trim();
|
|
2594
|
-
|
|
2688
|
+
if (local) {
|
|
2689
|
+
return prompt ? `\u8BF7\u4F7F\u7528 ${skillName} skill \u5B8C\u6210\u4EFB\u52A1
|
|
2595
2690
|
${prompt}` : `\u8BF7\u4F7F\u7528 ${skillName} skill \u5B8C\u6210\u4EFB\u52A1`;
|
|
2691
|
+
}
|
|
2692
|
+
const steps = installed ? `\u8BF7\u5148\u8FD0\u884C blade hub skill use ${skillName} \u52A0\u8F7D\u6280\u80FD` : `\u8BE5\u6280\u80FD\u5C1A\u672A\u5B89\u88C5\u3002\u8BF7\u5148\u8FD0\u884C blade hub skill install ${skillName} \u5B89\u88C5\uFF0C\u518D\u8FD0\u884C blade hub skill use ${skillName} \u52A0\u8F7D`;
|
|
2693
|
+
return prompt ? `${steps}\uFF0C\u7136\u540E\u5B8C\u6210\u4EFB\u52A1\uFF1A
|
|
2694
|
+
${prompt}` : `${steps}\uFF0C\u7136\u540E\u4F7F\u7528\u5B83\u5B8C\u6210\u4EFB\u52A1`;
|
|
2596
2695
|
}
|
|
2597
2696
|
var ATTACHMENT_TAG_RE = /\[附件:\s*([^\]]+)\](?:[ \t]*已上传到工作区路径:[ \t]*([^\r\n]+))?/g;
|
|
2598
2697
|
var CONTEXT_PART_RE = /^\[上下文:\s*([^\]]+)\]\n([\s\S]*)$/;
|
|
@@ -2678,6 +2777,14 @@ function groupMessagesByLoop(messages) {
|
|
|
2678
2777
|
}
|
|
2679
2778
|
|
|
2680
2779
|
// src/session/state.ts
|
|
2780
|
+
function toReplaySnapshot(raw) {
|
|
2781
|
+
if (!raw || !raw.status) return null;
|
|
2782
|
+
return {
|
|
2783
|
+
isReplay: raw.status === "replay",
|
|
2784
|
+
speed: raw.speed ?? 1,
|
|
2785
|
+
sourceSessionId: raw.source_session_id ?? null
|
|
2786
|
+
};
|
|
2787
|
+
}
|
|
2681
2788
|
function createInitialSessionState(sessionId) {
|
|
2682
2789
|
return {
|
|
2683
2790
|
sessionId,
|
|
@@ -2690,7 +2797,9 @@ function createInitialSessionState(sessionId) {
|
|
|
2690
2797
|
errorMessage: null,
|
|
2691
2798
|
askAnswers: {},
|
|
2692
2799
|
agentLoops: {},
|
|
2693
|
-
activeCompaction: null
|
|
2800
|
+
activeCompaction: null,
|
|
2801
|
+
replay: null,
|
|
2802
|
+
viewerRole: null
|
|
2694
2803
|
};
|
|
2695
2804
|
}
|
|
2696
2805
|
function parseAgentDescription(argumentsJson) {
|
|
@@ -3141,6 +3250,11 @@ var AgentSession = class _AgentSession {
|
|
|
3141
3250
|
patchFlushCancel = null;
|
|
3142
3251
|
joined = false;
|
|
3143
3252
|
joinPending = null;
|
|
3253
|
+
// 回放控制请求排成一条队列。连着点"5x"再点"退出回放"时,并发发出去的两个
|
|
3254
|
+
// 请求响应可能乱序回来,晚到的旧值会把已经退出的会话又显示成回放中;用
|
|
3255
|
+
// 序号丢弃过期响应又会在失败时留下中间态。串行执行就没有这些情况:每个
|
|
3256
|
+
// 响应都是当时最新的,失败也只影响自己。
|
|
3257
|
+
replayQueue = Promise.resolve();
|
|
3144
3258
|
pendingReplayMessage = null;
|
|
3145
3259
|
pendingReplayMode;
|
|
3146
3260
|
recentChatEndAt = 0;
|
|
@@ -3171,6 +3285,10 @@ var AgentSession = class _AgentSession {
|
|
|
3171
3285
|
emitWithAck: async () => readonlyError(),
|
|
3172
3286
|
ensureConnected: readonlyError,
|
|
3173
3287
|
fetchTurns: async () => turns,
|
|
3288
|
+
// 静态历史没有回放身份可言,给个空详情即可,不要抛错——_bootstrap
|
|
3289
|
+
// 不会跑到这里,但保持只读语义一致。
|
|
3290
|
+
fetchSessionInfo: async () => ({ replay_state: null }),
|
|
3291
|
+
updateReplay: async () => readonlyError(),
|
|
3174
3292
|
listDir: async () => readonlyError(),
|
|
3175
3293
|
countFiles: async () => readonlyError(),
|
|
3176
3294
|
downloadFile: async () => readonlyError(),
|
|
@@ -3462,10 +3580,13 @@ var AgentSession = class _AgentSession {
|
|
|
3462
3580
|
this.joinPending = null;
|
|
3463
3581
|
try {
|
|
3464
3582
|
await this.ensureJoined();
|
|
3465
|
-
await
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3583
|
+
await Promise.all([
|
|
3584
|
+
this.syncTurnsFromHistory({
|
|
3585
|
+
clearOnEmpty: true,
|
|
3586
|
+
preserveChangesSince: turnsBeforeRejoin
|
|
3587
|
+
}),
|
|
3588
|
+
this.syncReplayFromSession()
|
|
3589
|
+
]);
|
|
3469
3590
|
} catch {
|
|
3470
3591
|
}
|
|
3471
3592
|
}
|
|
@@ -3473,6 +3594,40 @@ var AgentSession = class _AgentSession {
|
|
|
3473
3594
|
async _bootstrap() {
|
|
3474
3595
|
await this.syncTurnsFromHistory({ suppressErrors: false });
|
|
3475
3596
|
await this.ensureJoined();
|
|
3597
|
+
await this.syncReplayFromSession();
|
|
3598
|
+
}
|
|
3599
|
+
/** 改回放是 owner 专属(服务端 PATCH 走 _get_owned_session)。 */
|
|
3600
|
+
get canControlReplay() {
|
|
3601
|
+
return this.state.viewerRole !== "viewer";
|
|
3602
|
+
}
|
|
3603
|
+
/** 拉一次会话详情,填回放状态和只读身份;失败按非回放会话处理,不挡启动。 */
|
|
3604
|
+
async syncReplayFromSession() {
|
|
3605
|
+
try {
|
|
3606
|
+
const info = await this.runtime.fetchSessionInfo(this.sessionId);
|
|
3607
|
+
const replay = toReplaySnapshot(info.replay_state);
|
|
3608
|
+
const viewerRole = info.viewer_role ?? null;
|
|
3609
|
+
this.update((s) => ({ ...s, replay, viewerRole }));
|
|
3610
|
+
} catch {
|
|
3611
|
+
}
|
|
3612
|
+
}
|
|
3613
|
+
/** 调整回放倍速;不是回放会话、或只读身份时什么都不做。 */
|
|
3614
|
+
async setReplaySpeed(speed) {
|
|
3615
|
+
if (!this.state.replay?.isReplay || !this.canControlReplay) return;
|
|
3616
|
+
await this.applyReplayControl({ speed });
|
|
3617
|
+
}
|
|
3618
|
+
/** 退出回放,之后的对话交给真实模型运行;不是回放会话、或只读身份时什么都不做。 */
|
|
3619
|
+
async exitReplay() {
|
|
3620
|
+
if (!this.state.replay?.isReplay || !this.canControlReplay) return;
|
|
3621
|
+
await this.applyReplayControl({ status: "autonomous" });
|
|
3622
|
+
}
|
|
3623
|
+
/** 把一次回放控制请求排进队列;前一个完成后才发出,响应直接安装。 */
|
|
3624
|
+
applyReplayControl(payload) {
|
|
3625
|
+
const run = this.replayQueue.then(async () => {
|
|
3626
|
+
const result = await this.runtime.updateReplay(this.sessionId, payload);
|
|
3627
|
+
this.update((s) => ({ ...s, replay: toReplaySnapshot(result) }));
|
|
3628
|
+
});
|
|
3629
|
+
this.replayQueue = run.catch(() => void 0);
|
|
3630
|
+
return run;
|
|
3476
3631
|
}
|
|
3477
3632
|
/** @internal */
|
|
3478
3633
|
_handleTurnStart(turn) {
|
|
@@ -3552,6 +3707,10 @@ var AgentSession = class _AgentSession {
|
|
|
3552
3707
|
if (data.status) {
|
|
3553
3708
|
this.update((s) => ({ ...s, status: data.status ?? s.status }));
|
|
3554
3709
|
}
|
|
3710
|
+
if (data.replay_state !== void 0) {
|
|
3711
|
+
const replay = toReplaySnapshot(data.replay_state);
|
|
3712
|
+
this.update((s) => ({ ...s, replay }));
|
|
3713
|
+
}
|
|
3555
3714
|
this.emitter.emit("sessionUpdated", {
|
|
3556
3715
|
intent: data.intent,
|
|
3557
3716
|
status: data.status,
|
|
@@ -4001,6 +4160,8 @@ var SessionHub = class {
|
|
|
4001
4160
|
}),
|
|
4002
4161
|
ensureConnected: () => this.ensureConnected(),
|
|
4003
4162
|
fetchTurns: (sessionId) => this.client.sessions.getSessionTurns(sessionId),
|
|
4163
|
+
fetchSessionInfo: (sessionId) => this.client.sessions.getSession(sessionId),
|
|
4164
|
+
updateReplay: (sessionId, payload) => this.client.sessions.updateReplaySession(sessionId, payload).then((result) => result.replay_state ?? null),
|
|
4004
4165
|
listDir: (sessionId, dirPath) => this.client.sessions.listDir(sessionId, dirPath),
|
|
4005
4166
|
countFiles: (sessionId, dirPath) => this.client.sessions.countFiles(sessionId, dirPath),
|
|
4006
4167
|
downloadFile: (sessionId, filePath, downloadName) => this.client.sessions.downloadFile(sessionId, filePath, downloadName),
|
|
@@ -4222,7 +4383,7 @@ function resolveAuthToken(options) {
|
|
|
4222
4383
|
|
|
4223
4384
|
// src/version.ts
|
|
4224
4385
|
var SDK_NAME = "agent-client";
|
|
4225
|
-
var SDK_VERSION = true ? "2608.0.
|
|
4386
|
+
var SDK_VERSION = true ? "2608.0.9" : "1.1.1";
|
|
4226
4387
|
|
|
4227
4388
|
// src/socket.ts
|
|
4228
4389
|
function withSdkIdentity(auth) {
|
|
@@ -4416,7 +4577,7 @@ var BladeClient = class {
|
|
|
4416
4577
|
const response = await (this.options.fetchImpl ?? fetch)(url.toString(), {
|
|
4417
4578
|
method,
|
|
4418
4579
|
body: init.body,
|
|
4419
|
-
credentials: init.credentials ?? "
|
|
4580
|
+
credentials: init.credentials ?? "same-origin",
|
|
4420
4581
|
headers: this.buildHeaders(url, init.headers),
|
|
4421
4582
|
signal: init.signal
|
|
4422
4583
|
});
|
|
@@ -4615,87 +4776,6 @@ function parseXhrHeaders(rawHeaders) {
|
|
|
4615
4776
|
return headers;
|
|
4616
4777
|
}
|
|
4617
4778
|
|
|
4618
|
-
// src/platform-endpoints.ts
|
|
4619
|
-
var PLATFORM_SERVICE_NAMES = [
|
|
4620
|
-
"os",
|
|
4621
|
-
"hub",
|
|
4622
|
-
"agent",
|
|
4623
|
-
"oauth",
|
|
4624
|
-
"llmGateway",
|
|
4625
|
-
"gitea"
|
|
4626
|
-
];
|
|
4627
|
-
var EMPTY_PLATFORM_ENDPOINTS = Object.freeze({
|
|
4628
|
-
services: Object.freeze({})
|
|
4629
|
-
});
|
|
4630
|
-
function defaultDocumentBaseUrl() {
|
|
4631
|
-
if (typeof document === "undefined") return void 0;
|
|
4632
|
-
if (document.querySelector("base[href]")) return document.baseURI;
|
|
4633
|
-
if (typeof location !== "undefined" && location.origin) return `${location.origin}/`;
|
|
4634
|
-
return document.baseURI;
|
|
4635
|
-
}
|
|
4636
|
-
function isRecord6(value) {
|
|
4637
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4638
|
-
}
|
|
4639
|
-
function normalizeServiceUrl(value) {
|
|
4640
|
-
if (typeof value !== "string" || !value.trim()) return null;
|
|
4641
|
-
if (value.includes("?") || value.includes("#")) return null;
|
|
4642
|
-
try {
|
|
4643
|
-
const url = new URL(value.trim());
|
|
4644
|
-
if (url.protocol !== "http:" && url.protocol !== "https:") return null;
|
|
4645
|
-
if (url.username || url.password || url.search || url.hash) return null;
|
|
4646
|
-
return url.toString().replace(/\/$/, "");
|
|
4647
|
-
} catch {
|
|
4648
|
-
return null;
|
|
4649
|
-
}
|
|
4650
|
-
}
|
|
4651
|
-
function parsePlatformEndpoints(value) {
|
|
4652
|
-
if (!isRecord6(value) || !isRecord6(value.services)) return null;
|
|
4653
|
-
const services = {};
|
|
4654
|
-
for (const name of PLATFORM_SERVICE_NAMES) {
|
|
4655
|
-
if (!(name in value.services)) continue;
|
|
4656
|
-
const url = normalizeServiceUrl(value.services[name]);
|
|
4657
|
-
if (!url) return null;
|
|
4658
|
-
services[name] = url;
|
|
4659
|
-
}
|
|
4660
|
-
return { services };
|
|
4661
|
-
}
|
|
4662
|
-
async function loadPlatformEndpoints(options = {}) {
|
|
4663
|
-
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
4664
|
-
const baseUrl = options.baseUrl ?? defaultDocumentBaseUrl();
|
|
4665
|
-
if (!fetchImpl || !baseUrl) return EMPTY_PLATFORM_ENDPOINTS;
|
|
4666
|
-
const controller = new AbortController();
|
|
4667
|
-
const timeout = globalThis.setTimeout(() => controller.abort(), options.timeoutMs ?? 3e3);
|
|
4668
|
-
try {
|
|
4669
|
-
const response = await fetchImpl(new URL("config.json", baseUrl), {
|
|
4670
|
-
cache: "no-store",
|
|
4671
|
-
credentials: "same-origin",
|
|
4672
|
-
headers: { Accept: "application/json" },
|
|
4673
|
-
signal: controller.signal
|
|
4674
|
-
});
|
|
4675
|
-
if (!response.ok) return EMPTY_PLATFORM_ENDPOINTS;
|
|
4676
|
-
return parsePlatformEndpoints(await response.json()) ?? EMPTY_PLATFORM_ENDPOINTS;
|
|
4677
|
-
} catch {
|
|
4678
|
-
return EMPTY_PLATFORM_ENDPOINTS;
|
|
4679
|
-
} finally {
|
|
4680
|
-
globalThis.clearTimeout(timeout);
|
|
4681
|
-
}
|
|
4682
|
-
}
|
|
4683
|
-
function resolveServiceUrl(endpoints, name, path = "") {
|
|
4684
|
-
const baseUrl = endpoints.services[name];
|
|
4685
|
-
if (!baseUrl) return null;
|
|
4686
|
-
if (!path) return baseUrl;
|
|
4687
|
-
const pathReference = path.trimStart();
|
|
4688
|
-
if (path.includes("\\") || pathReference.startsWith("//") || /^[a-z][a-z0-9+.-]*:/i.test(pathReference)) {
|
|
4689
|
-
return null;
|
|
4690
|
-
}
|
|
4691
|
-
const base = new URL(`${baseUrl}/`);
|
|
4692
|
-
const target = new URL(path.replace(/^\/+/, ""), base);
|
|
4693
|
-
if (target.origin !== base.origin || !target.pathname.startsWith(base.pathname)) {
|
|
4694
|
-
return null;
|
|
4695
|
-
}
|
|
4696
|
-
return target.toString();
|
|
4697
|
-
}
|
|
4698
|
-
|
|
4699
4779
|
// src/commands/protocol.ts
|
|
4700
4780
|
function isCommandEnvelope(value) {
|
|
4701
4781
|
if (typeof value !== "object" || value === null) return false;
|
|
@@ -4755,46 +4835,6 @@ function connectEmbedded(options) {
|
|
|
4755
4835
|
};
|
|
4756
4836
|
}
|
|
4757
4837
|
|
|
4758
|
-
// src/schemas/session.ts
|
|
4759
|
-
import { type } from "arktype";
|
|
4760
|
-
var SessionStatus = type(
|
|
4761
|
-
"'created' | 'running' | 'completed' | 'failed' | 'interrupted' | 'waiting_for_input'"
|
|
4762
|
-
);
|
|
4763
|
-
var SessionInfo = type({
|
|
4764
|
-
id: "string",
|
|
4765
|
-
intent: "string",
|
|
4766
|
-
status: SessionStatus,
|
|
4767
|
-
created_at: "string",
|
|
4768
|
-
updated_at: "string",
|
|
4769
|
-
"shared?": "boolean",
|
|
4770
|
-
"memory_enabled?": "boolean",
|
|
4771
|
-
"is_persistent?": "boolean",
|
|
4772
|
-
"is_headless?": "boolean",
|
|
4773
|
-
"viewer_role?": "'owner' | 'viewer'",
|
|
4774
|
-
"template_id?": "string | null",
|
|
4775
|
-
"model?": "string | null",
|
|
4776
|
-
"enable_thinking?": "boolean | null",
|
|
4777
|
-
"solution_id?": "string | null",
|
|
4778
|
-
"solution_ref?": "unknown",
|
|
4779
|
-
"biz_role_id?": "string | null",
|
|
4780
|
-
"solution?": "unknown",
|
|
4781
|
-
"plan_summary?": "string | null",
|
|
4782
|
-
"primary_skill_id?": "string | null",
|
|
4783
|
-
// ship-attack v2:后端 engine.set_bound_skill 写入的"绑定 skill id"。
|
|
4784
|
-
// 未绑定 / 非 ship-attack session 为 null。
|
|
4785
|
-
"bound_skill_id?": "string | null",
|
|
4786
|
-
"replay_state?": "unknown",
|
|
4787
|
-
"is_pinned?": "boolean",
|
|
4788
|
-
"pinned_at?": "string | null",
|
|
4789
|
-
"is_example?": "boolean",
|
|
4790
|
-
"ports?": "unknown",
|
|
4791
|
-
"disable_tools?": "string[]",
|
|
4792
|
-
"runtime_type?": "string | null",
|
|
4793
|
-
"daemon_id?": "string | null",
|
|
4794
|
-
"workspace_path?": "string | null",
|
|
4795
|
-
"match?": "unknown"
|
|
4796
|
-
});
|
|
4797
|
-
|
|
4798
4838
|
// src/schemas/solution.ts
|
|
4799
4839
|
var LayoutType = /* @__PURE__ */ ((LayoutType2) => {
|
|
4800
4840
|
LayoutType2["Default"] = "default";
|
|
@@ -4826,7 +4866,7 @@ export {
|
|
|
4826
4866
|
BladeApiError,
|
|
4827
4867
|
BladeClient,
|
|
4828
4868
|
ClientProjectionBuilder,
|
|
4829
|
-
|
|
4869
|
+
DEFAULT_REPLAY_SPEED,
|
|
4830
4870
|
LayoutType,
|
|
4831
4871
|
ModelsResource,
|
|
4832
4872
|
SDK_NAME,
|
|
@@ -4850,9 +4890,8 @@ export {
|
|
|
4850
4890
|
isCommandEnvelope,
|
|
4851
4891
|
isHiddenInternalMessage,
|
|
4852
4892
|
isInboundEnvelope,
|
|
4853
|
-
loadPlatformEndpoints,
|
|
4854
4893
|
normalizeMessageContent,
|
|
4855
|
-
|
|
4894
|
+
toReplaySnapshot,
|
|
4856
4895
|
transformSlashCommand
|
|
4857
4896
|
};
|
|
4858
4897
|
//# sourceMappingURL=index.js.map
|