limixbot 0.3.25 → 0.3.26
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/assets/{ActivityPanel-CgPkGKZZ.js → ActivityPanel-DkwIh-YJ.js} +1 -1
- package/dist/assets/{App-lurtH-qI.js → App-DCe3hDbm.js} +119 -119
- package/dist/assets/{AppEventEditor-G_71EWui.js → AppEventEditor-25dBAYiN.js} +1 -1
- package/dist/assets/BotSettingsDialog-DJxZo-ro.js +16 -0
- package/dist/assets/{BrowserPanel-DrKZNyYB.js → BrowserPanel-DVji4pZh.js} +1 -1
- package/dist/assets/{BrowserProfilesManager-CtiUbbdv.js → BrowserProfilesManager-C4rCEedT.js} +1 -1
- package/dist/assets/{CloudAddDialog-D4BX2927.js → CloudAddDialog-BybF9Mcz.js} +1 -1
- package/dist/assets/{ComputerFilesPane-RnmlJhlD.js → ComputerFilesPane-D-EgbYKM.js} +1 -1
- package/dist/assets/{ComputerPanel-D8upQSmd.js → ComputerPanel-CGKdVGz4.js} +2 -2
- package/dist/assets/{DataPanel-MZcku4SL.js → DataPanel-BXh6perv.js} +1 -1
- package/dist/assets/{DesktopViewer-p7N7cqle.js → DesktopViewer-CRcjWYHe.js} +1 -1
- package/dist/assets/{FilesView-CDbtoPxh.js → FilesView-B67sskTN.js} +1 -1
- package/dist/assets/{GrokMoveDialog-BebZb43R.js → GrokMoveDialog-DNJckF5G.js} +1 -1
- package/dist/assets/{InspectorPanel-BMkRIuTz.js → InspectorPanel-CjtRMfJu.js} +1 -1
- package/dist/assets/{KeyboardShortcutsModal-3XyVcNTZ.js → KeyboardShortcutsModal-znw4NkA5.js} +1 -1
- package/dist/assets/{LiveDesktop-BkRR5lCn.js → LiveDesktop-BgFrQKAC.js} +1 -1
- package/dist/assets/{LocalVmWorkspace-9l9iWtAy.js → LocalVmWorkspace-Dk61S9xg.js} +1 -1
- package/dist/assets/{MacLocalControl-B45d0l7H.js → MacLocalControl-DjyhCaWB.js} +1 -1
- package/dist/assets/{MarkdownFilePreview-ChR-f9oD.js → MarkdownFilePreview-Btb--ZSy.js} +1 -1
- package/dist/assets/{MenuMotion-BBaxdyL_.js → MenuMotion-BOUToDZz.js} +1 -1
- package/dist/assets/{NewBotDialog-C_uQRhxy.js → NewBotDialog-CsQPMX7F.js} +1 -1
- package/dist/assets/{PhoneAppDialog-CDO1xUXD.js → PhoneAppDialog-4OG210md.js} +1 -1
- package/dist/assets/{PlacesRail-iu-rjh5s.js → PlacesRail-dslg5RRJ.js} +1 -1
- package/dist/assets/{PluginsPanel-DlQBRYQB.js → PluginsPanel-CaWk4eSi.js} +1 -1
- package/dist/assets/{ProIntroduction-RbtTSiR6.js → ProIntroduction-BuOIgmKa.js} +1 -1
- package/dist/assets/{RemoteAgentSettingsPanel-C8r-80J4.js → RemoteAgentSettingsPanel-Drk4pPKT.js} +1 -1
- package/dist/assets/{RoomPanel-BIUTnm5N.js → RoomPanel-BGLIvmBz.js} +1 -1
- package/dist/assets/{RoutinesPage-DROK-336.js → RoutinesPage-BuAEX1fg.js} +2 -2
- package/dist/assets/SettingsModal-CwK4KFqq.js +19 -0
- package/dist/assets/{ShareTeamDialog-CVnQoRVQ.js → ShareTeamDialog-C5Fv8jWl.js} +1 -1
- package/dist/assets/{SignedInSites-B-iBIlfL.js → SignedInSites-BQ2QP4YA.js} +1 -1
- package/dist/assets/TeamLibraryPanel-B0HAAvLv.js +1 -0
- package/dist/assets/{TeamMapPage-kXk9hCNu.js → TeamMapPage-DCkx9eov.js} +1 -1
- package/dist/assets/{TriggersPage-prZBUP3d.js → TriggersPage-B2sqDFfI.js} +1 -1
- package/dist/assets/{VisibilitySection-DJjvTyLx.js → VisibilitySection-CqL-TbZp.js} +1 -1
- package/dist/assets/{WebhookTrigger-BAFikH71.js → WebhookTrigger-CorVaJKR.js} +1 -1
- package/dist/assets/{WorksOnGrid-DJmfLtP8.js → WorksOnGrid-1tMl62nD.js} +1 -1
- package/dist/assets/{abnfDiagram-YUKMZFIV-DUI6uevQ.js → abnfDiagram-YUKMZFIV-UeQ15ljp.js} +1 -1
- package/dist/assets/{activity-DVLFekjx.js → activity-Rykx5lZJ.js} +1 -1
- package/dist/assets/{architectureDiagram-47P4ROYG-CMELW5_4.js → architectureDiagram-47P4ROYG-Cb42ilBV.js} +1 -1
- package/dist/assets/{blockDiagram-E7TT5TSB-C3xD0z1e.js → blockDiagram-E7TT5TSB-BF1yg64q.js} +1 -1
- package/dist/assets/{c4Diagram-CRA5TL53-Cm9X0GEV.js → c4Diagram-CRA5TL53-VaNJpISp.js} +1 -1
- package/dist/assets/{calendar-days-CDvwJ_d4.js → calendar-days-kC634hX1.js} +1 -1
- package/dist/assets/channel-Di74R1Kh.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-LWcbjcjL.js → chunk-2Q5K7J3B-NtXjlfnQ.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-B3rNoWeE.js → chunk-5VM5RSS4-CYWCBb40.js} +1 -1
- package/dist/assets/{chunk-7TKQ45FW-FL9R0rHe.js → chunk-7TKQ45FW-CTx1wLnU.js} +1 -1
- package/dist/assets/{chunk-AW2ZBBNX-JRef_4rd.js → chunk-AW2ZBBNX-PDrwpbhk.js} +1 -1
- package/dist/assets/{chunk-FVRAUYC3-C__loQFR.js → chunk-FVRAUYC3-CqIp8Ubp.js} +2 -2
- package/dist/assets/{chunk-HTAEGDNF-Bx74_xmN.js → chunk-HTAEGDNF-yA6riuIr.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-B20vr3uH.js → chunk-JWPE2WC7-DEE_S031.js} +1 -1
- package/dist/assets/{chunk-KQW6MTUR-DfO9J2pt.js → chunk-KQW6MTUR-QybxNtsY.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-BAPGqnqj.js → chunk-XXDRQBXY-BjchQyqU.js} +1 -1
- package/dist/assets/{classDiagram-v2-K4WV4PDN-C3VR3yN8.js → classDiagram-v2-K4WV4PDN-DtOc8Abr.js} +1 -1
- package/dist/assets/{coins-CueFc0mY.js → coins-D0wNJCPg.js} +1 -1
- package/dist/assets/{core-Bw6kaGwe.js → core-Blwbk7ct.js} +1 -1
- package/dist/assets/{cose-bilkent-JH36ORCC-19p3m04s.js → cose-bilkent-JH36ORCC-CWHsqn7M.js} +1 -1
- package/dist/assets/{cynefin-EF2NZ3EQ-Tv0tce7B.js → cynefin-EF2NZ3EQ-BqS-D9jN.js} +1 -1
- package/dist/assets/{cynefinDiagram-3GCD6N5R-Iin0JwlW.js → cynefinDiagram-3GCD6N5R-Fc51YETb.js} +1 -1
- package/dist/assets/{dagre-W4DXFKR2-00TRmSmB.js → dagre-W4DXFKR2-So1kz52t.js} +1 -1
- package/dist/assets/{diagram-2UJZ2QOL-DslMBjKt.js → diagram-2UJZ2QOL-DpdA_Lk0.js} +1 -1
- package/dist/assets/{diagram-OVF4WLC6-C3o3f1eL.js → diagram-OVF4WLC6-BqSu7ih2.js} +1 -1
- package/dist/assets/{diagram-PFPMY2P6-RRy9F9wH.js → diagram-PFPMY2P6-BoM6YcIG.js} +1 -1
- package/dist/assets/{diagram-UMYRVEAY-CWfRKtkp.js → diagram-UMYRVEAY-B7wKoLi9.js} +1 -1
- package/dist/assets/{diagram-YEKJPTXX-SlJKvYay.js → diagram-YEKJPTXX-BEXKw540.js} +1 -1
- package/dist/assets/{diagram-ZIFT7M5P-DqzI8_eN.js → diagram-ZIFT7M5P-BAD7sJRi.js} +1 -1
- package/dist/assets/{ebnfDiagram-VR2GEFS7-CDsrBNMu.js → ebnfDiagram-VR2GEFS7-DILoqvy-.js} +1 -1
- package/dist/assets/{elk-IJKZMXRS-iikFqO-X.js → elk-IJKZMXRS-BHW-Cxin.js} +1 -1
- package/dist/assets/{erDiagram-O2IAPWRE-DzFCc0Gp.js → erDiagram-O2IAPWRE-Blp0Wwq0.js} +1 -1
- package/dist/assets/{file-up-BzW-KJx-.js → file-up-CEuHjVBK.js} +1 -1
- package/dist/assets/{flowDiagram-OXPTDLAJ-U5nLRus2.js → flowDiagram-OXPTDLAJ-6SkVmYje.js} +1 -1
- package/dist/assets/{ganttDiagram-R7TSEDQI-h7xqrZ23.js → ganttDiagram-R7TSEDQI-DFDwj8QX.js} +1 -1
- package/dist/assets/{gitGraphDiagram-XJZIOB7I-Bo9ZX2g5.js → gitGraphDiagram-XJZIOB7I-mtJG-sis.js} +1 -1
- package/dist/assets/{import-name-BN_-MYip.js → import-name-B3mVDURb.js} +1 -1
- package/dist/assets/{index-DkDFNBNq.js → index-BZOGYABm.js} +1 -1
- package/dist/assets/{index-BEjJ5c6H.js → index-D2Jm8LAf.js} +2 -2
- package/dist/assets/{infoDiagram-5W2HQ5XZ-CAZmFoiz.js → infoDiagram-5W2HQ5XZ-CmyXB17U.js} +1 -1
- package/dist/assets/{ishikawaDiagram-K3B6WC7H-T1M7DtvG.js → ishikawaDiagram-K3B6WC7H-BKFtIESa.js} +1 -1
- package/dist/assets/{journeyDiagram-COXZFDF6-DzO0Gzq9.js → journeyDiagram-COXZFDF6-DuXMREdz.js} +1 -1
- package/dist/assets/{kanban-definition-P3RFRI5V-C0fM1UHu.js → kanban-definition-P3RFRI5V-B9mWvKdh.js} +1 -1
- package/dist/assets/{mcp-servers-C7oRQpN5.js → mcp-servers-D3PCWkFu.js} +1 -1
- package/dist/assets/{mermaid.core-mKZTdBWz.js → mermaid.core-3c-Lql2y.js} +4 -4
- package/dist/assets/{mindmap-definition-LZFPQGKD-DEgoC28p.js → mindmap-definition-LZFPQGKD-BmLt9bkD.js} +1 -1
- package/dist/assets/{minimize-D-CvaOV-.js → minimize-BfKI3Nbu.js} +1 -1
- package/dist/assets/{monitor-DRcE92Rj.js → monitor-C-EBTsrK.js} +1 -1
- package/dist/assets/{new-bot-COj1uEQa.js → new-bot-3sQib6nJ.js} +1 -1
- package/dist/assets/{org-library-BIb28h24.js → org-library-DHuACSMf.js} +1 -1
- package/dist/assets/{package-format-hON5OXLZ.js → package-format-CAZ-xCP-.js} +1 -1
- package/dist/assets/{pegDiagram-BGZESJAR-Vatn56u_.js → pegDiagram-BGZESJAR-5w1MTtLg.js} +1 -1
- package/dist/assets/{pieDiagram-CAPJLFHJ-DniOgFo3.js → pieDiagram-CAPJLFHJ-C4etNvsK.js} +1 -1
- package/dist/assets/{quadrantDiagram-GDMTTRAM-BqhAQt5w.js → quadrantDiagram-GDMTTRAM-k4cDUeIN.js} +1 -1
- package/dist/assets/{rail-BWcrFQ2_.js → rail-C6qkoJbO.js} +1 -1
- package/dist/assets/{railroadDiagram-SM67HX2B-CxHs3JOs.js → railroadDiagram-SM67HX2B-B9d0qasL.js} +1 -1
- package/dist/assets/{remote-desktop-panel-9XdtvJrd.js → remote-desktop-panel-sCBdOaQh.js} +1 -1
- package/dist/assets/{requirementDiagram-X7JNWC4A-ChjhMFon.js → requirementDiagram-X7JNWC4A-BlYV4z86.js} +1 -1
- package/dist/assets/{sankeyDiagram-UM26HJRW-B67bYF8E.js → sankeyDiagram-UM26HJRW-DaIVWJEd.js} +1 -1
- package/dist/assets/{sequenceDiagram-ZO4K6R2Y-CTy7B-eu.js → sequenceDiagram-ZO4K6R2Y-CAnXpud0.js} +1 -1
- package/dist/assets/{sizeCapture-INFHLROL-uy6qiY-2.js → sizeCapture-INFHLROL-BIeqIcHi.js} +1 -1
- package/dist/assets/{stateDiagram-v2-TBUQTH76-BvYYPNqL.js → stateDiagram-v2-TBUQTH76-7ql5dn9y.js} +1 -1
- package/dist/assets/{styles-DlOxUga6.js → styles-BRMaHK08.js} +2 -2
- package/dist/assets/{swimlanes-N4OXWK64-CMSAKu-2.js → swimlanes-N4OXWK64-CItt-UlU.js} +1 -1
- package/dist/assets/swimlanesDiagram-K3J5GTZL-DAZsljU6.js +8 -0
- package/dist/assets/{team-share-BbQqJ5O6.js → team-share-2OjfOHsk.js} +1 -1
- package/dist/assets/{timeline-definition-YOQAKGHF-BOK1V5EC.js → timeline-definition-YOQAKGHF-8Xn9QBNo.js} +1 -1
- package/dist/assets/{usecaseDiagram-VIAY4XPW-DEywfSfC.js → usecaseDiagram-VIAY4XPW-B6LxotkO.js} +1 -1
- package/dist/assets/{vennDiagram-BLWOH2XV-DN1iWw97.js → vennDiagram-BLWOH2XV-Ciex0LDC.js} +1 -1
- package/dist/assets/{wardleyDiagram-YMQ3BMBF-BAIfK1We.js → wardleyDiagram-YMQ3BMBF-B_TbpQlN.js} +1 -1
- package/dist/assets/{xychartDiagram-TAQBALBS-BLBD8FaQ.js → xychartDiagram-TAQBALBS-Dz_iIYt1.js} +1 -1
- package/dist/index.html +2 -2
- package/dist/rail.html +4 -4
- package/dist-server/index.js +44 -39
- package/dist-server/package.json +1 -1
- package/dist-server/server/limix-bot-library.js +9 -8
- package/dist-server/server/limix-lang.js +2 -0
- package/dist-server/server/limix-skill-hub.js +18 -17
- package/dist-server/server/limix-skill-store.js +12 -11
- package/dist-server/shared/first-reply-rescue.js +4 -3
- package/package.json +1 -1
- package/dist/assets/BotSettingsDialog-BUvuLu53.js +0 -16
- package/dist/assets/SettingsModal--yYGUYK2.js +0 -15
- package/dist/assets/TeamLibraryPanel-CwVaJVC_.js +0 -1
- package/dist/assets/channel-CsOUN1ZX.js +0 -1
- package/dist/assets/swimlanesDiagram-K3J5GTZL-BaaZdhur.js +0 -8
package/dist-server/index.js
CHANGED
|
@@ -29812,52 +29812,56 @@ return json7(res,400,{error:"botIds must be a non-empty array of distinct bot id
|
|
|
29812
29812
|
id)).map(bot=>bot.id);return person2&&deps.own.told(person2,shown3),json7(res,200,{botIds:shown3})}}function createBotLifecycleRoutes(deps){let{store:store5,interruptAllDirectThreads:interruptAllDirectThreads2,cancelDirectTurnDispatch:cancelDirectTurnDispatch2,revokeInternalCapabilitiesForThread:revokeInternalCapabilitiesForThread2,activeGroupTurnForBot:activeGroupTurnForBot2,cancelGroupTurnOperations:cancelGroupTurnOperations2,runningTurnInstance:runningTurnInstance2,closeOpenApprovals:closeOpenApprovals2,deleteBotWithLifecycle:deleteBotWithLifecycle2}=deps;return async({req,res,path:path6,method:method2,
|
|
29813
29813
|
json:json7})=>{let m3=null,routines2=deps.routines();if(method2==="POST"&&path6==="/api/local-computer/interrupt")return String(req.headers["content-type"]??"").toLowerCase().startsWith("application/json")?(await Promise.allSettled(store5.bots.filter(bot=>bot.computer==="local").map(async bot=>{await interruptAllDirectThreads2(bot.id);let routineRun=routines2.activeBotRunForBot(bot.id);routineRun&&(cancelDirectTurnDispatch2(bot.id,routineRun.threadId),routineRun.threadId&&revokeInternalCapabilitiesForThread2(
|
|
29814
29814
|
routineRun.threadId),await routines2.cancelRun(routineRun.id));let groupTurn=activeGroupTurnForBot2(bot.id);groupTurn&&(cancelGroupTurnOperations2(groupTurn.group.id,groupTurn.threadId),revokeInternalCapabilitiesForThread2(groupTurn.threadId),await runningTurnInstance2(bot,groupTurn.threadId)?.adapter.interruptTurn(groupTurn.threadId).catch(()=>{}),closeOpenApprovals2(groupTurn.threadId))})),json7(res,200,{ok:!0})):json7(res,415,{error:"content-type must be application/json"});if(m3=path6.match(
|
|
29815
|
-
/^\/api\/bots\/([\w-]+)$/),m3&&method2==="DELETE"){let result2=await deleteBotWithLifecycle2(m3[1]);return json7(res,result2.status,result2.body)}return PASS}}init_zod();init_mascot_bodies();init_wire();var TEAM_LIBRARY_RAW_ROOT="https://raw.githubusercontent.com/leomieo/limix-bot-teams/main",LIST_TTL_MS=600*1e3,MAX_CATALOG_BYTES=256e3,MAX_BOT_BYTES=64e3,CATEGORIES=["everyday","work","writing","design","sales","personal"],ID20=/^[a-z0-9][a-z0-9-]{0,63}$/,NOTE_PATH=/^(?:MEMORY\.md|memory\/[a-z0-9][a-z0-9-]{0,63}\.md)$/,TIME3=/^(?:[01]\d|2[0-3]):[0-5]\d$/,isRecord38=value3=>!!value3&&typeof value3=="object"&&!Array.isArray(value3);function text27(value3,field5,max4,allowEmpty=!1){
|
|
29816
|
-
|
|
29817
|
-
|
|
29818
|
-
|
|
29819
|
-
return value3.bots.forEach((raw,index4)=>{let field5=`bots[${index4}]`;try{if(!isRecord38(raw))throw new Error(`${field5} is invalid`);let id12=text27(raw.id,`${field5}.id`,64);if(!ID20.test(id12)||seen.has(id12))throw new Error(`${field5}.id is invalid`);let routines2=typeof raw.routines=="number"&&Number.isSafeInteger(raw.routines)&&raw.routines>=0&&raw.routines<=10?raw.routines:0;out.push({id:id12,name:text27(raw.name,`${field5}.name`,40),title:text27(
|
|
29820
|
-
raw.category,`${field5}.category`),description:text27(raw.description,`${field5}.description`,300),try:text27(raw.try,`${field5}.try`,200),look:look(raw.look,`${field5}.look`),apps:apps(raw.apps,`${field5}.apps`),routines:routines2}),seen.add(id12)}catch{}}),out}function parseLibraryBot(value3,expectedId){if(!isRecord38(value3)||value3.format!=="limix.bot"||value3.version!==1)throw new Error("T\u1EC7p bot m\u1EABu kh\xF4ng \u0111\xFAng \u0111\u1ECBnh d\u1EA1ng"
|
|
29821
|
-
if(id12!==expectedId)throw new Error("T\u1EC7p bot m\u1EABu kh\xF4ng kh\u1EDBp v\u1EDBi danh s\xE1ch");if(!Array.isArray(value3.tryAsking)||value3.tryAsking.length===0||value3.tryAsking.length>6)throw new Error("tryAsking is invalid");let bot={id:id12,category:category(value3.category,"category"),name:text27(value3.name,"name",40),title:text27(value3.title,"title",
|
|
29822
|
-
tryAsking.map((line6,index4)=>text27(line6,`tryAsking[${index4}]`,200)),apps:apps(value3.apps,"apps")};if(value3.notes!==void 0){if(!isRecord38(value3.notes)||Object.keys(value3.notes).length>6)throw new Error("notes is invalid");bot.notes={};for(let[path6,note2]of Object.entries(value3.notes)){if(!NOTE_PATH.test(path6))throw new Error(`notes path ${path6}
|
|
29815
|
+
/^\/api\/bots\/([\w-]+)$/),m3&&method2==="DELETE"){let result2=await deleteBotWithLifecycle2(m3[1]);return json7(res,result2.status,result2.body)}return PASS}}init_zod();init_mascot_bodies();init_wire();var lxs=(vi4,en4)=>/^vi/i.test(process.env.OMB_UI_LANGUAGE?.trim()??"")?vi4:en4;var TEAM_LIBRARY_RAW_ROOT="https://raw.githubusercontent.com/leomieo/limix-bot-teams/main",LIST_TTL_MS=600*1e3,MAX_CATALOG_BYTES=256e3,MAX_BOT_BYTES=64e3,CATEGORIES=["everyday","work","writing","design","sales","personal"],ID20=/^[a-z0-9][a-z0-9-]{0,63}$/,NOTE_PATH=/^(?:MEMORY\.md|memory\/[a-z0-9][a-z0-9-]{0,63}\.md)$/,TIME3=/^(?:[01]\d|2[0-3]):[0-5]\d$/,isRecord38=value3=>!!value3&&typeof value3=="object"&&!Array.isArray(value3);function text27(value3,field5,max4,allowEmpty=!1){if(typeof value3!=
|
|
29816
|
+
"string"||!allowEmpty&&!value3.trim())throw new Error(`${field5} is required`);let trimmed=value3.trim();if(trimmed.length>max4)throw new Error(`${field5} is too long`);return trimmed}function look(value3,field5){if(!isRecord38(value3))throw new Error(`${field5} is invalid`);let color6=text27(value3.color,`${field5}.color`,20),mascotBody=text27(value3.mascotBody,`${field5}.mascotBody`,20);if(!MAUS_COLOR_NAMES.includes(color6))throw new Error(`${field5}.color is not a known colour`);if(!MASCOT_BODY_IDS.
|
|
29817
|
+
includes(mascotBody))throw new Error(`${field5}.mascotBody is not a known shape`);return{color:color6,mascotBody}}function category(value3,field5){let found2=CATEGORIES.find(entry2=>entry2===value3);if(!found2)throw new Error(`${field5} is not a known category`);return found2}function apps(value3,field5){if(value3===void 0)return[];if(!Array.isArray(value3)||value3.length>10)throw new Error(`${field5} is invalid`);return value3.map((app,index4)=>{let slug3=text27(app,`${field5}[${index4}]`,40);if(!/^[a-z0-9][a-z0-9_-]*$/.
|
|
29818
|
+
test(slug3))throw new Error(`${field5}[${index4}] is invalid`);return slug3})}function parseBotListing(value3){if(!isRecord38(value3)||value3.format!=="openmaus.catalog"||value3.version!==1)throw new Error(lxs("Danh s\xE1ch m\u1EABu kh\xF4ng \u0111\xFAng \u0111\u1ECBnh d\u1EA1ng","The template list has the wrong format"));if(value3.bots===void 0)return[];if(!Array.isArray(value3.bots)||value3.bots.length>200)throw new Error(lxs("Danh s\xE1ch bot m\u1EABu kh\xF4ng h\u1EE3p l\u1EC7","The template \
|
|
29819
|
+
bot list is not valid"));let seen=new Set,out=[];return value3.bots.forEach((raw,index4)=>{let field5=`bots[${index4}]`;try{if(!isRecord38(raw))throw new Error(`${field5} is invalid`);let id12=text27(raw.id,`${field5}.id`,64);if(!ID20.test(id12)||seen.has(id12))throw new Error(`${field5}.id is invalid`);let routines2=typeof raw.routines=="number"&&Number.isSafeInteger(raw.routines)&&raw.routines>=0&&raw.routines<=10?raw.routines:0;out.push({id:id12,name:text27(raw.name,`${field5}.name`,40),title:text27(
|
|
29820
|
+
raw.title,`${field5}.title`,80),category:category(raw.category,`${field5}.category`),description:text27(raw.description,`${field5}.description`,300),try:text27(raw.try,`${field5}.try`,200),look:look(raw.look,`${field5}.look`),apps:apps(raw.apps,`${field5}.apps`),routines:routines2}),seen.add(id12)}catch{}}),out}function parseLibraryBot(value3,expectedId){if(!isRecord38(value3)||value3.format!=="limix.bot"||value3.version!==1)throw new Error(lxs("T\u1EC7p bot m\u1EABu kh\xF4ng \u0111\xFAng \u0111\u1ECBnh d\u1EA1ng",
|
|
29821
|
+
"The template bot file has the wrong format"));let id12=text27(value3.id,"id",64);if(id12!==expectedId)throw new Error(lxs("T\u1EC7p bot m\u1EABu kh\xF4ng kh\u1EDBp v\u1EDBi danh s\xE1ch","The template bot file does not match the list"));if(!Array.isArray(value3.tryAsking)||value3.tryAsking.length===0||value3.tryAsking.length>6)throw new Error("tryAsking is invalid");let bot={id:id12,category:category(value3.category,"category"),name:text27(value3.name,"name",40),title:text27(value3.title,"title",
|
|
29822
|
+
80),description:text27(value3.description,"description",300),soul:text27(value3.soul,"soul",8e3),look:look(value3.look,"look"),tryAsking:value3.tryAsking.map((line6,index4)=>text27(line6,`tryAsking[${index4}]`,200)),apps:apps(value3.apps,"apps")};if(value3.notes!==void 0){if(!isRecord38(value3.notes)||Object.keys(value3.notes).length>6)throw new Error("notes is invalid");bot.notes={};for(let[path6,note2]of Object.entries(value3.notes)){if(!NOTE_PATH.test(path6))throw new Error(`notes path ${path6}\
|
|
29823
|
+
is not allowed`);bot.notes[path6]=text27(note2,`notes.${path6}`,8e3)+`
|
|
29823
29824
|
`}}if(value3.routines!==void 0){if(!Array.isArray(value3.routines)||value3.routines.length>5)throw new Error("routines is invalid");bot.routines=value3.routines.map((raw,index4)=>{let field5=`routines[${index4}]`;if(!isRecord38(raw)||!isRecord38(raw.schedule)||raw.schedule.type!=="daily")throw new Error(`${field5} is invalid`);let time6=text27(raw.schedule.time,`${field5}.schedule.time`,5);if(!TIME3.test(time6))throw new Error(`${field5}.schedule.time is invalid`);let weekdays3=raw.schedule.weekdays;
|
|
29824
29825
|
if(!Array.isArray(weekdays3)||weekdays3.length===0||weekdays3.length>7||weekdays3.some(day2=>!Number.isInteger(day2)||day2<0||day2>6))throw new Error(`${field5}.schedule.weekdays is invalid`);return{name:text27(raw.name,`${field5}.name`,80),prompt:text27(raw.prompt,`${field5}.prompt`,2e3),schedule:{type:"daily",time:time6,weekdays:[...new Set(weekdays3)].sort()},enabled:!1,overlap:"skip"}})}if(value3.skills!==void 0){if(!Array.isArray(value3.skills)||value3.skills.length>20||value3.skills.some(name6=>typeof name6!=
|
|
29825
|
-
"string"||!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name6)||name6.length>64))throw new Error("skills is invalid");bot.skills=[...new Set(value3.skills)]}return bot}async function fetchJson2(url2,maxBytes){let response=await fetch(url2,{headers:{accept:"application/json, text/plain;q=0.9"},redirect:"error",signal:AbortSignal.timeout(1e4)});if(!response.ok)throw new Error(`GitHub tr\u1EA3 l\u1ED7i HTTP ${response.status}`);let raw=await response.text();if(Buffer.
|
|
29826
|
-
T\u1EC7p m\u1EABu qu\xE1 l\u1EDBn");try{return JSON.parse(raw)}catch{throw new Error("GitHub kh\xF4ng tr\u1EA3 v\u1EC1 JSON h\u1EE3p l\u1EC7")}}var cached4=null;async function listing2(fresh=!1){if(!fresh&&cached4&&Date.now()-cached4.at<LIST_TTL_MS)return cached4.bots;try{let bots=parseBotListing(await fetchJson2(`${TEAM_LIBRARY_RAW_ROOT}/catalog.json${fresh?`?t=${Date.now()}`:
|
|
29827
|
-
async function handleLimixBotLibrary(method2,path6){try{if(method2==="POST"&&path6==="/api/limix/bots/refresh")return{status:200,body:{bots:await listing2(!0)}};if(method2!=="GET")return null;if(path6==="/api/limix/bots")return{status:200,body:{bots:await listing2()}};let match5=path6.match(/^\/api\/limix\/bots\/([a-z0-9][a-z0-9-]{0,63})$/);if(!match5)return null;let id12=match5[1];
|
|
29828
|
-
/bots/${id12}.json?t=${Date.now()}`,MAX_BOT_BYTES),id12)}}:{status:404,body:{error:"Kh\xF4ng c\xF3 bot m\u1EABu n\xE0y"}}}catch(error66){return{status:502,body:{error:error66 instanceof Error?error66.message:String(error66)}}}}init_new_bot_defaults();import{lstatSync as lstatSync45,readdirSync as readdirSync58,readFileSync as readFileSync185,statSync as statSync47}from"node:fs";import{basename as basename34,isAbsolute as isAbsolute41,join as join255}from"node:path";var MAX_ENTRIES2=400,MAX_DEPTH5=8;function listEntries(root3){let out=[],walk6=(dir,prefix,depth2)=>{if(!(depth2>MAX_DEPTH5||out.length>MAX_ENTRIES2))for(let name6 of readdirSync58(dir)){if(out.length>MAX_ENTRIES2)return;let absolute=join255(dir,name6),path6=prefix?`${prefix}/${name6}`:name6,info2=lstatSync45(absolute);info2.isSymbolicLink()?out.push({path:path6,kind:"link",size:0,absolute}):info2.isDirectory()?(out.push({path:path6,kind:"folder",size:0,absolute}),walk6(absolute,path6,depth2+1)):
|
|
29826
|
+
"string"||!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name6)||name6.length>64))throw new Error("skills is invalid");bot.skills=[...new Set(value3.skills)]}return bot}async function fetchJson2(url2,maxBytes){let response=await fetch(url2,{headers:{accept:"application/json, text/plain;q=0.9"},redirect:"error",signal:AbortSignal.timeout(1e4)});if(!response.ok)throw new Error(lxs(`GitHub tr\u1EA3 l\u1ED7i HTTP ${response.status}`,`GitHub returned HTTP ${response.status}`));let raw=await response.text();if(Buffer.
|
|
29827
|
+
byteLength(raw)>maxBytes)throw new Error(lxs("T\u1EC7p m\u1EABu qu\xE1 l\u1EDBn","The template file is too large"));try{return JSON.parse(raw)}catch{throw new Error(lxs("GitHub kh\xF4ng tr\u1EA3 v\u1EC1 JSON h\u1EE3p l\u1EC7","GitHub did not return valid JSON"))}}var cached4=null;async function listing2(fresh=!1){if(!fresh&&cached4&&Date.now()-cached4.at<LIST_TTL_MS)return cached4.bots;try{let bots=parseBotListing(await fetchJson2(`${TEAM_LIBRARY_RAW_ROOT}/catalog.json${fresh?`?t=${Date.now()}`:
|
|
29828
|
+
""}`,MAX_CATALOG_BYTES));return cached4={at:Date.now(),bots},bots}catch(error66){if(cached4)return cached4.bots;throw error66}}async function handleLimixBotLibrary(method2,path6){try{if(method2==="POST"&&path6==="/api/limix/bots/refresh")return{status:200,body:{bots:await listing2(!0)}};if(method2!=="GET")return null;if(path6==="/api/limix/bots")return{status:200,body:{bots:await listing2()}};let match5=path6.match(/^\/api\/limix\/bots\/([a-z0-9][a-z0-9-]{0,63})$/);if(!match5)return null;let id12=match5[1];
|
|
29829
|
+
return(await listing2()).some(bot=>bot.id===id12)?{status:200,body:{bot:parseLibraryBot(await fetchJson2(`${TEAM_LIBRARY_RAW_ROOT}/bots/${id12}.json?t=${Date.now()}`,MAX_BOT_BYTES),id12)}}:{status:404,body:{error:lxs("Kh\xF4ng c\xF3 bot m\u1EABu n\xE0y","No such template bot")}}}catch(error66){return{status:502,body:{error:error66 instanceof Error?error66.message:String(error66)}}}}init_new_bot_defaults();import{lstatSync as lstatSync45,readdirSync as readdirSync58,readFileSync as readFileSync185,statSync as statSync47}from"node:fs";import{basename as basename34,isAbsolute as isAbsolute41,join as join255}from"node:path";var MAX_ENTRIES2=400,MAX_DEPTH5=8;function listEntries(root3){let out=[],walk6=(dir,prefix,depth2)=>{if(!(depth2>MAX_DEPTH5||out.length>MAX_ENTRIES2))for(let name6 of readdirSync58(dir)){if(out.length>MAX_ENTRIES2)return;let absolute=join255(dir,name6),path6=prefix?`${prefix}/${name6}`:name6,info2=lstatSync45(absolute);info2.isSymbolicLink()?out.push({path:path6,kind:"link",size:0,absolute}):info2.isDirectory()?(out.push({path:path6,kind:"folder",size:0,absolute}),walk6(absolute,path6,depth2+1)):
|
|
29829
29830
|
info2.isFile()&&out.push({path:path6,kind:"file",size:info2.size,executable:process.platform!=="win32"&&(info2.mode&73)!==0,absolute})}};return walk6(root3,"",1),out}function readOne(dir){let{take:take2,skipped:skipped2}=planSkillFolder(listEntries(dir));if(take2[0]?.path!=="SKILL.md")throw new Error(`no SKILL.md in ${basename34(dir)}`);let files2=take2.filter(entry2=>entry2.kind==="file").map(entry2=>({path:entry2.path,content:readFileSync185(entry2.absolute),executable:entry2.executable===!0}));
|
|
29830
29831
|
return{source:`local:${basename34(dir)}`,files:files2,skipped:skipped2}}function readLocalSkills(folder3){try{if(!isAbsolute41(folder3)||!statSync47(folder3).isDirectory())return{error:"Choose a folder on this computer."};let dirs=[],has2=dir=>{try{return statSync47(join255(dir,"SKILL.md")).isFile()}catch{return!1}};if(has2(folder3))dirs.push(folder3);else for(let name6 of readdirSync58(folder3)){let sub=join255(folder3,name6);try{statSync47(sub).isDirectory()&&has2(sub)&&dirs.push(sub)}catch{}}
|
|
29831
29832
|
return dirs.length?{skills:dirs.slice(0,50).map(readOne)}:{error:"No SKILL.md found in that folder or in its subfolders."}}catch(error66){return{error:error66 instanceof Error?error66.message:"Could not read that folder."}}}import{createHash as createHash117}from"node:crypto";import{existsSync as existsSync99,mkdirSync as mkdirSync121,readFileSync as readFileSync186}from"node:fs";import{join as join256}from"node:path";init_atomic();init_config3();var HUB_REPO="leomieo/limix-skill-hub",HUB_BRANCH="main",SYNC_MIN_GAP_MS=1200*60*1e3,FILE_MAX_BYTES3=1024*1024,DEFAULT_SETTINGS={autoSync:!0,autoInstall:!1,lastSync:null,lastError:null,etag:null},hubDir=()=>join256(DATA_DIR,"skill-hub"),settingsFile=()=>join256(hubDir(),"settings.json"),catalogFile=()=>join256(hubDir(),"catalog.json");function readJson6(file3,fallback2){try{return JSON.parse(readFileSync186(file3,"utf8"))}catch{return fallback2}}function hubSettings(){return{...DEFAULT_SETTINGS,
|
|
29832
29833
|
...readJson6(settingsFile(),{})}}function saveSettings(next){mkdirSync121(hubDir(),{recursive:!0,mode:448}),writeFileAtomic(settingsFile(),JSON.stringify(next,null,2),{mode:384})}function hubCatalog(){let body2=readJson6(catalogFile(),{});return Array.isArray(body2.skills)?body2.skills:[]}var rawUrl=path6=>`https://raw.githubusercontent.com/${HUB_REPO}/${HUB_BRANCH}/${path6}`;async function fetchText(url2,headers={}){return fetch(url2,{signal:AbortSignal.timeout(3e4),headers:{"User-Agent":"limix\
|
|
29833
|
-
-bot",...headers}})}var syncing=null;function syncHub(){return syncing||(syncing=(async()=>{let settings=hubSettings();try{let response=await fetchText(rawUrl("registry/catalog.json"),settings.etag&&existsSync99(catalogFile())?{"If-None-Match":settings.etag}:{});if(response.status===304)return saveSettings({...settings,lastSync:new Date().toISOString(),lastError:null}),{changed:!1,count:hubCatalog().length};if(!response.ok)throw new Error(`Hub tr\u1EA3 v\u1EC1 ${response.status}
|
|
29834
|
-
text(),parsed2=JSON.parse(text31);if(!Array.isArray(parsed2.skills))throw new Error("Danh m\u1EE5c Hub kh\xF4ng \u0111\xFAng \u0111\u1ECBnh d\u1EA1ng");return mkdirSync121(hubDir(),{recursive:!0,mode:448}),writeFileAtomic(catalogFile(),text31,{mode:384}),saveSettings({...settings,etag:response.headers.get("etag"),lastSync:new Date().toISOString(),lastError:null}),settings.autoInstall&&await autoInstallEligible(),
|
|
29835
|
-
cause.message:String(cause);return saveSettings({...settings,lastError:message8}),{error:message8}}finally{syncing=null}})(),syncing)}var SCRIPT4=/\.(py|js|mjs|cjs|ts|sh|bash|zsh|ps1|bat|cmd|rb|pl|php|go|rs|exe|dll|so|dylib|bin|jar|class|scr|msi|com|apk|dex|node|wasm)$/i,MCP_FILE=/(^|\/)(\.mcp\.json|mcp\.json|mcp-servers?\.json|claude_desktop_config\.json)$/i,isToolFile=path6=>SCRIPT4.test(path6)||MCP_FILE.
|
|
29836
|
-
return entry2.area==="quarantine"||entry2.security_status==="quarantined"?"Skill n\u1EB1m trong khu c\xE1ch ly v\xEC c\xF3 m\xE3 nguy hi\u1EC3m.":entry2.approval_status==="quarantined"||entry2.approval_status==="rejected"?"Skill b\u1ECB t\u1EEB ch\u1ED1i.":null}function installedFromHub(){let out=new Map;
|
|
29837
|
-
reviewState:entry2.reviewState})}return out}function hubSkills(){let installed2=installedFromHub(),library=readSkillLibraryIndex();return hubCatalog().map(entry2=>{let have=installed2.get(entry2.id);return{...entry2,installed:!!have,enabled:have?.reviewState==="approved",updateAvailable:!!(have&&have.checksum!==
|
|
29838
|
-
async function installHubSkill(id12,options2={}){if(!isSkillName(id12))return{error:"T\xEAn k\u1EF9 n\u0103ng kh\xF4ng h\u1EE3p l\u1EC7"};let entry2=hubCatalog().find(e=>e.id===id12);if(!entry2)
|
|
29839
|
-
return{error:"
|
|
29840
|
-
let
|
|
29841
|
-
|
|
29842
|
-
{
|
|
29843
|
-
if(
|
|
29844
|
-
result2?{error:"K\u1EF9 n\u0103ng \
|
|
29845
|
-
|
|
29846
|
-
|
|
29847
|
-
|
|
29848
|
-
|
|
29834
|
+
-bot",...headers}})}var syncing=null;function syncHub(){return syncing||(syncing=(async()=>{let settings=hubSettings();try{let response=await fetchText(rawUrl("registry/catalog.json"),settings.etag&&existsSync99(catalogFile())?{"If-None-Match":settings.etag}:{});if(response.status===304)return saveSettings({...settings,lastSync:new Date().toISOString(),lastError:null}),{changed:!1,count:hubCatalog().length};if(!response.ok)throw new Error(lxs(`Hub tr\u1EA3 v\u1EC1 ${response.status}`,`The hub re\
|
|
29835
|
+
turned ${response.status}`));let text31=await response.text(),parsed2=JSON.parse(text31);if(!Array.isArray(parsed2.skills))throw new Error(lxs("Danh m\u1EE5c Hub kh\xF4ng \u0111\xFAng \u0111\u1ECBnh d\u1EA1ng","The hub catalog has the wrong format"));return mkdirSync121(hubDir(),{recursive:!0,mode:448}),writeFileAtomic(catalogFile(),text31,{mode:384}),saveSettings({...settings,etag:response.headers.get("etag"),lastSync:new Date().toISOString(),lastError:null}),settings.autoInstall&&await autoInstallEligible(),
|
|
29836
|
+
{changed:!0,count:parsed2.skills.length}}catch(cause){let message8=cause instanceof Error?cause.message:String(cause);return saveSettings({...settings,lastError:message8}),{error:message8}}finally{syncing=null}})(),syncing)}var SCRIPT4=/\.(py|js|mjs|cjs|ts|sh|bash|zsh|ps1|bat|cmd|rb|pl|php|go|rs|exe|dll|so|dylib|bin|jar|class|scr|msi|com|apk|dex|node|wasm)$/i,MCP_FILE=/(^|\/)(\.mcp\.json|mcp\.json|mcp-servers?\.json|claude_desktop_config\.json)$/i,isToolFile=path6=>SCRIPT4.test(path6)||MCP_FILE.
|
|
29837
|
+
test(path6),sha=data4=>createHash117("sha256").update(data4).digest("hex");function hubSkillBlocked(entry2){return entry2.area==="quarantine"||entry2.security_status==="quarantined"?lxs("Skill n\u1EB1m trong khu c\xE1ch ly v\xEC c\xF3 m\xE3 nguy hi\u1EC3m.","This skill is quarantined because it contains dangerous code."):entry2.approval_status==="quarantined"||entry2.approval_status==="rejected"?lxs("Skill b\u1ECB t\u1EEB ch\u1ED1i.","This skill was rejected."):null}function installedFromHub(){let out=new Map;
|
|
29838
|
+
for(let entry2 of Object.values(readSkillLibraryIndex())){let match5=/^limix-hub:([a-z0-9-]+)@([0-9a-f]{64})/.exec(entry2.source);match5&&out.set(match5[1],{checksum:match5[2],sha256:entry2.sha256,reviewState:entry2.reviewState})}return out}function hubSkills(){let installed2=installedFromHub(),library=readSkillLibraryIndex();return hubCatalog().map(entry2=>{let have=installed2.get(entry2.id);return{...entry2,installed:!!have,enabled:have?.reviewState==="approved",updateAvailable:!!(have&&have.checksum!==
|
|
29839
|
+
entry2.checksum),blocked:!have&&library[entry2.id]?lxs("Th\u01B0 vi\u1EC7n \u0111\xE3 c\xF3 k\u1EF9 n\u0103ng c\xF9ng t\xEAn kh\xF4ng \u0111\u1EBFn t\u1EEB Hub.","The library already has a skill with this name that did not come from the hub."):null,risky:hubSkillBlocked(entry2)}})}async function installHubSkill(id12,options2={}){if(!isSkillName(id12))return{error:lxs("T\xEAn k\u1EF9 n\u0103ng kh\xF4ng h\u1EE3p l\u1EC7","Invalid skill name")};let entry2=hubCatalog().find(e=>e.id===id12);if(!entry2)
|
|
29840
|
+
return{error:lxs("Kh\xF4ng th\u1EA5y k\u1EF9 n\u0103ng n\xE0y trong danh m\u1EE5c Hub. H\xE3y \u0111\u1ED3ng b\u1ED9 l\u1EA1i.","This skill is not in the hub catalog. Sync again.")};let blocked=hubSkillBlocked(entry2);if(blocked&&!options2.allowRisky)return{error:blocked};let installed2=installedFromHub().get(id12);if(readSkillLibraryIndex()[id12]&&!installed2)return{error:lxs("Th\u01B0 vi\u1EC7n \u0111\xE3 c\xF3 k\u1EF9 n\u0103ng c\xF9ng t\xEAn kh\xF4ng \u0111\u1EBFn t\u1EEB Hub.","The library \
|
|
29841
|
+
already has a skill with this name that did not come from the hub.")};let wanted=Object.keys(entry2.file_hashes).filter(path6=>path6!=="SKILL.md"&&(options2.allowTools||!isToolFile(path6))),skipped2=Object.keys(entry2.file_hashes).filter(path6=>path6!=="SKILL.md"&&!wanted.includes(path6)),files2=[],skillMd="";for(let path6 of["SKILL.md",...wanted]){let expected=entry2.file_hashes[path6];if(!expected)return{error:lxs(`Danh m\u1EE5c thi\u1EBFu m\xE3 ki\u1EC3m tra cho ${path6}`,`The catalog has no \
|
|
29842
|
+
checksum for ${path6}`)};let response;try{response=await fetchText(rawUrl(`${entry2.area==="quarantine"?"quarantine":"skills"}/${id12}/${path6}`))}catch(cause){return{error:lxs(`Kh\xF4ng t\u1EA3i \u0111\u01B0\u1EE3c ${path6}: `,`Couldn't download ${path6}: `)+(cause instanceof Error?cause.message:String(cause))}}if(!response.ok)return{error:lxs(`Kh\xF4ng t\u1EA3i \u0111\u01B0\u1EE3c ${path6} (${response.status})`,`Couldn't download ${path6} (${response.status})`)};let bytes4=new Uint8Array(await response.
|
|
29843
|
+
arrayBuffer());if(bytes4.byteLength>FILE_MAX_BYTES3)return{error:lxs(`${path6} qu\xE1 l\u1EDBn`,`${path6} is too large`)};if(sha(bytes4)!==expected)return{error:lxs(`${path6} kh\xF4ng kh\u1EDBp m\xE3 SHA-256 trong danh m\u1EE5c \u2014 \u0111\xE3 d\u1EEBng c\xE0i`,`${path6} does not match the SHA-256 in the catalog \u2014 install stopped`)};path6==="SKILL.md"?skillMd=new TextDecoder().decode(bytes4):files2.push({path:path6,content:bytes4})}if(installed2){let retired=retireLibrarySkill(id12,installed2.
|
|
29844
|
+
sha256);if("error"in retired)return{error:retired.error};if("stale"in retired)return{error:lxs("K\u1EF9 n\u0103ng \u0111\xE3 b\u1ECB s\u1EEDa tay sau khi c\xE0i, kh\xF4ng c\u1EADp nh\u1EADt \u0111\xE8.","This skill was edited by hand after installing, so it is not overwritten.")}}let result2=installLibrarySkill({name:id12,instructions:skillMd,source:`limix-hub:${id12}@${entry2.checksum} (${entry2.source.repository??"?"}@${(entry2.source.commit??"").slice(0,7)})`,...entry2.license?{license:entry2.
|
|
29845
|
+
license}:{},...entry2.compatibility.text?{compatibility:entry2.compatibility.text}:{},...entry2.tags.length?{tags:entry2.tags}:{},warnings:entry2.notes,skippedFiles:skipped2,reviewState:"approved",...files2.length?{files:files2}:{}});return"error"in result2?{error:result2.error}:{ok:!0,skipped:skipped2}}function setHubSkillEnabled(id12,enabled){if(!installedFromHub().has(id12))return{error:lxs("K\u1EF9 n\u0103ng n\xE0y ch\u01B0a c\xE0i t\u1EEB Hub","This skill was not installed from the hub")};let result2=setLibrarySkillReviewState(
|
|
29846
|
+
id12,enabled?"approved":"disabled");return"error"in result2?{error:result2.error}:{ok:!0}}function removeHubSkill(id12){let installed2=installedFromHub().get(id12);if(!installed2)return{error:lxs("K\u1EF9 n\u0103ng n\xE0y ch\u01B0a c\xE0i t\u1EEB Hub","This skill was not installed from the hub")};let result2=retireLibrarySkill(id12,installed2.sha256);return"error"in result2?{error:result2.error}:"stale"in result2?{error:lxs("K\u1EF9 n\u0103ng \u0111\xE3 b\u1ECB s\u1EEDa tay sau khi c\xE0i, kh\xF4ng g\u1EE1 t\u1EF1 \u0111\u1ED9ng.",
|
|
29847
|
+
"This skill was edited by hand after installing, so it is not removed automatically.")}:{ok:!0}}async function autoInstallEligible(){let budget=5;for(let skill of hubSkills()){if(budget<=0)return;if(!(!skill.blocked&&!skill.risky&&skill.kind==="instructions-only"&&skill.approval_status==="approved"&&(skill.grade==="excellent"||skill.grade==="recommended"))||skill.installed&&!skill.updateAvailable)continue;budget-=1,"ok"in await installHubSkill(skill.id)&&skill.updateAvailable&&skill.enabled&&setHubSkillEnabled(
|
|
29848
|
+
skill.id,!0)}}async function handleLimixHub(method2,path6,body2){let record31=body2&&typeof body2=="object"?body2:{};if(method2==="GET"&&path6==="/api/limix/hub")return{status:200,body:{repo:HUB_REPO,settings:hubSettings(),skills:hubSkills()}};if(method2==="POST"&&path6==="/api/limix/hub/sync"){let result2=await syncHub();return"error"in result2?{status:502,body:result2}:{status:200,body:result2}}if(method2==="POST"&&path6==="/api/limix/hub/settings"){let current3=hubSettings();return saveSettings(
|
|
29849
|
+
{...current3,autoSync:typeof record31.autoSync=="boolean"?record31.autoSync:current3.autoSync,autoInstall:typeof record31.autoInstall=="boolean"?record31.autoInstall:current3.autoInstall}),{status:200,body:{settings:hubSettings()}}}let id12=typeof record31.id=="string"?record31.id:"";if(method2==="POST"&&(path6==="/api/limix/hub/install"||path6==="/api/limix/hub/update")){let result2=await installHubSkill(id12,{allowTools:record31.allowTools===!0,allowRisky:record31.allowRisky===!0});return"erro\
|
|
29850
|
+
r"in result2?{status:422,body:result2}:{status:200,body:result2}}if(method2==="POST"&&path6==="/api/limix/hub/enable"){let result2=setHubSkillEnabled(id12,record31.enabled===!0);return"error"in result2?{status:422,body:result2}:{status:200,body:result2}}if(method2==="POST"&&path6==="/api/limix/hub/remove"){let result2=removeHubSkill(id12);return"error"in result2?{status:422,body:result2}:{status:200,body:result2}}return null}var started2=!1;function startHubAutoSync(){if(started2)return;started2=
|
|
29851
|
+
!0;let tick2=()=>{let settings=hubSettings();settings.autoSync&&(settings.lastSync&&Date.now()-Date.parse(settings.lastSync)<SYNC_MIN_GAP_MS||syncHub())};setTimeout(tick2,6e4).unref(),setInterval(tick2,1800*1e3).unref()}import{existsSync as existsSync100,mkdirSync as mkdirSync122,readFileSync as readFileSync187}from"node:fs";import{join as join257}from"node:path";init_atomic();init_config3();var LIMIX_CORE_SKILLS=new Set(["create-verification-skill","agent-reach","windows-computer-automation","phone-connection","phone-harness"]),BOT_ID8=/^[\w-]+$/,picksPath=botId=>join257(DATA_DIR,"skill-state",botId,"limix-bundled.json");function bundledPicks(botId){if(!BOT_ID8.test(botId))return new Set;try{let parsed2=JSON.parse(readFileSync187(picksPath(botId),"utf8"));return new Set(Array.isArray(parsed2)?parsed2.filter(name6=>typeof name6=="string"&&isSkillName(name6)):[])}catch{return new Set}}
|
|
29849
29852
|
function setBundledPick(botId,id12,on3){if(!BOT_ID8.test(botId)||!isSkillName(id12))return;let picks2=bundledPicks(botId);picks2.has(id12)!==on3&&(on3?picks2.add(id12):picks2.delete(id12),mkdirSync122(join257(DATA_DIR,"skill-state",botId),{recursive:!0,mode:448}),writeFileAtomic(picksPath(botId),`${JSON.stringify([...picks2].sort(),null,2)}
|
|
29850
29853
|
`,{mode:384}))}function limixBundledFor(botId,skills){let picks2=bundledPicks(botId);return skills.filter(({manifest})=>LIMIX_CORE_SKILLS.has(manifest.id)||picks2.has(manifest.id))}var slashCalled=(text31,id12)=>new RegExp(`(^|\\s)/${id12.replace(/[-]/g,"\\-")}(?=$|[\\s.,:;!?])`,"m").test(text31);function limixSelectBundled(text31,capabilities,skills){let available=new Set(capabilities),called=skills.filter(({manifest})=>manifest.requiredCapabilities.every(capability2=>available.has(capability2))&&
|
|
29851
|
-
slashCalled(text31,manifest.id));return mergeSkills(selectBundledSkills(text31,available,skills),called)}function limixBundledListings(botId){return limixBundledFor(botId,loadBundledSkills()).map(({manifest})=>({name:manifest.id,description:manifest.description,enabled:!0,editable:!1,source:LIMIX_CORE_SKILLS.has(manifest.id)?"K\u0129 n\u0103ng g\u1ED1c c\u1EE7a Limix Bot":"Kho K\u0129 N\u0103ng (Skills) \xB7 c\xF3 s\u1EB5n",
|
|
29852
|
-
has(manifest.id)}))}function limixBundledSwitch(botId,name6,enabled){return loadBundledSkills().some(({manifest})=>manifest.id===name6)?LIMIX_CORE_SKILLS.has(name6)?enabled?{status:200,body:{skill:limixBundledListings(botId).find(skill=>skill.name===name6)}}:{status:409,body:{error:"K\u0129 n\u0103ng g\u1ED1c c\u1EE7a Limix Bot lu\xF4n b\u1EADt cho m\u1ECDi bot."
|
|
29853
|
-
enabled}}}):null}function storeItems(bots,botId){let bot=bots.bot(botId),assigned=new Set(bot.assignedSkills??[]),picks2=bundledPicks(botId),items=new Map;for(let{manifest}of loadBundledSkills()){let core=LIMIX_CORE_SKILLS.has(manifest.id);items.set(manifest.id,{name:manifest.id,description:manifest.description,kind:"bundled",added:core||picks2.has(manifest.
|
|
29854
|
-
let intact=!skill.warnings.some(warning=>warning.includes("enablement is blocked"));items.set(skill.name,{name:skill.name,description:skill.description,kind:"library",added:assigned.has(skill.name)&&skill.enabled,core:!1,ready:intact,warnings:skill.warnings.length,source:skill.source.startsWith("limix-hub:")?"Th\u01B0 vi\u1EC7n k\u0129 n\
|
|
29855
|
-
{name:skill.name,description:skill.description,kind:"imported",added:skill.enabled,core:!1,ready:!0,warnings:skill.warnings.length,source:skill.source||"T\u1EEB m\xE1y t\xEDnh n\xE0y"});return[...items.values()].sort((a,b11)=>Number(b11.core)-Number(a.core)||a.name.
|
|
29856
|
-
let item=storeItems(bots,botId).find(entry2=>entry2.name===name6&&(!kind2||entry2.kind===kind2));if(!item)return{error
|
|
29857
|
-
|
|
29858
|
-
|
|
29859
|
-
|
|
29860
|
-
|
|
29854
|
+
slashCalled(text31,manifest.id));return mergeSkills(selectBundledSkills(text31,available,skills),called)}function limixBundledListings(botId){return limixBundledFor(botId,loadBundledSkills()).map(({manifest})=>({name:manifest.id,description:manifest.description,enabled:!0,editable:!1,source:LIMIX_CORE_SKILLS.has(manifest.id)?lxs("K\u0129 n\u0103ng g\u1ED1c c\u1EE7a Limix Bot","Limix Bot core skill"):lxs("Kho K\u0129 N\u0103ng (Skills) \xB7 c\xF3 s\u1EB5n","Skills Store \xB7 built in"),sha256:"",
|
|
29855
|
+
importedAt:"",warnings:[],skippedFiles:[],origin:"bundled",core:LIMIX_CORE_SKILLS.has(manifest.id)}))}function limixBundledSwitch(botId,name6,enabled){return loadBundledSkills().some(({manifest})=>manifest.id===name6)?LIMIX_CORE_SKILLS.has(name6)?enabled?{status:200,body:{skill:limixBundledListings(botId).find(skill=>skill.name===name6)}}:{status:409,body:{error:lxs("K\u0129 n\u0103ng g\u1ED1c c\u1EE7a Limix Bot lu\xF4n b\u1EADt cho m\u1ECDi bot.","Limix Bot core skills are always on for every b\
|
|
29856
|
+
ot.")}}:(setBundledPick(botId,name6,enabled),{status:200,body:{skill:{...limixBundledListings(botId).find(skill=>skill.name===name6),name:name6,enabled}}}):null}function storeItems(bots,botId){let bot=bots.bot(botId),assigned=new Set(bot.assignedSkills??[]),picks2=bundledPicks(botId),items=new Map;for(let{manifest}of loadBundledSkills()){let core=LIMIX_CORE_SKILLS.has(manifest.id);items.set(manifest.id,{name:manifest.id,description:manifest.description,kind:"bundled",added:core||picks2.has(manifest.
|
|
29857
|
+
id),core,ready:!0,warnings:0,source:lxs("C\xF3 s\u1EB5n trong Limix Bot","Built into Limix Bot")})}for(let skill of listLibrarySkills()){if(items.has(skill.name))continue;let intact=!skill.warnings.some(warning=>warning.includes("enablement is blocked"));items.set(skill.name,{name:skill.name,description:skill.description,kind:"library",added:assigned.has(skill.name)&&skill.enabled,core:!1,ready:intact,warnings:skill.warnings.length,source:skill.source.startsWith("limix-hub:")?lxs("Th\u01B0 vi\u1EC7n k\u0129 n\u0103\
|
|
29858
|
+
ng Limix","Limix skill library"):lxs("\u0110\xE3 c\xE0i trong C\xE0i \u0111\u1EB7t","Installed in Settings")})}for(let skill of importedSkillListings(botId,new Set))items.has(skill.name)||items.set(skill.name,{name:skill.name,description:skill.description,kind:"imported",added:skill.enabled,core:!1,ready:!0,warnings:skill.warnings.length,source:skill.source||lxs("T\u1EEB m\xE1y t\xEDnh n\xE0y","From this computer")});return[...items.values()].sort((a,b11)=>Number(b11.core)-Number(a.core)||a.name.
|
|
29859
|
+
localeCompare(b11.name))}function limixStoreSet(bots,botId,name6,add11,kind2){let bot=bots.bot(botId);if(!bot)return{error:lxs("Kh\xF4ng t\xECm th\u1EA5y bot.","Bot not found.")};if(!isSkillName(name6))return{error:lxs("T\xEAn k\u0129 n\u0103ng kh\xF4ng h\u1EE3p l\u1EC7.","Invalid skill name.")};let item=storeItems(bots,botId).find(entry2=>entry2.name===name6&&(!kind2||entry2.kind===kind2));if(!item)return{error:lxs(`Kho ch\u01B0a c\xF3 k\u0129 n\u0103ng "${name6}".`,`The store has no skill "${name6}\
|
|
29860
|
+
".`)};if(item.kind==="bundled")return item.core?add11?{ok:!0}:{error:lxs("K\u0129 n\u0103ng g\u1ED1c c\u1EE7a Limix Bot lu\xF4n b\u1EADt cho m\u1ECDi bot.","Limix Bot core skills are always on for every bot.")}:(setBundledPick(botId,name6,add11),{ok:!0});if(item.kind==="imported"){let result3=setImportedSkillEnabled(botId,name6,add11);return"error"in result3?{error:result3.error}:{ok:!0}}let assigned=bot.assignedSkills??[];if(add11){let listing3=listLibrarySkills().find(skill=>skill.name===name6);
|
|
29861
|
+
if(listing3&&!listing3.enabled){let approved=setLibrarySkillReviewState(name6,"approved");if("error"in approved)return{error:approved.error}}return setLibrarySkillOptOut(botId,name6,!1),assigned.includes(name6)||bots.patchBot(botId,{assignedSkills:[...assigned,name6]}),{ok:!0}}let result2=unassignLibrarySkill(botId,name6,assigned);return"unassigned"in result2&&result2.assignedSkills&&bots.patchBot(botId,{assignedSkills:result2.assignedSkills}),{ok:!0}}function handleLimixSkillStore(bots,method2,path6,body2){
|
|
29862
|
+
let m3=path6.match(/^\/api\/limix\/skill-store\/([\w-]+)$/);if(!m3)return null;let botId=m3[1];if(!bots.bot(botId))return{status:404,body:{error:lxs("Kh\xF4ng t\xECm th\u1EA5y bot.","Bot not found.")}};if(method2==="GET")return{status:200,body:{skills:storeItems(bots,botId)}};if(method2==="POST"){let raw=body2&&typeof body2=="object"?body2:{},kind2=raw.kind==="bundled"||raw.kind==="library"||raw.kind==="imported"?raw.kind:void 0;if(typeof raw.name!="string"||typeof raw.add!="boolean")return{status:400,
|
|
29863
|
+
body:{error:lxs("C\u1EA7n t\xEAn k\u0129 n\u0103ng v\xE0 add.","A skill name and add are required.")}};let result2=limixStoreSet(bots,botId,raw.name,raw.add,kind2);return"error"in result2?{status:422,body:result2}:{status:200,body:{skills:storeItems(bots,botId)}}}return{status:405,body:{error:"method not allowed"}}}function limixSkillDefaultsMigration(){let marker=join257(DATA_DIR,"skill-state","limix-skill-defaults-v1.json");if(!existsSync100(marker))try{for(let[name6,entry2]of Object.entries(readSkillLibraryIndex()))
|
|
29864
|
+
entry2.everyBot&&setLibrarySkillEveryBot(name6,!1);mkdirSync122(join257(DATA_DIR,"skill-state"),{recursive:!0,mode:448}),writeFileAtomic(marker,`${JSON.stringify({at:new Date().toISOString()})}
|
|
29861
29865
|
`,{mode:384})}catch{}}function createBotSkillsRoutes(deps){let{store:store5,cfg:cfg2,listBotSkills:listBotSkills2,readBotSkillFile:readBotSkillFile2,stagedSkillListing:stagedSkillListing2}=deps;return async({req,res,url:url2,path:path6,method:method2,json:json7,readBody:readBody2})=>{let m3=null;if(path6.startsWith("/api/limix/bots")){let library=await handleLimixBotLibrary(method2,path6);if(library)return json7(res,library.status,library.body)}if(path6.startsWith("/api/limix/hub")){let hub=await handleLimixHub(method2,
|
|
29862
29866
|
path6,method2==="POST"?await readBody2(req):void 0);if(hub)return json7(res,hub.status,hub.body)}if(path6.startsWith("/api/limix/skill-store/")){let skillStore=handleLimixSkillStore(store5,method2,path6,method2==="POST"?await readBody2(req):void 0);if(skillStore)return json7(res,skillStore.status,skillStore.body)}if(method2==="GET"&&path6==="/api/limix/bundled-skills"){let skills=loadBundledSkills().map(({manifest})=>({id:manifest.id,name:manifest.name,description:manifest.description,defaultEnabled:manifest.
|
|
29863
29867
|
defaultEnabled,triggerTerms:manifest.triggerTerms,core:LIMIX_CORE_SKILLS.has(manifest.id)}));return json7(res,200,{skills})}if(m3=path6.match(/^\/api\/bots\/([\w-]+)\/skill-template$/),m3&&method2==="POST"){if(!store5.bot(m3[1]))return json7(res,404,{error:"no such bot"});let checked=botSkillTemplateSchema.safeParse(await readBody2(req));if(!checked.success)return json7(res,400,{error:checked.error.message});let input2=checked.data,parsed2=parseSkillMd(input2.text);if("error"in parsed2||parsed2.
|
|
@@ -30833,7 +30837,8 @@ bot.id,body2.threadId);let expectedThreadId=body2.threadId;if(expectedThreadId!=
|
|
|
30833
30837
|
code:"stop_not_yours"}),refusal7!==null};if(typeof expectedThreadId=="string"&&store5.taskByThread(bot.id,expectedThreadId))return stopReachesLiveTurn(turnId,liveTurn(expectedThreadId))?stopRefused(expectedThreadId)?void 0:(await stopThreadForPerson2(bot.id,expectedThreadId,messageSender2(auth)),json7(res,200,{ok:!0})):json7(res,200,{ok:!0,outcome:"already-stopped"});let directClaim=directTurnDispatchClaims2.get(bot.threadId),routineRun=routines2.activeBotRunForBot(bot.id);if(routineRun)return expectedThreadId!==
|
|
30834
30838
|
void 0&&routineRun.threadId!==expectedThreadId?json7(res,409,userError("serverError.chat.routineElsewhere")):(cancelDirectTurnDispatch2(bot.id,routineRun.threadId??expectedThreadId),routineRun.threadId&&revokeInternalCapabilitiesForThread2(routineRun.threadId),await routines2.cancelRun(routineRun.id),json7(res,200,{ok:!0}));let instance=registry3.get((botForThread2(bot.id,expectedThreadId??bot.threadId)??bot).modelSelection.instanceId),busyGroup=activeGroupTurnForBot2(bot.id);return busyGroup?expectedThreadId!==
|
|
30835
30839
|
void 0&&busyGroup.threadId!==expectedThreadId?json7(res,409,userError("serverError.chat.busyInRoom",{room:busyGroup.group.name})):stopRefused(busyGroup.threadId)?void 0:(cancelGroupTurnOperations2(busyGroup.group.id,busyGroup.threadId),revokeInternalCapabilitiesForThread2(busyGroup.threadId),await instance?.adapter.interruptTurn(busyGroup.threadId).catch(()=>{}),closeOpenApprovals2(busyGroup.threadId),json7(res,200,{ok:!0})):expectedThreadId!==void 0&&!busyGroup&&bot.threadId!==expectedThreadId&&
|
|
30836
|
-
directClaim?.threadId!==expectedThreadId?json7(res,409,{error:"the bot switched tasks before it could be interrupted"}):stopReachesLiveTurn(turnId,liveTurn(expectedThreadId??bot.threadId))?stopRefused(expectedThreadId??bot.threadId)?void 0:(await stopThreadForPerson2(bot.id,expectedThreadId??bot.threadId,messageSender2(auth)),json7(res,200,{ok:!0})):json7(res,200,{ok:!0,outcome:"already-stopped"})}return PASS};return route}var internalTurnThreads=new Set;function markInternalTurn(threadId){internalTurnThreads.add(threadId)}function clearInternalTurn(threadId){internalTurnThreads.delete(threadId)}function isInternalTurn(threadId){return internalTurnThreads.has(threadId)}function firstReplyWaitingName(botName,ai2){return
|
|
30840
|
+
directClaim?.threadId!==expectedThreadId?json7(res,409,{error:"the bot switched tasks before it could be interrupted"}):stopReachesLiveTurn(turnId,liveTurn(expectedThreadId??bot.threadId))?stopRefused(expectedThreadId??bot.threadId)?void 0:(await stopThreadForPerson2(bot.id,expectedThreadId??bot.threadId,messageSender2(auth)),json7(res,200,{ok:!0})):json7(res,200,{ok:!0,outcome:"already-stopped"})}return PASS};return route}var internalTurnThreads=new Set;function markInternalTurn(threadId){internalTurnThreads.add(threadId)}function clearInternalTurn(threadId){internalTurnThreads.delete(threadId)}function isInternalTurn(threadId){return internalTurnThreads.has(threadId)}var limixVi=()=>/^vi/i.test((typeof process<"u"?process.env?.OMB_UI_LANGUAGE:"")??"");function firstReplyWaitingName(botName,ai2){return limixVi()?`notice: ${botName} v\u1EABn ch\u01B0a nh\u1EADn \u0111\u01B0\u1EE3c ph\u1EA3n h\u1ED3i t\u1EEB ${ai2}.`:`notice: ${botName} hasn't heard back from ${ai2} yet.`}function firstReplyOverName(botName,ai2){return limixVi()?`notice: ${botName} kh\xF4ng nh\u1EADn \u0111\u01B0\u1EE3c ph\u1EA3n h\u1ED3i t\u1EEB ${ai2}.`:`notice: ${botName} didn't hear back fr\
|
|
30841
|
+
om ${ai2}.`}function firstReplySettledName(ai2){return limixVi()?`notice: ${ai2} \u0111\xE3 tr\u1EA3 l\u1EDDi, sau m\u1ED9t l\xFAc ch\u1EADm.`:`notice: ${ai2} answered after a slow start.`}var DEFAULT_FIRST_REPLY_NOTICE_MS=2e4;function firstReplyNoticeMs(env=process.env){let raw=env.OMB_FIRST_REPLY_NOTICE_MS?.trim();if(!raw)return DEFAULT_FIRST_REPLY_NOTICE_MS;let ms2=Number(raw);return Number.isFinite(ms2)&&ms2>=0?ms2:DEFAULT_FIRST_REPLY_NOTICE_MS}var AI_OUTPUT=new Set(["content.delta","item.started","item.updated","item.completed","request.opened","turn.progress"]);function createFirstReplyRescue(deps){let watches=new Map;closeLeftOpen(deps);let pause=watch=>{watch.timer&&clearTimeout(
|
|
30837
30842
|
watch.timer),watch.timer=null},arm=(threadId,watch)=>{pause(watch);let ms2=firstReplyNoticeMs();ms2<=0||(watch.timer=setTimeout(()=>{watch.timer=null;try{post3(threadId,watch)}catch(error66){console.error(`first-reply rescue: could not post in ${threadId}:`,error66 instanceof Error?error66.message:error66)}},ms2),watch.timer.unref?.())},post3=(threadId,watch)=>{if(watch.phase!=="waiting"||watches.get(threadId)!==watch||(watch.phase="over",deps.quiet(threadId)||!deps.live(threadId,watch.turnId)))
|
|
30838
30843
|
return;let speaker2=deps.speaker(threadId),group2=speaker2?deps.store.groupByThread(threadId):void 0,owner2=speaker2?deps.store.bot(speaker2.botId):deps.store.botByThread(threadId),bot=owner2&&!group2?deps.botForThread(owner2.id,threadId)??owner2:owner2;if(!bot)return;let instanceId=deps.runningEngine(threadId)??bot.modelSelection.instanceId,ai2=deps.engineName(instanceId),request9=group2?void 0:deps.requests.get(threadId),requestMessageId=request9&&!request9.stopped&&request9.turnId===watch.turnId?
|
|
30839
30844
|
request9.messageId:void 0,rescue={botId:bot.id,threadId,turnId:watch.turnId,...requestMessageId?{requestMessageId}:{},instanceId,ai:ai2,...group2?{groupId:group2.id}:{}},name6=firstReplyWaitingName(bot.name,ai2),row=deps.store.appendMessage(threadId,{role:"bot",kind:"activity",turnId:watch.turnId,...speaker2&&group2?{from:{botId:speaker2.botId,name:speaker2.name,color:speaker2.color}}:{},tool:{name:name6,ok:!0,rescue}});watch.row={messageId:row.id,botName:bot.name,rescue},watch.phase="posted"},close2=(threadId,watch,how)=>{
|
package/dist-server/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"type":"module","version":"0.3.
|
|
1
|
+
{"type":"module","version":"0.3.26"}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// template only describes who the bot is: its routines always arrive paused and nothing is connected for the person.
|
|
5
5
|
import { MASCOT_BODY_IDS } from "../shared/mascot-bodies.js";
|
|
6
6
|
import { MAUS_COLOR_NAMES } from "../shared/wire.js";
|
|
7
|
+
import { lxs } from "./limix-lang.js";
|
|
7
8
|
const TEAM_LIBRARY_RAW_ROOT = "https://raw.githubusercontent.com/leomieo/limix-bot-teams/main";
|
|
8
9
|
const LIST_TTL_MS = 10 * 60 * 1000;
|
|
9
10
|
const MAX_CATALOG_BYTES = 256_000;
|
|
@@ -52,11 +53,11 @@ function apps(value, field) {
|
|
|
52
53
|
}
|
|
53
54
|
export function parseBotListing(value) {
|
|
54
55
|
if (!isRecord(value) || value.format !== "openmaus.catalog" || value.version !== 1)
|
|
55
|
-
throw new Error("Danh sách mẫu không đúng định dạng");
|
|
56
|
+
throw new Error(lxs("Danh sách mẫu không đúng định dạng", "The template list has the wrong format"));
|
|
56
57
|
if (value.bots === undefined)
|
|
57
58
|
return [];
|
|
58
59
|
if (!Array.isArray(value.bots) || value.bots.length > 200)
|
|
59
|
-
throw new Error("Danh sách bot mẫu không hợp lệ");
|
|
60
|
+
throw new Error(lxs("Danh sách bot mẫu không hợp lệ", "The template bot list is not valid"));
|
|
60
61
|
const seen = new Set();
|
|
61
62
|
const out = [];
|
|
62
63
|
value.bots.forEach((raw, index) => {
|
|
@@ -83,10 +84,10 @@ export function parseBotListing(value) {
|
|
|
83
84
|
}
|
|
84
85
|
export function parseLibraryBot(value, expectedId) {
|
|
85
86
|
if (!isRecord(value) || value.format !== "limix.bot" || value.version !== 1)
|
|
86
|
-
throw new Error("Tệp bot mẫu không đúng định dạng");
|
|
87
|
+
throw new Error(lxs("Tệp bot mẫu không đúng định dạng", "The template bot file has the wrong format"));
|
|
87
88
|
const id = text(value.id, "id", 64);
|
|
88
89
|
if (id !== expectedId)
|
|
89
|
-
throw new Error("Tệp bot mẫu không khớp với danh sách");
|
|
90
|
+
throw new Error(lxs("Tệp bot mẫu không khớp với danh sách", "The template bot file does not match the list"));
|
|
90
91
|
if (!Array.isArray(value.tryAsking) || value.tryAsking.length === 0 || value.tryAsking.length > 6)
|
|
91
92
|
throw new Error("tryAsking is invalid");
|
|
92
93
|
const bot = {
|
|
@@ -134,15 +135,15 @@ export function parseLibraryBot(value, expectedId) {
|
|
|
134
135
|
async function fetchJson(url, maxBytes) {
|
|
135
136
|
const response = await fetch(url, { headers: { accept: "application/json, text/plain;q=0.9" }, redirect: "error", signal: AbortSignal.timeout(10_000) });
|
|
136
137
|
if (!response.ok)
|
|
137
|
-
throw new Error(`GitHub trả lỗi HTTP ${response.status}`);
|
|
138
|
+
throw new Error(lxs(`GitHub trả lỗi HTTP ${response.status}`, `GitHub returned HTTP ${response.status}`));
|
|
138
139
|
const raw = await response.text();
|
|
139
140
|
if (Buffer.byteLength(raw) > maxBytes)
|
|
140
|
-
throw new Error("Tệp mẫu quá lớn");
|
|
141
|
+
throw new Error(lxs("Tệp mẫu quá lớn", "The template file is too large"));
|
|
141
142
|
try {
|
|
142
143
|
return JSON.parse(raw);
|
|
143
144
|
}
|
|
144
145
|
catch {
|
|
145
|
-
throw new Error("GitHub không trả về JSON hợp lệ");
|
|
146
|
+
throw new Error(lxs("GitHub không trả về JSON hợp lệ", "GitHub did not return valid JSON"));
|
|
146
147
|
}
|
|
147
148
|
}
|
|
148
149
|
let cached = null;
|
|
@@ -175,7 +176,7 @@ export async function handleLimixBotLibrary(method, path) {
|
|
|
175
176
|
return null;
|
|
176
177
|
const id = match[1];
|
|
177
178
|
if (!(await listing()).some((bot) => bot.id === id))
|
|
178
|
-
return { status: 404, body: { error: "Không có bot mẫu này" } };
|
|
179
|
+
return { status: 404, body: { error: lxs("Không có bot mẫu này", "No such template bot") } };
|
|
179
180
|
return { status: 200, body: { bot: parseLibraryBot(await fetchJson(`${TEAM_LIBRARY_RAW_ROOT}/bots/${id}.json?t=${Date.now()}`, MAX_BOT_BYTES), id) } };
|
|
180
181
|
}
|
|
181
182
|
catch (error) {
|
|
@@ -9,6 +9,7 @@ import { isSkillName } from "../shared/skill-md.js";
|
|
|
9
9
|
import { writeFileAtomic } from "./atomic.js";
|
|
10
10
|
import { DATA_DIR } from "./config.js";
|
|
11
11
|
import { installLibrarySkill, readSkillLibraryIndex, retireLibrarySkill, setLibrarySkillReviewState } from "./skill-library.js";
|
|
12
|
+
import { lxs } from "./limix-lang.js";
|
|
12
13
|
export const HUB_REPO = "leomieo/limix-skill-hub";
|
|
13
14
|
const HUB_BRANCH = "main";
|
|
14
15
|
const SYNC_MIN_GAP_MS = 20 * 60 * 60 * 1000;
|
|
@@ -54,11 +55,11 @@ export function syncHub() {
|
|
|
54
55
|
return { changed: false, count: hubCatalog().length };
|
|
55
56
|
}
|
|
56
57
|
if (!response.ok)
|
|
57
|
-
throw new Error(`Hub trả về ${response.status}`);
|
|
58
|
+
throw new Error(lxs(`Hub trả về ${response.status}`, `The hub returned ${response.status}`));
|
|
58
59
|
const text = await response.text();
|
|
59
60
|
const parsed = JSON.parse(text);
|
|
60
61
|
if (!Array.isArray(parsed.skills))
|
|
61
|
-
throw new Error("Danh mục Hub không đúng định dạng");
|
|
62
|
+
throw new Error(lxs("Danh mục Hub không đúng định dạng", "The hub catalog has the wrong format"));
|
|
62
63
|
mkdirSync(hubDir(), { recursive: true, mode: 0o700 });
|
|
63
64
|
writeFileAtomic(catalogFile(), text, { mode: 0o600 });
|
|
64
65
|
saveSettings({ ...settings, etag: response.headers.get("etag"), lastSync: new Date().toISOString(), lastError: null });
|
|
@@ -83,9 +84,9 @@ const isToolFile = (path) => SCRIPT.test(path) || MCP_FILE.test(path);
|
|
|
83
84
|
const sha = (data) => createHash("sha256").update(data).digest("hex");
|
|
84
85
|
export function hubSkillBlocked(entry) {
|
|
85
86
|
if (entry.area === "quarantine" || entry.security_status === "quarantined")
|
|
86
|
-
return "Skill nằm trong khu cách ly vì có mã nguy hiểm.";
|
|
87
|
+
return lxs("Skill nằm trong khu cách ly vì có mã nguy hiểm.", "This skill is quarantined because it contains dangerous code.");
|
|
87
88
|
if (entry.approval_status === "quarantined" || entry.approval_status === "rejected")
|
|
88
|
-
return "Skill bị từ chối.";
|
|
89
|
+
return lxs("Skill bị từ chối.", "This skill was rejected.");
|
|
89
90
|
return null;
|
|
90
91
|
}
|
|
91
92
|
/** installed state of every catalog entry that came from the hub */
|
|
@@ -109,24 +110,24 @@ export function hubSkills() {
|
|
|
109
110
|
// A same-named skill the owner imported by hand is not the hub's: never touched by sync.
|
|
110
111
|
enabled: have?.reviewState === "approved",
|
|
111
112
|
updateAvailable: Boolean(have && have.checksum !== entry.checksum),
|
|
112
|
-
blocked: !have && library[entry.id] ? "Thư viện đã có kỹ năng cùng tên không đến từ Hub." : null,
|
|
113
|
+
blocked: !have && library[entry.id] ? lxs("Thư viện đã có kỹ năng cùng tên không đến từ Hub.", "The library already has a skill with this name that did not come from the hub.") : null,
|
|
113
114
|
risky: hubSkillBlocked(entry),
|
|
114
115
|
};
|
|
115
116
|
});
|
|
116
117
|
}
|
|
117
118
|
export async function installHubSkill(id, options = {}) {
|
|
118
119
|
if (!isSkillName(id))
|
|
119
|
-
return { error: "Tên kỹ năng không hợp lệ" };
|
|
120
|
+
return { error: lxs("Tên kỹ năng không hợp lệ", "Invalid skill name") };
|
|
120
121
|
const entry = hubCatalog().find((e) => e.id === id);
|
|
121
122
|
if (!entry)
|
|
122
|
-
return { error: "Không thấy kỹ năng này trong danh mục Hub. Hãy đồng bộ lại." };
|
|
123
|
+
return { error: lxs("Không thấy kỹ năng này trong danh mục Hub. Hãy đồng bộ lại.", "This skill is not in the hub catalog. Sync again.") };
|
|
123
124
|
const blocked = hubSkillBlocked(entry);
|
|
124
125
|
if (blocked && !options.allowRisky)
|
|
125
126
|
return { error: blocked };
|
|
126
127
|
const installed = installedFromHub().get(id);
|
|
127
128
|
const existing = readSkillLibraryIndex()[id];
|
|
128
129
|
if (existing && !installed)
|
|
129
|
-
return { error: "Thư viện đã có kỹ năng cùng tên không đến từ Hub." };
|
|
130
|
+
return { error: lxs("Thư viện đã có kỹ năng cùng tên không đến từ Hub.", "The library already has a skill with this name that did not come from the hub.") };
|
|
130
131
|
const wanted = Object.keys(entry.file_hashes).filter((path) => path !== "SKILL.md" && (options.allowTools || !isToolFile(path)));
|
|
131
132
|
const skipped = Object.keys(entry.file_hashes).filter((path) => path !== "SKILL.md" && !wanted.includes(path));
|
|
132
133
|
const files = [];
|
|
@@ -134,22 +135,22 @@ export async function installHubSkill(id, options = {}) {
|
|
|
134
135
|
for (const path of ["SKILL.md", ...wanted]) {
|
|
135
136
|
const expected = entry.file_hashes[path];
|
|
136
137
|
if (!expected)
|
|
137
|
-
return { error: `Danh mục thiếu mã kiểm tra cho ${path}` };
|
|
138
|
+
return { error: lxs(`Danh mục thiếu mã kiểm tra cho ${path}`, `The catalog has no checksum for ${path}`) };
|
|
138
139
|
let response;
|
|
139
140
|
try {
|
|
140
141
|
response = await fetchText(rawUrl(`${entry.area === "quarantine" ? "quarantine" : "skills"}/${id}/${path}`));
|
|
141
142
|
}
|
|
142
143
|
catch (cause) {
|
|
143
|
-
return { error: `Không tải được ${path}: ${cause instanceof Error ? cause.message : String(cause)
|
|
144
|
+
return { error: lxs(`Không tải được ${path}: `, `Couldn't download ${path}: `) + (cause instanceof Error ? cause.message : String(cause)) };
|
|
144
145
|
}
|
|
145
146
|
if (!response.ok)
|
|
146
|
-
return { error: `Không tải được ${path} (${response.status})` };
|
|
147
|
+
return { error: lxs(`Không tải được ${path} (${response.status})`, `Couldn't download ${path} (${response.status})`) };
|
|
147
148
|
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
148
149
|
if (bytes.byteLength > FILE_MAX_BYTES)
|
|
149
|
-
return { error: `${path} quá lớn` };
|
|
150
|
+
return { error: lxs(`${path} quá lớn`, `${path} is too large`) };
|
|
150
151
|
// Pinned content: a file the hub no longer serves as catalogued is refused, never installed.
|
|
151
152
|
if (sha(bytes) !== expected)
|
|
152
|
-
return { error: `${path} không khớp mã SHA-256 trong danh mục — đã dừng cài` };
|
|
153
|
+
return { error: lxs(`${path} không khớp mã SHA-256 trong danh mục — đã dừng cài`, `${path} does not match the SHA-256 in the catalog — install stopped`) };
|
|
153
154
|
if (path === "SKILL.md")
|
|
154
155
|
skillMd = new TextDecoder().decode(bytes);
|
|
155
156
|
else
|
|
@@ -160,7 +161,7 @@ export async function installHubSkill(id, options = {}) {
|
|
|
160
161
|
if ("error" in retired)
|
|
161
162
|
return { error: retired.error };
|
|
162
163
|
if ("stale" in retired)
|
|
163
|
-
return { error: "Kỹ năng đã bị sửa tay sau khi cài, không cập nhật đè." };
|
|
164
|
+
return { error: lxs("Kỹ năng đã bị sửa tay sau khi cài, không cập nhật đè.", "This skill was edited by hand after installing, so it is not overwritten.") };
|
|
164
165
|
}
|
|
165
166
|
const result = installLibrarySkill({
|
|
166
167
|
name: id,
|
|
@@ -180,19 +181,19 @@ export async function installHubSkill(id, options = {}) {
|
|
|
180
181
|
}
|
|
181
182
|
export function setHubSkillEnabled(id, enabled) {
|
|
182
183
|
if (!installedFromHub().has(id))
|
|
183
|
-
return { error: "Kỹ năng này chưa cài từ Hub" };
|
|
184
|
+
return { error: lxs("Kỹ năng này chưa cài từ Hub", "This skill was not installed from the hub") };
|
|
184
185
|
const result = setLibrarySkillReviewState(id, enabled ? "approved" : "disabled");
|
|
185
186
|
return "error" in result ? { error: result.error } : { ok: true };
|
|
186
187
|
}
|
|
187
188
|
export function removeHubSkill(id) {
|
|
188
189
|
const installed = installedFromHub().get(id);
|
|
189
190
|
if (!installed)
|
|
190
|
-
return { error: "Kỹ năng này chưa cài từ Hub" };
|
|
191
|
+
return { error: lxs("Kỹ năng này chưa cài từ Hub", "This skill was not installed from the hub") };
|
|
191
192
|
const result = retireLibrarySkill(id, installed.sha256);
|
|
192
193
|
if ("error" in result)
|
|
193
194
|
return { error: result.error };
|
|
194
195
|
if ("stale" in result)
|
|
195
|
-
return { error: "Kỹ năng đã bị sửa tay sau khi cài, không gỡ tự động." };
|
|
196
|
+
return { error: lxs("Kỹ năng đã bị sửa tay sau khi cài, không gỡ tự động.", "This skill was edited by hand after installing, so it is not removed automatically.") };
|
|
196
197
|
return { ok: true }; // the bytes stay in the library archive, so a removal can be undone by hand
|
|
197
198
|
}
|
|
198
199
|
/** Auto Install (off by default): only excellent/recommended, approved, instructions-only skills; max 5 per sync. */
|
|
@@ -16,6 +16,7 @@ import { writeFileAtomic } from "./atomic.js";
|
|
|
16
16
|
import { DATA_DIR } from "./config.js";
|
|
17
17
|
import { importedSkillListings, setImportedSkillEnabled } from "./imported-skills.js";
|
|
18
18
|
import { listLibrarySkills, loadBundledSkills, mergeSkills, readSkillLibraryIndex, selectBundledSkills, setLibrarySkillEveryBot, setLibrarySkillOptOut, setLibrarySkillReviewState, unassignLibrarySkill, } from "./skill-library.js";
|
|
19
|
+
import { lxs } from "./limix-lang.js";
|
|
19
20
|
/** Always on for every bot. "Learning new skills" itself is the skill-authoring feature, on for every bot already. */
|
|
20
21
|
export const LIMIX_CORE_SKILLS = new Set([
|
|
21
22
|
"create-verification-skill", "agent-reach", "windows-computer-automation", "phone-connection", "phone-harness",
|
|
@@ -62,7 +63,7 @@ export function limixSelectBundled(text, capabilities, skills) {
|
|
|
62
63
|
export function limixBundledListings(botId) {
|
|
63
64
|
return limixBundledFor(botId, loadBundledSkills()).map(({ manifest }) => ({
|
|
64
65
|
name: manifest.id, description: manifest.description, enabled: true, editable: false,
|
|
65
|
-
source: LIMIX_CORE_SKILLS.has(manifest.id) ? "Kĩ năng gốc của Limix Bot" : "Kho Kĩ Năng (Skills) · có sẵn",
|
|
66
|
+
source: LIMIX_CORE_SKILLS.has(manifest.id) ? lxs("Kĩ năng gốc của Limix Bot", "Limix Bot core skill") : lxs("Kho Kĩ Năng (Skills) · có sẵn", "Skills Store · built in"),
|
|
66
67
|
sha256: "", importedAt: "", warnings: [], skippedFiles: [], origin: "bundled", core: LIMIX_CORE_SKILLS.has(manifest.id),
|
|
67
68
|
}));
|
|
68
69
|
}
|
|
@@ -72,7 +73,7 @@ export function limixBundledSwitch(botId, name, enabled) {
|
|
|
72
73
|
return null;
|
|
73
74
|
if (LIMIX_CORE_SKILLS.has(name)) {
|
|
74
75
|
return enabled ? { status: 200, body: { skill: limixBundledListings(botId).find((skill) => skill.name === name) } }
|
|
75
|
-
: { status: 409, body: { error: "Kĩ năng gốc của Limix Bot luôn bật cho mọi bot." } };
|
|
76
|
+
: { status: 409, body: { error: lxs("Kĩ năng gốc của Limix Bot luôn bật cho mọi bot.", "Limix Bot core skills are always on for every bot.") } };
|
|
76
77
|
}
|
|
77
78
|
setBundledPick(botId, name, enabled);
|
|
78
79
|
return { status: 200, body: { skill: { ...limixBundledListings(botId).find((skill) => skill.name === name), name, enabled } } };
|
|
@@ -84,7 +85,7 @@ function storeItems(bots, botId) {
|
|
|
84
85
|
const items = new Map();
|
|
85
86
|
for (const { manifest } of loadBundledSkills()) {
|
|
86
87
|
const core = LIMIX_CORE_SKILLS.has(manifest.id);
|
|
87
|
-
items.set(manifest.id, { name: manifest.id, description: manifest.description, kind: "bundled", added: core || picks.has(manifest.id), core, ready: true, warnings: 0, source: "Có sẵn trong Limix Bot" });
|
|
88
|
+
items.set(manifest.id, { name: manifest.id, description: manifest.description, kind: "bundled", added: core || picks.has(manifest.id), core, ready: true, warnings: 0, source: lxs("Có sẵn trong Limix Bot", "Built into Limix Bot") });
|
|
88
89
|
}
|
|
89
90
|
for (const skill of listLibrarySkills()) {
|
|
90
91
|
if (items.has(skill.name))
|
|
@@ -92,13 +93,13 @@ function storeItems(bots, botId) {
|
|
|
92
93
|
const intact = !skill.warnings.some((warning) => warning.includes("enablement is blocked"));
|
|
93
94
|
items.set(skill.name, {
|
|
94
95
|
name: skill.name, description: skill.description, kind: "library", added: assigned.has(skill.name) && skill.enabled, core: false,
|
|
95
|
-
ready: intact, warnings: skill.warnings.length, source: skill.source.startsWith("limix-hub:") ? "Thư viện kĩ năng Limix" : "Đã cài trong Cài đặt",
|
|
96
|
+
ready: intact, warnings: skill.warnings.length, source: skill.source.startsWith("limix-hub:") ? lxs("Thư viện kĩ năng Limix", "Limix skill library") : lxs("Đã cài trong Cài đặt", "Installed in Settings"),
|
|
96
97
|
});
|
|
97
98
|
}
|
|
98
99
|
for (const skill of importedSkillListings(botId, new Set())) {
|
|
99
100
|
if (items.has(skill.name))
|
|
100
101
|
continue;
|
|
101
|
-
items.set(skill.name, { name: skill.name, description: skill.description, kind: "imported", added: skill.enabled, core: false, ready: true, warnings: skill.warnings.length, source: skill.source || "Từ máy tính này" });
|
|
102
|
+
items.set(skill.name, { name: skill.name, description: skill.description, kind: "imported", added: skill.enabled, core: false, ready: true, warnings: skill.warnings.length, source: skill.source || lxs("Từ máy tính này", "From this computer") });
|
|
102
103
|
}
|
|
103
104
|
return [...items.values()].sort((a, b) => Number(b.core) - Number(a.core) || a.name.localeCompare(b.name));
|
|
104
105
|
}
|
|
@@ -106,15 +107,15 @@ function storeItems(bots, botId) {
|
|
|
106
107
|
export function limixStoreSet(bots, botId, name, add, kind) {
|
|
107
108
|
const bot = bots.bot(botId);
|
|
108
109
|
if (!bot)
|
|
109
|
-
return { error: "Không tìm thấy bot." };
|
|
110
|
+
return { error: lxs("Không tìm thấy bot.", "Bot not found.") };
|
|
110
111
|
if (!isSkillName(name))
|
|
111
|
-
return { error: "Tên kĩ năng không hợp lệ." };
|
|
112
|
+
return { error: lxs("Tên kĩ năng không hợp lệ.", "Invalid skill name.") };
|
|
112
113
|
const item = storeItems(bots, botId).find((entry) => entry.name === name && (!kind || entry.kind === kind));
|
|
113
114
|
if (!item)
|
|
114
|
-
return { error: `Kho chưa có kĩ năng "${name}".` };
|
|
115
|
+
return { error: lxs(`Kho chưa có kĩ năng "${name}".`, `The store has no skill "${name}".`) };
|
|
115
116
|
if (item.kind === "bundled") {
|
|
116
117
|
if (item.core)
|
|
117
|
-
return add ? { ok: true } : { error: "Kĩ năng gốc của Limix Bot luôn bật cho mọi bot." };
|
|
118
|
+
return add ? { ok: true } : { error: lxs("Kĩ năng gốc của Limix Bot luôn bật cho mọi bot.", "Limix Bot core skills are always on for every bot.") };
|
|
118
119
|
setBundledPick(botId, name, add);
|
|
119
120
|
return { ok: true };
|
|
120
121
|
}
|
|
@@ -148,14 +149,14 @@ export function handleLimixSkillStore(bots, method, path, body) {
|
|
|
148
149
|
return null;
|
|
149
150
|
const botId = m[1];
|
|
150
151
|
if (!bots.bot(botId))
|
|
151
|
-
return { status: 404, body: { error: "Không tìm thấy bot." } };
|
|
152
|
+
return { status: 404, body: { error: lxs("Không tìm thấy bot.", "Bot not found.") } };
|
|
152
153
|
if (method === "GET")
|
|
153
154
|
return { status: 200, body: { skills: storeItems(bots, botId) } };
|
|
154
155
|
if (method === "POST") {
|
|
155
156
|
const raw = (body && typeof body === "object" ? body : {});
|
|
156
157
|
const kind = raw.kind === "bundled" || raw.kind === "library" || raw.kind === "imported" ? raw.kind : undefined;
|
|
157
158
|
if (typeof raw.name !== "string" || typeof raw.add !== "boolean")
|
|
158
|
-
return { status: 400, body: { error: "Cần tên kĩ năng và add." } };
|
|
159
|
+
return { status: 400, body: { error: lxs("Cần tên kĩ năng và add.", "A skill name and add are required.") } };
|
|
159
160
|
const result = limixStoreSet(bots, botId, raw.name, raw.add, kind);
|
|
160
161
|
return "error" in result ? { status: 422, body: result } : { status: 200, body: { skills: storeItems(bots, botId) } };
|
|
161
162
|
}
|
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
// notice row with what the person can do about it. The row's tool name is a
|
|
3
3
|
// whole plain sentence, so a client that only prints tool names (the phones)
|
|
4
4
|
// still reads it; this payload is what lets the desktop draw its buttons.
|
|
5
|
+
const limixVi = () => /^vi/i.test((typeof process !== "undefined" ? process.env?.OMB_UI_LANGUAGE : "") ?? "");
|
|
5
6
|
/** The waiting row's words, for clients that print the tool name. */
|
|
6
7
|
export function firstReplyWaitingName(botName, ai) {
|
|
7
|
-
return `notice: ${botName} vẫn chưa nhận được phản hồi từ ${ai}.`;
|
|
8
|
+
return limixVi() ? `notice: ${botName} vẫn chưa nhận được phản hồi từ ${ai}.` : `notice: ${botName} hasn't heard back from ${ai} yet.`;
|
|
8
9
|
}
|
|
9
10
|
/** The same row once its turn ended without an answer: history, true as it stands. */
|
|
10
11
|
export function firstReplyOverName(botName, ai) {
|
|
11
|
-
return `notice: ${botName} không nhận được phản hồi từ ${ai}.`;
|
|
12
|
+
return limixVi() ? `notice: ${botName} không nhận được phản hồi từ ${ai}.` : `notice: ${botName} didn't hear back from ${ai}.`;
|
|
12
13
|
}
|
|
13
14
|
/** The same row once the AI's first output arrived. */
|
|
14
15
|
export function firstReplySettledName(ai) {
|
|
15
|
-
return `notice: ${ai} đã trả lời, sau một lúc chậm.`;
|
|
16
|
+
return limixVi() ? `notice: ${ai} đã trả lời, sau một lúc chậm.` : `notice: ${ai} answered after a slow start.`;
|
|
16
17
|
}
|