REFERENCE
SDK 範例
@asgard-js/core 與 @asgard-js/react 的用法與可以直接操作的範例。
sandbox:// 交接卡
Agent 在 sandbox 裡開了一個瀏覽器工作階段、產出了一份檔案、或整理出一整個資料夾時,需要一個
方式把使用者交接過去。做法是讓附件卡片的 defaultAction 指向 sandbox:// 這個自訂 scheme。
SDK 會在走到「開新分頁」的預設行為之前攔截它,解析成一個具型別的 intent,再交給對應的
處理器。原始的 sandbox:// URI 永遠不會被丟進 window.open()。
URI 格式
sandbox://<sandboxName>/<action>?<query>目前支援三個 action:
URI 解析結果 sandbox://sbx-a/open-browser{ kind: 'open-browser', sandboxName: 'sbx-a' }sandbox://sbx-a/open-file?absolute_path=%2Fhome%2Fuser%2Freport.md{ kind: 'open-file', sandboxName: 'sbx-a', absolutePath: '/home/user/report.md' }sandbox://sbx-a/open-folder?absolute_path=%2Fwork%2Foutput{ kind: 'open-folder', sandboxName: 'sbx-a', absolutePath: '/work/output' }absolute_path必須做 URL 編碼(絕對路徑裡有很多/)。為什麼資料夾要自己一個 action
檔案和資料夾的終點不一樣:檔案進檢視器(讀內容,再掛上變更追蹤),資料夾只把樹展開到 那一層、選取它,不讀內容也不追蹤變更。
那為什麼不共用
open-file、讓面板自己判斷?因為卡片抵達的當下,面板還沒列過那一層,它分不出 這個路徑是檔案還是目錄。「先當檔案試、失敗再退回目錄」也不是可行的替代方案——後端對目錄的fs/file會回500,fs/watch的連線則直接被切斷。所以終點由卡片自己說明;這也是RequestedFile.kind為必填的原因。兩種卡共用同一個自動展開開關卡片到站時要不要順便把內建側欄拉出來,兩種卡看的都是
autoRevealOnOpenFileCard(預設true)。名稱裡的OpenFileCard早於資料夾卡存在,為相容而保留——「卡片可不可以把面板拉 出來」是同一個問題,不需要兩個旗標。接手處理
三個 action 都可以由整合方覆寫:
<Chatbot
config={{ botProviderEndpoint: "..." }}
customChannelId="my-channel"
// 有給就以你的為準;沒給則走 SDK 預設(取一次性 URL 後開新分頁)
onSandboxOpenBrowser={(sandboxName) => { /* … */ }}
// open-file 的目的地通常是 File Explorer 的檢視器
onSandboxOpenFile={(sandboxName, absolutePath) => {
controller.requestFile(sandboxName, absolutePath);
}}
// open-folder 的目的地是檔案樹,不是檢視器
onSandboxOpenFolder={(sandboxName, absolutePath) => {
controller.requestFolder(sandboxName, absolutePath);
}}
// 預設處理器開分頁的目標,預設 '_blank'
sandboxBrowserOpenTarget="_blank"
// 想真的接管 open-file 就一定要加這行,理由見下方
fileExplorer="off"
/>三個 handler 的語意不一樣onSandboxOpenBrowser是真的二選一:有給就以你的為準,沒給才走 SDK 預設。onSandboxOpenFile和onSandboxOpenFolder都不是。 它們是「加上去」而不是「取代掉」——你的 handler 會被呼叫,但內建的 File Explorer 側欄同時也照樣會被拉開,因為 那段邏輯只看fileExplorerprop,不看你有沒有給 callback。而fileExplorer預設就是'builtin'。結果是你自己的 controller 和 Chatbot 內部的 controller 會同時收到同一個
requestFile或requestFolder。要真的接管,必須另外把fileExplorer設成"off"。自己解析
resolveSandboxUri也可以單獨使用——例如你自己渲染卡片的時候:import { resolveSandboxUri } from "@asgard-js/core";
const intent = resolveSandboxUri(uri);
if (intent === null) {
// 不是 sandbox://、action 不認得、或缺 absolute_path
// → 當成一般卡片處理,絕對不要 window.open() 原始 URI
}回傳型別:
type SandboxUriIntent =
| { kind: "open-browser"; sandboxName: string }
| { kind: "open-file"; sandboxName: string; absolutePath: string }
| { kind: "open-folder"; sandboxName: string; absolutePath: string };備註解析用的是手寫 regex 而不是
new URL()。絕對路徑帶有大量/,而 WHATWG URL parser 對 非特殊 scheme 的 host 大小寫處理並不可靠;query 的部分仍交給URLSearchParams解碼。一般連結不受影響
https://之類的defaultAction完全照舊走正常開連結行為——攔截只發生在sandbox://。也看看
- File Explorer —
open-file與open-folder的落點 - 冷啟動 HUD — sandbox 啟動與
launchedSandboxes - 訊息模板 — 附件卡片本身的結構
- File Explorer —