@fsaos/出道口
FSAOS Gateway SDK——从所部署的生产捆绑中逆向地被设计成干净可维护的TypeScript源.
这是什么
这是 源代码 为51KB IIFE捆绑, 授权今天的每个FSAOS页面。 此前,R2上仅存在精密的捆绑而无源回取. 这个软件包重建了完整的TypeScript源,有合适的类型,JSDoc评论,以及可复制的构建管道.
建筑
SDK是装入的 <script> 由调度人员标记并曝光 window.__FSAOS_GATEWAY__ 作为一个全球。 部件通过 require() 闪入 dom-mount.js。 。 。 .
CDN Scripts (loaded before gateway.js):
1. supabase-js@2 → window.supabase
2. react@18 + react-dom → window.React / window.ReactDOM
Gateway SDK:
3. gateway.js → window.__FSAOS_GATEWAY__
Mount Runtime:
4. dom-mount.js → require() shim + mount()
5. Component IIFE → window.__FSAOS_COMPONENT__
6. __FSAOS_MOUNT__() → renders the component
地点对访问——两种阅读
两个问题看起来相似,但由不同的电话回答。 获得这个权利是正确UI和显示一个成员项目不是他们的区别.
- 地点 ——"这道道上有什么"/"这个帐户中这种类型的"-
useItems, (中文(简体) ),useList, (中文(简体) ),useTree。这些范围为经常账户,并返回那里的东西。 没错 没有 知道吗 你们在帐户里的人。 。 。 . - 访问 ——"这个演员在这个背景中能达到什么?" ——
useResolvedItems。由内核解决服务器侧:账户联盟/成员/空间/系统/许可级,框架绑定在代理主机上,优先级识别.
拇指规则:如果答案因不同而不同 谁问 或者说 它们在哪个频道 - 连接器、工具或任何成员范围的东西——使用 useResolvedItems。SDK从不计算帧或权限客户端;它向内核转发并生成答案.
出口
错误
EnforcementDeniedError拒绝执行的错误类别(规则、准入、应享权利)
会话管理
initSession()——通过域范围会话初始化GET /d/{hostname}/initinitializeFsaos()- 冠状单声域会话+跟踪靴子clearSession()- 清除缓存会话getSessionEntry()获得当前会话条目( 或 无效)getAccessToken()——获取当前认证符 (等待第一个认证事件)setCachedToken()- 手动设定验证符(用于嵌入符)getScope()编号 :setScope()编号 :subscribeScope()——主动范围(账户/开关)接入器+订阅
网关呼叫
gatewayCall(method, params)- RPC核心调度器:POST /d/{hostname}
公开页面表格
submitForm(capabilityId, fields, options?)——通过公有领域网关提交与当前已发布的页面发布相绑定的窗体数据集. 浏览器仅提供已声明的能力名,当前页面 URL,以及字段值;它从未收到数据集身份或写入证书. 这项工作来自IIFE、无害环境管理或npm建筑.FormSubmissionError- 包括马厩fillId, HTTP 状态,以及错误代码,所以不确定的提交可以重试而不创建第二个填充.
const receipt = await submitForm('hero-optin', { email });
if (receipt.next) window.location.assign(receipt.next);
Plain HTML 不装入 SDK 可以使用相同的合同 :
<form method="post" action="https://fsaos-mcp-gw-rust.fly.dev/d/launch.example.com/actions/hero-optin">
<input type="hidden" name="_page_path" value="/offer">
<input type="email" name="email" required>
<button type="submit">Continue</button>
</form>
共享实例
supabase共享的Supabase客户实例queryClient共享 TanStack 查询实例
查询密钥厂
vfsKeys——所有 VFS 数据的决定性查询密钥
频道房间模式
频道包含校长为成员. 大赦国际作为主要/成员,而不是作为单独的渠道。 SDK通过显示房间模式 Channel.aiMode (代理系统主Spec D17;内核修订) b7c4d2a9e1f3, 2026-10-29) (中文(简体) )
Channel.aiMode | 含义 |
|---|---|
"open" | 每条消息都要由楼层决定,不管是谁说的. |
"active" | 只有值班人员回答一个人(Channel.activeAgentIds; 缺席 = 每个在频道就座的代理人). 一个特工的讯息只通过提及就能唤醒另一个特工. |
"strict" | 仅提及或指名收件人. |
"hand_raise" | 楼层决定是针对一个人的讯息,但没有人被唤醒;答案是: conversation.hands_raised 活动(subscribeToEvents)而取所言者,明收所寄也. |
提及,一个被命名的收件人和 trigger_ai 在每个模式下回答。 内核正好存储这四个值( Q)CHANNEL_AI_MODES并拒绝任何其他 create-channel (单位:千美元)CHANNEL_AI_MODE_INVALID遗产 on编号 :off 行被重写 on → active, (中文(简体) ), off → strict。内核未重写的一行读取的方式相同:
| 遗留/后端值 | SDK 数值 |
|---|---|
"on", (中文(简体) ), "auto" | "active" |
"off", (中文(简体) ), "mention",缺少或未知 | "strict" |
聊天、线索和突围
一个频道是聊天(一个DM或一个团体),其成员是它的观众(Agent System Master Spec D21,2026-10-30). 在一个聊天中,每个消息是一回事,一个消息,每个顶级行的内核计算有两面旗帜:
| 旗帜 | 含义 |
|---|---|
ChannelMessage.reply_count > 0 | 线程 : 聊天内部的边谈( T)useChannelMessages(...).threads) (中文(简体) ). |
ChannelMessage.member_count > 0 | 分出:线根有自成一员,只取自(和上下所有者)读取并贴入;内核向外人隐瞒useChannelMessages(...).breakouts) (中文(简体) ). |
创建聊天 member_ids 已经诞生 auto_thread: false (消息发生在频道种子中);成员自己的代理频道保存 auto_thread: true特工在与他人交谈时,将工作交给其他特工 todo 工具的 assign_to 编号 : report_to;受让人在聊天中被唤醒,当工作完成、失败或需要输入时,授权代理在原始对话中被唤醒.
SDK不决定AI何时应作出反应,产出物剂,或推断物剂身份. 它转发网关呼叫并规范读取模型;内核拥有频道政策和消息触发行为.
成员、席位和代理人
会员门的打出平面(Agent System Master Spec D13-D21). 每个函数都转发一个网关呼叫,内核读取的精确参数并返回其接收;钩子(useInviteMember, (中文(简体) ), useAcceptInvite, (中文(简体) ), useSeatMember, (中文(简体) ), useCreateAgent, (中文(简体) ), useAnswerInput)用每个需要的缓存无效来包裹相同的呼叫.
| 打电话 | 内核门 | 它会做什么 |
|---|---|---|
inviteMember({ email, target_scope_id, role? }) | invite-member | 一个成员项目, pending 直到被接受为止. role 是他们在账户中的地位(梯子角色,默认) editor· ; ROLE_ABOVE_INVITER 如果超过你的话。 回返 member_id。 。 。 . |
seatMember({ chat_id, member_id, role? }) | link (单位:千美元)has_member(中文(简体) ) | 把它们放在一个聊天(一个频道)或突破(一个线根)上. role 进入边缘 context 并且只是他们在聊天中的角色;缺席读作编辑. 没有 metadata 在边缘。 在受邀者之后,请一位受邀成员就座:名册上显示该席位为待决席位并开始接受. |
acceptInvite({ member_id }) | accept-invite | 成员转身 active· ; can_do 在每一次谈话中,他们都坐在那里,这是谬误的。 无二座阶. |
createAgent({ display_name, description?, role_path?, character?, upload?, instructions? }) | create-agent | 一项交易:身份、主会员物品、, instructions/ 块,全账户角色,座位 每一个空间. role_path 必须是已经存在(ROLE_NOT_FOUND; 为用户默认角色省略. 只有一个人可以称呼它。 回返 principal_id, (中文(简体) ), handle, (中文(简体) ), member_id, (中文(简体) ), member_path, (中文(简体) ), seats。它不创造聊天: 与代理交谈, create-channel 与 member_ids: [principal_id] a 德国马克. |
answerInput({ step_id, answer, input_request_id? | input_request? }) | todo-control answer_input | 回答一个步骤,那就是 awaiting_input,请从步骤中读取请求编号或请求对象(TODO_INPUT_CHANGED 如果步子继续前进的话). 内核记录 input_response,将该步骤还原为待定并唤醒受让人;代理人决定下一步。 没有任何机械能读出答案是或不是. |
update 与 type_data 在顶层合并:每个发送的密钥都替换了整个密钥,没有发送的密钥被保存({ type_data: { ai_mode: 'strict' } } 改变模式,没有其他任何变化; _remove_keys: ['x'] 删除密钥。 一个频道的 name (路径)是受约束的,是被拒绝的 type_data.display_name。 。 。 .
VFS 操作(纯自动同步函数)
normalizeItem(raw)——将原始网关响应规范化VFSItemfetchVfsItem(path)——获取单个项目fetchVfsItemById(id)——通过 UUID 获取单个项目fetchVfsChildren(path)——取取子(也取取取缓存)fetchItems(filter)——以结构化过滤器获取项目:1type,或any_of各类团体;fields, (中文(简体) ),field_in, (中文(简体) ),discovery页:1itemsFilterFromQueryFilter(typeData, { scope?, limit? })- 转换存储的已保存过滤器(a)query_filter插入过滤器useInfiniteItems带fetchTypeDefinitions(scopeId?)——获取类型定义为Map<string, TypeDefinition>fetchEdgesForItem(itemId)——取出边缘(来/出)fetchVfsTree(path, depth?, types?, limit?)——获取递归树fetchMemberFocus(scopeId, types?, limit?)- 获取以成员为重点的项目fetchResolvedItems(contextId, itemType)- 强制性形式useResolvedItems(查阅决议)inviteMember编号 :acceptInvite编号 :seatMember编号 :createAgent编号 :answerInput- 见成员、席位和代理人
快取无效
invalidateChildren(path)无效的儿童查询invalidateItem(path)— 无效的项目查询invalidatePathAndParent(path)- 无效项目+父母子女invalidateSubtree(path)— 无效包含路径前缀的所有查询invalidateAllVfs()- 核:所有VFS查询无效invalidateTypes(scopeId?)- 无效类型定义
实时
initVfsRealtimeWs(scopeId)— 在 WebSocket 上订阅 VFS 更改disposeVfsRealtimeWs()- 断开实时连接mountRealtimeScope(path)编号 :unmountRealtimeScope(path)——从实时订阅中添加/删除范围subscribeToPath(path, callback)——订阅项目路径及其后人waitForPathReady(path, timeoutMs?)——等待网关确认当前路径订阅resubscribePath(path)——为活动路径发送新电线级订阅getPathSubscriptionState(path)- 读取pending, (中文(简体) ),ready, (中文(简体) ),disconnected,或unsubscribedonConnectionStateChange(callback)——央视运输连接状态变化
反应钩 (TanStack 查询)
读取钩:
| 钩子 | 说明 |
|---|---|
useItem(path) | 按路径排列的单个项目 |
useItemById(id) | 单项, 按UUID |
useList(path) 编号 : useChildren(path) | 一条路的儿童(地点) |
useItems(filter) | 按类型分列的带有过滤器的项目(位置;账户范围, 没有 框架意识) |
useInfiniteItems(filter, { realtime? }) | Kernel-filtered infinite pages;可选事件驱动的刷新,缓存项目,加载更多和回放. 没有选票. |
useIncrementalItems(filter, { shouldReadUpdate? }) | Live Create-at feed:查询页面一次,点读插入和所选更新,删除后调节/再连接. 没有选票. |
useInfiniteChildren(path) | 无限滚动目录列表 |
useTree(path, depth?) | 递归树 |
useSearch(query, types?) | 关键字搜索 |
useResolve(params) | 多信号搜索(准确 + 三克 + 关键词 + 语义) |
useResolvedItems(contextId, itemType) | 访问 决议——这个行动者能够在这里达到什么(框架范围,预知). 用于连接器,工具,任何成员范围类型. |
useOpen(path, options?) | 打开信封——使指令+兼容组件 |
useEdges(itemId) | 项的边缘 |
useTypes(scopeId?) 编号 : useType(typeKey, scopeId?) | 类型定义 |
useTypeHelpers(scopeId?) | 衍生类型系统帮助函数 |
useMemberFocus(scopeId, types?, limit?) | 范围与当前用户相关的项目 |
useItemVersions(itemId, opts?) | 版本历史useItemHistory 已贬值) |
useRecentActivity(scopePath, limit?, offset?) | 范围审计日志 |
useScope() 编号 : useScopeReady() | 当前范围会议/准备情况 |
useAuth() | 反应 Supabase 认证 |
useAccounts() | 账户成员、转换器账户以及每个账户下的账户 |
usePrincipal() 编号 : useUpdatePrincipal() | 当前主要身份/更新 |
useAsset(idOrPath) | 解析资产 URL |
useComponent(path) | 从边缘动态加载一个组件 |
useTheme() | 读取主题 CSS 自定义属性 |
useInfiniteItems(filter, { realtime: true }) 使用已有的 WebSocket 事件
发送和重联恢复。 像其他实时钩子一样,主机
必须持有一个SDK账户或路径订阅(直接或通过SDK靴子).
它不会打开单独的套接字或订阅每个组件一次。 共享
查询缓存保持新鲜,直到相关项目更改或传输重联.
不同直接事件 parent_id 不更新父过滤列表 ;
正在装入的物品仍然更新其以前的列表 。 已关闭列表标记
不取取而 stale, 而未更改的列表从缓存重新打开 。 所有消费者
相同的过滤查询应当使用相同的实时设置.
对于一个高流量的信息传输, useIncrementalItems 避免重装
回复更改时加载页面。 它支持一个项目 type,明确
scope, (中文(简体) ), sort_by: 'created_at'选择 parent_id, (中文(简体) ), fields,或
field_in。返回与
useInfiniteItems。插入一个成本项目读取;除非
shouldReadUpdate(change, loadedItems) 选择它们; 移动、删除和重新连接
对齐页面。 更复杂的过滤器返回到普通实时
列出。 主机仍然需要其正常的账户或路径WebSocket订阅.
过滤器名称 1 type,或 any_of: 1-10组,每组有一个 item_type, (中文(简体) ),
fields 财务报告和审定财务报表 field_in,因此,一个列表涵盖项目类型,一个总计和一个
页面顺序. discovery: 'catalog' 只保留内核类
人自己的独立事物;内核在呼出之前应用. 菜单
存储的段落在不手工构建过滤器的情况下运行 :
const { resolvedChildren } = useArrangement({ key: 'library' });
const section = resolvedChildren.find(child => child.child_type === 'query_filter');
const list = useInfiniteItems(
itemsFilterFromQueryFilter(section?.type_data, { scope: accountPath, limit: 50 }),
{ realtime: true },
);
// list.allItems, list.fetchNextPage(), list.isExhausted, list.totalHint
使用已有的
refreshQuery 防止重叠负载。 多页被读取
插入/删除后,相抵页码保持不变.
目前VFS通知不包括任意性 type_data 字段,这样
直接儿童变化可能需要读取列表以评价字段过滤器
(例如 role=user) (中文(简体) ). 缺少的老父母信息只能识别
当更改的项目已缓存时移动出; 重新连接或下一次获取
调节剩余数。 不使用背景投票.
频道/ 通知钩 :
| 钩子 | 说明 |
|---|---|
useChannelMessages(opts) | 频道消息+捆绑卡片 |
useInfiniteChannelMessages(opts) | 光标贴出的消息( 活的尾巴) |
useAllChannels() 编号 : useChannels() 编号 : useDmChannels() | 频道列表( 所有 / 公用 / DM); 存档频道仅与 { includeArchived: true } |
usePersonalItemOrder('channel-order' | 'project-order', items) | 通过现有安排应用并保存被选中的空间成员的个人顺序. |
useNotifications(opts?) | 通知种子 |
useUnreadCounts(opts?) | 未读总数 |
实时钩子 :
| 钩子 | 说明 |
|---|---|
useEventStream(...) | 按类型订阅 SSE 事件 |
useRealtimeQuery(opts) | useQuery 在匹配事件上自动调整 |
突变钩:
| 钩子 | 说明 |
|---|---|
useCreate() | 创建项目( 无效父子) |
useUpdate() | 更新项目( 无效项目 + 父) |
useDelete() | 删除项( 从缓存中移除 + 无效父) |
useMove() | 移动项目( 无效旧父、 新父、 项目) |
useLink() | 创建边缘( 两个端点的边缘无效) |
useMutation(method, opts?) | 任何网关方法的一般变异 |
源文件
src/
types.ts — All type definitions (VFSItem, TypeDefinition, VFSEdge, etc.)
config.ts — Reads __FSAOS_CONFIG__, creates Supabase client
enforcement.ts — EnforcementDeniedError class
session.ts — Auth token lifecycle, domain session init, scope accessors
client.ts — gatewayCall() — the core RPC dispatcher
query-client.ts — TanStack QueryClient with VFS-tuned defaults
vfs-keys.ts — Query key factory
vfs.ts — VFS fetch functions + cache invalidation helpers
vfs-realtime-ws.ts — WebSocket realtime subscriptions
ws.ts — WebSocket transport (events, scope, ping)
hooks.ts — All React hooks (useQuery/useMutation wrappers)
resolved-items.ts — useResolvedItems / fetchResolvedItems (access resolution)
upload.ts — File upload (imperative + hook)
schema-utils.ts — Schema interpretation utilities
index.ts — Barrel export
应用程序拖带
在导入 SDK 之前设定已注册的应用程序配置, 然后
初始化一次 。 默认启动跟踪; 自定义商务事件依然存在
明确。 每一份已发布文件的自动页面和声明动作抓取运行
源,宣布这些适配器。 成功提交的公开表格
其收讫于声明的每个来源 form_receipt; 默认来源
继续提供耐用表格行的访客和会话归属.
window.__FSAOS_CONFIG__ = {
hostname: 'app.example.com',
gatewayUrl: 'https://fsaos-mcp-gw-rust.fly.dev',
};
const { initializeFsaos } = await import('@ai4b-team/fsaos-gateway-sdk');
const fsaos = await initializeFsaos();
使用 initializeFsaos({ tracking: false }) 在同意或产品政策时
需要跟踪才能继续被禁用。 已出版的FSAOS IIFE网页称相同
鞋带自动.
构建
npm install
node build.mjs # dist/gateway.js (minified IIFE, ~51 KB)
node build.mjs --dev # dist/gateway.dev.js (readable, ~128 KB)
该建筑产生:
dist/gateway.js- 被分配到window.__FSAOS_GATEWAY__dist/gateway.dev.js— 调试时未忽略dist/*.d.ts——类型脚本声明dist/*.js.map- 原始地图
依赖关系
内嵌 (IFE的一部分):
@tanstack/react-queryv5 — 查询/ 调试缓存、 调试、 背景调试
外部全球 (通过CDN装入) <script> 在此捆绑之前 :
react通过window.React@supabase/supabase-js通过window.supabase.createClient
0个核心
这个SDK有 零 引用源,捆绑,或类型声明中的"内核". 所有命名都使用网关术语. (内核拥有分辨率,帧和强制;SDK是向它前进的控制表面
许可证
专有和来源 看到了吗 自由主义。您可以安装 并将此软件包作为您自己的应用程序中的依赖,但您可以 没有 复制、修改或以独立库的形式再版。 版权所有 AI4B团队.
个人频道和项目订单
usePersonalItemOrder(key, items) 重复使用 resolve-arrangement 和普通情况
update 原始的。 原始的。 提供完整的犬科身份证
已返回 items 打电话 reorder(ids) 一次在滴滴。 它回来了 contextKey
用于在空间变化时重置在进展中的 UI 手势,加 ready, (中文(简体) ),
supported, (中文(简体) ), isLoading, (中文(简体) ), isSaving, (中文(简体) ), error, (中文(简体) ), saveError 财务报告和审定财务报表 refetch。 。 。 .
钩子只读取选定的空间成员提供的内容 channel-order 或者说
project-order 安排。 账户挂载不支持且不提出请求 .
它从未造成一个缺失的安排或回到一个共同的蓝图.
没有保存位置的频道出现 在此之前 手动命令频道,
最新的创建首先使用 Channel.createdAt 从现有的查询有效载荷.
缺少/相等的创建时间保留源顺序. 没有保存的订单,这是
也是频道列表的默认顺序. 现有手动设置的频道保存
他们的相对位置。 抵达/搬迁不作安排书写
下个明确的重排会保存已提供的命令。 项目订单不变.
一个保存保存了看不见的ID, 拒绝 Stale Space 回调和无效列表,
,并通过现有的连接安排进行调节。 没有投票,
项已读取或包含父更改。 对于页面收藏, 应用命令
在 pagination 之前的后端中; 此勾钩未发现工程文件夹
或提供该页已读合同.
个人侧边栏
usePersonalSections(contextId, 'channels' | 'chats' | 'projects') 读取个人
参考组。 频道/项目使用空间ID,或聊天使用频道ID.
钩子回来了 sections, (中文(简体) ), ready, (中文(简体) ), change, (中文(简体) ), destination,加载/误入
状态. change 支持创建、重命名、删除、指定、删除并重排
操作;它保存了犬类物品的位置。 提供稳定的客户端 UUID
页:1 section_id 因此,重试不能创建重复的区.
捕获 destination(sectionId) 在启动新频道或聊天之前,先拨打
assignPersonalSection(destination, savedItemId) 一旦犬类物品
已保存。 重试该备案与创建/发送分开。 俘虏
空间和上下文在导航后隔离出后期结果. 版本快照
防止迟发反应取代新的组织;刷新现有用途
合并了项目更改事件和重新连接,没有投票.
要求 personal-sections 来自迁移的内核方法 0793523446ad。 。 。 .
项目准备就绪;消费者必须单独提供项目UI.
绑定浏览器读取流量
共享 SDK QueryClient 的主机可以在安装读取器之前选择迁移调度 :
configureGatewayReads({ concurrency: 6, timeoutMs: 15_000, onTiming: recordTiming });
useInfiniteItems(filter, { priority: 'foreground' });
gatewayCall('query', params, { signal, priority: 'background' });
等同的飞行读取 共享一个运输请求,范围为证书,
会话生成,主机名,以及有效的参数. 每个观察员收到其全权证书
拥有结果对象,并可独立取消。 上次取消时中止
获取; 身份重置取消队列并活动读取 。 最后期限包括队列
时间. onTiming 仅报告方法、优先顺序、队列/请求持续时间和结果.
命令、写、实现查询和订阅绕过队列。 这个
排程器从不重试请求; 重试/冷却策略仍为主机的
已存在的查询客户端。 没有配置,运输行为不变.