@agimon-ai/doompi-computer-use 0.0.1-alpha.14 → 0.0.1-alpha.16
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/controllers/webComputerUseChannel.cjs +1 -1
- package/dist/controllers/webComputerUseChannel.cjs.map +1 -1
- package/dist/controllers/webComputerUseChannel.mjs +1 -1
- package/dist/controllers/webComputerUseChannel.mjs.map +1 -1
- package/dist/types/computerUseApi.cjs +1 -1
- package/dist/types/computerUseApi.cjs.map +1 -1
- package/dist/types/computerUseApi.d.cts.map +1 -1
- package/dist/types/computerUseApi.d.mts.map +1 -1
- package/dist/types/computerUseApi.mjs +1 -1
- package/dist/types/computerUseApi.mjs.map +1 -1
- package/package.json +12 -12
- package/src/types/computerUseApi.ts +2 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../types/computerUseApi.cjs"),t=require("../services/webComputerUseAvailability/index.cjs");let n=require("node:crypto");function
|
|
1
|
+
const e=require("../types/computerUseApi.cjs"),t=require("../services/webComputerUseAvailability/index.cjs");let n=require("node:crypto");const r=`DoomPi Desktop computer use is unavailable.`;function i(e){return typeof e==`object`&&e&&!Array.isArray(e)?e:void 0}function a(e){let t=i(e);return t?.action===`status`||t?.action===`targets`||t?.action===`stop`||t?.action===`artifacts`?{action:t.action}:void 0}async function o(e){let n=await e.json();if(!e.ok)throw t.computerUseSessionApiError(e.status,n);return n}function s(e){let t=i(e);if(typeof t?.sessionId==`string`&&typeof t.revision==`number`&&typeof t.wake==`number`&&[`inactive`,`awaiting_confirmation`,`activating`,`active`,`stopping`,`failed`].includes(t.phase))return t}function c(){let c;return{frameType:e.computerUseChannelType,lifecycle:`hub`,start(l){let u=new Map,d=new Map,f=new Map,p=new Map,m=new Map,h=new Set,g,_=!1,v=async(t,n,r=`GET`,i)=>l.requestSessionApi(t,{basePath:e.API_BASE_PATH,path:n,method:r,...i===void 0?{}:{body:JSON.stringify(i)}}),y=(e,t)=>{t.phase===`active`||t.phase===`activating`||t.phase===`stopping`?g=e.sessionId:g===e.sessionId&&(g=void 0,p.delete(e.sessionId));let n={state:t,targets:f.get(e.sessionId)??[],...g!==void 0&&g!==e.sessionId?{busy:!0}:{}};d.set(e.sessionId,n),l.publish(e.sessionId,n)},b=async t=>{let n=s(await o(await v(t,e.COMPUTER_USE_ROUTES.hubState)));if(n===void 0)throw Error(`Computer-use API returned an invalid session state.`);return y(t,n),n},x=async(t,n)=>{if(!l.computerUse?.available)throw Error(r);try{let r=await l.computerUse.request(t,{operation:n.operation,payload:n.operation===`act`?{grantId:n.grantId,sequence:n.sequence,action:n.payload}:{grantId:n.grantId}});await o(await v(t,e.COMPUTER_USE_ROUTES.hubComplete,`POST`,{id:n.id,result:r}))}catch(r){await o(await v(t,e.COMPUTER_USE_ROUTES.hubComplete,`POST`,{id:n.id,error:r instanceof Error?r.message:String(r)}))}},S=async t=>{let r=await o(await v(t,e.COMPUTER_USE_ROUTES.hubAuthorization)),i;if(r!==null&&l.computerUse?.available)try{i=await l.computerUse.request(t,{operation:`stop`,payload:{grantId:r.grantId}})}catch(e){let t=e instanceof Error?e.message:String(e);i={artifactId:(0,n.randomUUID)(),status:`failed`,failure:{code:`recording_failed`,message:t.slice(0,512)}},l.onNotice(`computer-use Desktop stop was already unavailable (${t})`)}await o(await v(t,e.COMPUTER_USE_ROUTES.hubStop,`POST`,{artifact:i})),p.delete(t.sessionId),g===t.sessionId&&(g=void 0)},C=async(n,a)=>{if(!(_||h.has(n.sessionId))){h.add(n.sessionId);try{let t=a??await b(n);if(t.phase===`active`){if(!p.has(n.sessionId)){let t=await o(await v(n,e.COMPUTER_USE_ROUTES.hubAuthorization));t!==null&&p.set(n.sessionId,t.grantId)}let r=typeof t.expiresAt==`number`&&t.expiresAt<=Date.now(),a=!1;if(!r&&l.computerUse?.available)try{a=i(await l.computerUse.request(n,{operation:`status`}))?.ownedBySession===!0}catch{a=!0}if(r||!a){await S(n),await b(n);return}}if(t.phase===`awaiting_confirmation`&&(g===void 0||g===n.sessionId)){let t=await o(await v(n,e.COMPUTER_USE_ROUTES.hubActivation));if(t!==null&&!l.computerUse?.available){await o(await v(n,e.COMPUTER_USE_ROUTES.hubStop,`POST`,{error:r})),await b(n);return}if(t!==null&&l.computerUse?.available){g=n.sessionId;try{let r=await l.computerUse.request(n,{operation:`activate`,payload:t});await o(await v(n,e.COMPUTER_USE_ROUTES.hubStop,`POST`,{host:r}));let a=i(r)?.grantId;typeof a==`string`&&p.set(n.sessionId,a)}catch(t){g=void 0,await o(await v(n,e.COMPUTER_USE_ROUTES.hubStop,`POST`,{error:t instanceof Error?t.message:String(t)}))}await b(n)}}t.phase===`stopping`&&(await S(n),await b(n));let s=await o(await v(n,e.COMPUTER_USE_ROUTES.hubNext));s!==null&&await x(n,s)}catch(e){e instanceof t.MissingComputerUseApiError?d.delete(n.sessionId):l.onNotice(`computer-use state handling failed for ${n.sessionId} (${e instanceof Error?e.message:String(e)})`)}finally{h.delete(n.sessionId)}}},w=(e,t)=>{let n=s(t);n!==void 0&&n.sessionId===e.sessionId&&(y(e,n),C(e,n))};return c=(e,n)=>{let r=a(n);r!==void 0&&(async()=>{try{if(r.action===`targets`){let t=l.computerUse?.available?await l.computerUse.request(e,{operation:`targets`}):[];f.set(e.sessionId,Array.isArray(t)?t.filter(e=>i(e)!==void 0):[])}else r.action===`stop`&&await S(e);await b(e),C(e)}catch(n){n instanceof t.MissingComputerUseApiError?d.delete(e.sessionId):l.onNotice(`computer-use command failed for ${e.sessionId} (${n instanceof Error?n.message:String(n)})`)}})()},{payloadFor:e=>d.get(e.sessionId),sessionAdded(t){u.set(t.sessionId,t);let n=l.directEvents.subscribe(e.computerUseChannelType,t.sessionId,e=>w(t,e),{replayLatest:!0});m.set(t.sessionId,n),C(t)},sessionRemoved(e){let t=u.get(e),n=p.get(e);m.get(e)?.(),m.delete(e),t!==void 0&&n!==void 0&&l.computerUse?.available&&l.computerUse.request(t,{operation:`stop`,payload:{grantId:n}}).catch(e=>l.onNotice(`computer-use session cleanup failed (${e instanceof Error?e.message:String(e)})`)),u.delete(e),d.delete(e),f.delete(e),p.delete(e),h.delete(e),g===e&&(g=void 0)},close(){_=!0;for(let e of m.values())e();if(m.clear(),l.computerUse?.available)for(let[e,t]of p){let n=u.get(e);n!==void 0&&l.computerUse.request(n,{operation:`stop`,payload:{grantId:t}}).catch(()=>void 0)}u.clear(),d.clear(),f.clear(),p.clear()}}},receive(e,t){c?.(e,t)}}}exports.computerUseChannelType=e.computerUseChannelType,exports.createComputerUseChannel=c;
|
|
2
2
|
//# sourceMappingURL=webComputerUseChannel.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webComputerUseChannel.cjs","names":["computerUseSessionApiError","computerUseChannelType","API_BASE_PATH","COMPUTER_USE_ROUTES","randomUUID","MissingComputerUseApiError"],"sources":["../../src/controllers/webComputerUseChannel.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport type { DoomHubChannelSource, DoomHubSessionScope, DoomHubChannel } from '@agimon-ai/doompi-core/hub-channel';\n\nimport { computerUseSessionApiError, MissingComputerUseApiError } from '../services/webComputerUseAvailability';\nimport {\n API_BASE_PATH,\n computerUseChannelType,\n COMPUTER_USE_ROUTES,\n type ComputerUseActivationRequest,\n type ComputerUseBrokerRequest,\n type ComputerUseBrowserCommand,\n type ComputerUseChannelPayload,\n type ComputerUseSessionView,\n} from '../types/computerUseApi';\nexport { computerUseChannelType };\n\nfunction record(value: unknown): Record<string, unknown> | undefined {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\nfunction command(value: unknown): ComputerUseBrowserCommand | undefined {\n const input = record(value);\n return input?.action === 'status' ||\n input?.action === 'targets' ||\n input?.action === 'stop' ||\n input?.action === 'artifacts'\n ? { action: input.action }\n : undefined;\n}\n\nasync function responseJson(response: Response): Promise<unknown> {\n const value = await response.json();\n if (!response.ok) throw computerUseSessionApiError(response.status, value);\n return value;\n}\n\nfunction sessionState(value: unknown): ComputerUseSessionView | undefined {\n const input = record(value);\n if (\n typeof input?.sessionId !== 'string' ||\n typeof input.revision !== 'number' ||\n typeof input.wake !== 'number' ||\n !['inactive', 'awaiting_confirmation', 'activating', 'active', 'stopping', 'failed'].includes(input.phase as string)\n )\n return undefined;\n return input as unknown as ComputerUseSessionView;\n}\n\nexport function createComputerUseChannel(): DoomHubChannel {\n let receiveCommand: ((scope: DoomHubSessionScope, payload: unknown) => void) | undefined;\n return {\n frameType: computerUseChannelType,\n lifecycle: 'hub',\n start(host) {\n const scopes = new Map<string, DoomHubSessionScope>();\n const latest = new Map<string, ComputerUseChannelPayload>();\n const targets = new Map<string, readonly Record<string, unknown>[]>();\n const grants = new Map<string, string>();\n const subscriptions = new Map<string, () => void>();\n const processing = new Set<string>();\n let activeSessionId: string | undefined;\n let closed = false;\n\n const sessionRequest = async (scope: DoomHubSessionScope, path: string, method = 'GET', value?: unknown) =>\n host.requestSessionApi(scope, {\n basePath: API_BASE_PATH,\n path,\n method,\n ...(value === undefined ? {} : { body: JSON.stringify(value) }),\n });\n\n const publish = (scope: DoomHubSessionScope, state: ComputerUseSessionView): void => {\n if (state.phase === 'active' || state.phase === 'activating' || state.phase === 'stopping') {\n activeSessionId = scope.sessionId;\n } else if (activeSessionId === scope.sessionId) {\n activeSessionId = undefined;\n grants.delete(scope.sessionId);\n }\n const payload: ComputerUseChannelPayload = {\n state,\n targets: targets.get(scope.sessionId) ?? [],\n ...(activeSessionId !== undefined && activeSessionId !== scope.sessionId ? { busy: true } : {}),\n };\n latest.set(scope.sessionId, payload);\n host.publish(scope.sessionId, payload);\n };\n\n const refresh = async (scope: DoomHubSessionScope): Promise<ComputerUseSessionView> => {\n const state = sessionState(await responseJson(await sessionRequest(scope, COMPUTER_USE_ROUTES.hubState)));\n if (state === undefined) throw new Error('Computer-use API returned an invalid session state.');\n publish(scope, state);\n return state;\n };\n\n const completePending = async (scope: DoomHubSessionScope, pending: ComputerUseBrokerRequest): Promise<void> => {\n if (!host.computerUse?.available) throw new Error('DoomPi Desktop computer use is unavailable.');\n try {\n const result = await host.computerUse.request(scope, {\n operation: pending.operation,\n payload:\n pending.operation === 'act'\n ? { grantId: pending.grantId, sequence: pending.sequence, action: pending.payload }\n : { grantId: pending.grantId },\n });\n await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubComplete, 'POST', { id: pending.id, result }),\n );\n } catch (error) {\n await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubComplete, 'POST', {\n id: pending.id,\n error: error instanceof Error ? error.message : String(error),\n }),\n );\n }\n };\n\n const stopSession = async (scope: DoomHubSessionScope): Promise<void> => {\n const authorization = (await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubAuthorization),\n )) as { grantId: string } | null;\n let artifact: unknown;\n if (authorization !== null && host.computerUse?.available) {\n try {\n artifact = await host.computerUse.request(scope, {\n operation: 'stop',\n payload: { grantId: authorization.grantId },\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n artifact = {\n artifactId: randomUUID(),\n status: 'failed',\n failure: { code: 'recording_failed', message: message.slice(0, 512) },\n };\n host.onNotice(`computer-use Desktop stop was already unavailable (${message})`);\n }\n }\n await responseJson(await sessionRequest(scope, COMPUTER_USE_ROUTES.hubStop, 'POST', { artifact }));\n grants.delete(scope.sessionId);\n if (activeSessionId === scope.sessionId) activeSessionId = undefined;\n };\n\n const processScope = async (scope: DoomHubSessionScope, announced?: ComputerUseSessionView): Promise<void> => {\n if (closed || processing.has(scope.sessionId)) return;\n processing.add(scope.sessionId);\n try {\n const state = announced ?? (await refresh(scope));\n if (state.phase === 'active') {\n if (!grants.has(scope.sessionId)) {\n const authorization = (await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubAuthorization),\n )) as { grantId: string } | null;\n if (authorization !== null) grants.set(scope.sessionId, authorization.grantId);\n }\n const expired = typeof state.expiresAt === 'number' && state.expiresAt <= Date.now();\n let owned = false;\n if (!expired && host.computerUse?.available) {\n try {\n const hostStatus = record(await host.computerUse.request(scope, { operation: 'status' }));\n owned = hostStatus?.ownedBySession === true;\n } catch {\n owned = true;\n }\n }\n if (expired || !owned) {\n await stopSession(scope);\n await refresh(scope);\n return;\n }\n }\n if (\n state.phase === 'awaiting_confirmation' &&\n (activeSessionId === undefined || activeSessionId === scope.sessionId)\n ) {\n if (!host.computerUse?.available) throw new Error('DoomPi Desktop computer use is unavailable.');\n const activation = (await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubActivation),\n )) as ComputerUseActivationRequest | null;\n if (activation !== null) {\n activeSessionId = scope.sessionId;\n try {\n const hostResult = await host.computerUse.request(scope, {\n operation: 'activate',\n payload: activation,\n });\n await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubStop, 'POST', { host: hostResult }),\n );\n const grantId = record(hostResult)?.grantId;\n if (typeof grantId === 'string') grants.set(scope.sessionId, grantId);\n } catch (error) {\n activeSessionId = undefined;\n await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubStop, 'POST', {\n error: error instanceof Error ? error.message : String(error),\n }),\n );\n }\n await refresh(scope);\n }\n }\n if (state.phase === 'stopping') {\n await stopSession(scope);\n await refresh(scope);\n }\n const pending = (await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubNext),\n )) as ComputerUseBrokerRequest | null;\n if (pending !== null) await completePending(scope, pending);\n } catch (error) {\n if (error instanceof MissingComputerUseApiError) {\n latest.delete(scope.sessionId);\n } else {\n host.onNotice(\n `computer-use state handling failed for ${scope.sessionId} (${error instanceof Error ? error.message : String(error)})`,\n );\n }\n } finally {\n processing.delete(scope.sessionId);\n }\n };\n\n const onState = (scope: DoomHubSessionScope, payload: unknown): void => {\n const state = sessionState(payload);\n if (state === undefined || state.sessionId !== scope.sessionId) return;\n publish(scope, state);\n void processScope(scope, state);\n };\n\n receiveCommand = (scope, payload) => {\n const parsed = command(payload);\n if (parsed === undefined) return;\n void (async () => {\n try {\n if (parsed.action === 'targets') {\n if (!host.computerUse?.available) throw new Error('DoomPi Desktop computer use is unavailable.');\n const result = await host.computerUse.request(scope, { operation: 'targets' });\n targets.set(\n scope.sessionId,\n Array.isArray(result)\n ? result.filter((item): item is Record<string, unknown> => record(item) !== undefined)\n : [],\n );\n } else if (parsed.action === 'stop') {\n await stopSession(scope);\n }\n await refresh(scope);\n void processScope(scope);\n } catch (error) {\n if (error instanceof MissingComputerUseApiError) {\n latest.delete(scope.sessionId);\n } else {\n host.onNotice(\n `computer-use command failed for ${scope.sessionId} (${error instanceof Error ? error.message : String(error)})`,\n );\n }\n }\n })();\n };\n\n const source: DoomHubChannelSource = {\n payloadFor: (scope) => latest.get(scope.sessionId),\n sessionAdded(scope) {\n scopes.set(scope.sessionId, scope);\n const unsubscribe = host.directEvents.subscribe(\n computerUseChannelType,\n scope.sessionId,\n (payload) => onState(scope, payload),\n { replayLatest: true },\n );\n subscriptions.set(scope.sessionId, unsubscribe);\n void processScope(scope);\n },\n sessionRemoved(sessionId) {\n const scope = scopes.get(sessionId);\n const grantId = grants.get(sessionId);\n subscriptions.get(sessionId)?.();\n subscriptions.delete(sessionId);\n if (scope !== undefined && grantId !== undefined && host.computerUse?.available) {\n void host.computerUse\n .request(scope, { operation: 'stop', payload: { grantId } })\n .catch((error: unknown) =>\n host.onNotice(\n `computer-use session cleanup failed (${error instanceof Error ? error.message : String(error)})`,\n ),\n );\n }\n scopes.delete(sessionId);\n latest.delete(sessionId);\n targets.delete(sessionId);\n grants.delete(sessionId);\n processing.delete(sessionId);\n if (activeSessionId === sessionId) activeSessionId = undefined;\n },\n close() {\n closed = true;\n for (const unsubscribe of subscriptions.values()) unsubscribe();\n subscriptions.clear();\n if (host.computerUse?.available) {\n for (const [sessionId, grantId] of grants) {\n const scope = scopes.get(sessionId);\n if (scope !== undefined)\n void host.computerUse\n .request(scope, { operation: 'stop', payload: { grantId } })\n .catch(() => undefined);\n }\n }\n scopes.clear();\n latest.clear();\n targets.clear();\n grants.clear();\n },\n };\n return source;\n },\n receive(scope, payload) {\n receiveCommand?.(scope, payload);\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"webComputerUseChannel.cjs","names":["computerUseSessionApiError","computerUseChannelType","API_BASE_PATH","COMPUTER_USE_ROUTES","randomUUID","MissingComputerUseApiError"],"sources":["../../src/controllers/webComputerUseChannel.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport type { DoomHubChannelSource, DoomHubSessionScope, DoomHubChannel } from '@agimon-ai/doompi-core/hub-channel';\n\nimport { computerUseSessionApiError, MissingComputerUseApiError } from '../services/webComputerUseAvailability';\nimport {\n API_BASE_PATH,\n computerUseChannelType,\n COMPUTER_USE_ROUTES,\n type ComputerUseActivationRequest,\n type ComputerUseBrokerRequest,\n type ComputerUseBrowserCommand,\n type ComputerUseChannelPayload,\n type ComputerUseSessionView,\n} from '../types/computerUseApi';\nexport { computerUseChannelType };\n\n/** Computer use runs on the DoomPi Desktop host, so every other client declines it. */\nconst DESKTOP_UNAVAILABLE = 'DoomPi Desktop computer use is unavailable.';\n\nfunction record(value: unknown): Record<string, unknown> | undefined {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\nfunction command(value: unknown): ComputerUseBrowserCommand | undefined {\n const input = record(value);\n return input?.action === 'status' ||\n input?.action === 'targets' ||\n input?.action === 'stop' ||\n input?.action === 'artifacts'\n ? { action: input.action }\n : undefined;\n}\n\nasync function responseJson(response: Response): Promise<unknown> {\n const value = await response.json();\n if (!response.ok) throw computerUseSessionApiError(response.status, value);\n return value;\n}\n\nfunction sessionState(value: unknown): ComputerUseSessionView | undefined {\n const input = record(value);\n if (\n typeof input?.sessionId !== 'string' ||\n typeof input.revision !== 'number' ||\n typeof input.wake !== 'number' ||\n !['inactive', 'awaiting_confirmation', 'activating', 'active', 'stopping', 'failed'].includes(input.phase as string)\n )\n return undefined;\n return input as unknown as ComputerUseSessionView;\n}\n\nexport function createComputerUseChannel(): DoomHubChannel {\n let receiveCommand: ((scope: DoomHubSessionScope, payload: unknown) => void) | undefined;\n return {\n frameType: computerUseChannelType,\n lifecycle: 'hub',\n start(host) {\n const scopes = new Map<string, DoomHubSessionScope>();\n const latest = new Map<string, ComputerUseChannelPayload>();\n const targets = new Map<string, readonly Record<string, unknown>[]>();\n const grants = new Map<string, string>();\n const subscriptions = new Map<string, () => void>();\n const processing = new Set<string>();\n let activeSessionId: string | undefined;\n let closed = false;\n\n const sessionRequest = async (scope: DoomHubSessionScope, path: string, method = 'GET', value?: unknown) =>\n host.requestSessionApi(scope, {\n basePath: API_BASE_PATH,\n path,\n method,\n ...(value === undefined ? {} : { body: JSON.stringify(value) }),\n });\n\n const publish = (scope: DoomHubSessionScope, state: ComputerUseSessionView): void => {\n if (state.phase === 'active' || state.phase === 'activating' || state.phase === 'stopping') {\n activeSessionId = scope.sessionId;\n } else if (activeSessionId === scope.sessionId) {\n activeSessionId = undefined;\n grants.delete(scope.sessionId);\n }\n const payload: ComputerUseChannelPayload = {\n state,\n targets: targets.get(scope.sessionId) ?? [],\n ...(activeSessionId !== undefined && activeSessionId !== scope.sessionId ? { busy: true } : {}),\n };\n latest.set(scope.sessionId, payload);\n host.publish(scope.sessionId, payload);\n };\n\n const refresh = async (scope: DoomHubSessionScope): Promise<ComputerUseSessionView> => {\n const state = sessionState(await responseJson(await sessionRequest(scope, COMPUTER_USE_ROUTES.hubState)));\n if (state === undefined) throw new Error('Computer-use API returned an invalid session state.');\n publish(scope, state);\n return state;\n };\n\n const completePending = async (scope: DoomHubSessionScope, pending: ComputerUseBrokerRequest): Promise<void> => {\n if (!host.computerUse?.available) throw new Error(DESKTOP_UNAVAILABLE);\n try {\n const result = await host.computerUse.request(scope, {\n operation: pending.operation,\n payload:\n pending.operation === 'act'\n ? { grantId: pending.grantId, sequence: pending.sequence, action: pending.payload }\n : { grantId: pending.grantId },\n });\n await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubComplete, 'POST', { id: pending.id, result }),\n );\n } catch (error) {\n await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubComplete, 'POST', {\n id: pending.id,\n error: error instanceof Error ? error.message : String(error),\n }),\n );\n }\n };\n\n const stopSession = async (scope: DoomHubSessionScope): Promise<void> => {\n const authorization = (await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubAuthorization),\n )) as { grantId: string } | null;\n let artifact: unknown;\n if (authorization !== null && host.computerUse?.available) {\n try {\n artifact = await host.computerUse.request(scope, {\n operation: 'stop',\n payload: { grantId: authorization.grantId },\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n artifact = {\n artifactId: randomUUID(),\n status: 'failed',\n failure: { code: 'recording_failed', message: message.slice(0, 512) },\n };\n host.onNotice(`computer-use Desktop stop was already unavailable (${message})`);\n }\n }\n await responseJson(await sessionRequest(scope, COMPUTER_USE_ROUTES.hubStop, 'POST', { artifact }));\n grants.delete(scope.sessionId);\n if (activeSessionId === scope.sessionId) activeSessionId = undefined;\n };\n\n const processScope = async (scope: DoomHubSessionScope, announced?: ComputerUseSessionView): Promise<void> => {\n if (closed || processing.has(scope.sessionId)) return;\n processing.add(scope.sessionId);\n try {\n const state = announced ?? (await refresh(scope));\n if (state.phase === 'active') {\n if (!grants.has(scope.sessionId)) {\n const authorization = (await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubAuthorization),\n )) as { grantId: string } | null;\n if (authorization !== null) grants.set(scope.sessionId, authorization.grantId);\n }\n const expired = typeof state.expiresAt === 'number' && state.expiresAt <= Date.now();\n let owned = false;\n if (!expired && host.computerUse?.available) {\n try {\n const hostStatus = record(await host.computerUse.request(scope, { operation: 'status' }));\n owned = hostStatus?.ownedBySession === true;\n } catch {\n owned = true;\n }\n }\n if (expired || !owned) {\n await stopSession(scope);\n await refresh(scope);\n return;\n }\n }\n if (\n state.phase === 'awaiting_confirmation' &&\n (activeSessionId === undefined || activeSessionId === scope.sessionId)\n ) {\n const activation = (await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubActivation),\n )) as ComputerUseActivationRequest | null;\n if (activation !== null && !host.computerUse?.available) {\n // No desktop host: decline through the API's own failure path so the browser\n // shows why, instead of leaving the request pending or raising a server error.\n await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubStop, 'POST', { error: DESKTOP_UNAVAILABLE }),\n );\n await refresh(scope);\n return;\n }\n if (activation !== null && host.computerUse?.available) {\n activeSessionId = scope.sessionId;\n try {\n const hostResult = await host.computerUse.request(scope, {\n operation: 'activate',\n payload: activation,\n });\n await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubStop, 'POST', { host: hostResult }),\n );\n const grantId = record(hostResult)?.grantId;\n if (typeof grantId === 'string') grants.set(scope.sessionId, grantId);\n } catch (error) {\n activeSessionId = undefined;\n await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubStop, 'POST', {\n error: error instanceof Error ? error.message : String(error),\n }),\n );\n }\n await refresh(scope);\n }\n }\n if (state.phase === 'stopping') {\n await stopSession(scope);\n await refresh(scope);\n }\n const pending = (await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubNext),\n )) as ComputerUseBrokerRequest | null;\n if (pending !== null) await completePending(scope, pending);\n } catch (error) {\n if (error instanceof MissingComputerUseApiError) {\n latest.delete(scope.sessionId);\n } else {\n host.onNotice(\n `computer-use state handling failed for ${scope.sessionId} (${error instanceof Error ? error.message : String(error)})`,\n );\n }\n } finally {\n processing.delete(scope.sessionId);\n }\n };\n\n const onState = (scope: DoomHubSessionScope, payload: unknown): void => {\n const state = sessionState(payload);\n if (state === undefined || state.sessionId !== scope.sessionId) return;\n publish(scope, state);\n void processScope(scope, state);\n };\n\n receiveCommand = (scope, payload) => {\n const parsed = command(payload);\n if (parsed === undefined) return;\n void (async () => {\n try {\n if (parsed.action === 'targets') {\n // Without a desktop host there is nothing to capture, so the browser sees an\n // empty target list rather than an error it cannot act on.\n const result = host.computerUse?.available\n ? await host.computerUse.request(scope, { operation: 'targets' })\n : [];\n targets.set(\n scope.sessionId,\n Array.isArray(result)\n ? result.filter((item): item is Record<string, unknown> => record(item) !== undefined)\n : [],\n );\n } else if (parsed.action === 'stop') {\n await stopSession(scope);\n }\n await refresh(scope);\n void processScope(scope);\n } catch (error) {\n if (error instanceof MissingComputerUseApiError) {\n latest.delete(scope.sessionId);\n } else {\n host.onNotice(\n `computer-use command failed for ${scope.sessionId} (${error instanceof Error ? error.message : String(error)})`,\n );\n }\n }\n })();\n };\n\n const source: DoomHubChannelSource = {\n payloadFor: (scope) => latest.get(scope.sessionId),\n sessionAdded(scope) {\n scopes.set(scope.sessionId, scope);\n const unsubscribe = host.directEvents.subscribe(\n computerUseChannelType,\n scope.sessionId,\n (payload) => onState(scope, payload),\n { replayLatest: true },\n );\n subscriptions.set(scope.sessionId, unsubscribe);\n void processScope(scope);\n },\n sessionRemoved(sessionId) {\n const scope = scopes.get(sessionId);\n const grantId = grants.get(sessionId);\n subscriptions.get(sessionId)?.();\n subscriptions.delete(sessionId);\n if (scope !== undefined && grantId !== undefined && host.computerUse?.available) {\n void host.computerUse\n .request(scope, { operation: 'stop', payload: { grantId } })\n .catch((error: unknown) =>\n host.onNotice(\n `computer-use session cleanup failed (${error instanceof Error ? error.message : String(error)})`,\n ),\n );\n }\n scopes.delete(sessionId);\n latest.delete(sessionId);\n targets.delete(sessionId);\n grants.delete(sessionId);\n processing.delete(sessionId);\n if (activeSessionId === sessionId) activeSessionId = undefined;\n },\n close() {\n closed = true;\n for (const unsubscribe of subscriptions.values()) unsubscribe();\n subscriptions.clear();\n if (host.computerUse?.available) {\n for (const [sessionId, grantId] of grants) {\n const scope = scopes.get(sessionId);\n if (scope !== undefined)\n void host.computerUse\n .request(scope, { operation: 'stop', payload: { grantId } })\n .catch(() => undefined);\n }\n }\n scopes.clear();\n latest.clear();\n targets.clear();\n grants.clear();\n },\n };\n return source;\n },\n receive(scope, payload) {\n receiveCommand?.(scope, payload);\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{API_BASE_PATH as e,COMPUTER_USE_ROUTES as t,computerUseChannelType as n}from"../types/computerUseApi.mjs";import{MissingComputerUseApiError as r,computerUseSessionApiError as i}from"../services/webComputerUseAvailability/index.mjs";import{randomUUID as a}from"node:crypto";function
|
|
1
|
+
import{API_BASE_PATH as e,COMPUTER_USE_ROUTES as t,computerUseChannelType as n}from"../types/computerUseApi.mjs";import{MissingComputerUseApiError as r,computerUseSessionApiError as i}from"../services/webComputerUseAvailability/index.mjs";import{randomUUID as a}from"node:crypto";const o=`DoomPi Desktop computer use is unavailable.`;function s(e){return typeof e==`object`&&e&&!Array.isArray(e)?e:void 0}function c(e){let t=s(e);return t?.action===`status`||t?.action===`targets`||t?.action===`stop`||t?.action===`artifacts`?{action:t.action}:void 0}async function l(e){let t=await e.json();if(!e.ok)throw i(e.status,t);return t}function u(e){let t=s(e);if(typeof t?.sessionId==`string`&&typeof t.revision==`number`&&typeof t.wake==`number`&&[`inactive`,`awaiting_confirmation`,`activating`,`active`,`stopping`,`failed`].includes(t.phase))return t}function d(){let i;return{frameType:n,lifecycle:`hub`,start(d){let f=new Map,p=new Map,m=new Map,h=new Map,g=new Map,_=new Set,v,y=!1,b=async(t,n,r=`GET`,i)=>d.requestSessionApi(t,{basePath:e,path:n,method:r,...i===void 0?{}:{body:JSON.stringify(i)}}),x=(e,t)=>{t.phase===`active`||t.phase===`activating`||t.phase===`stopping`?v=e.sessionId:v===e.sessionId&&(v=void 0,h.delete(e.sessionId));let n={state:t,targets:m.get(e.sessionId)??[],...v!==void 0&&v!==e.sessionId?{busy:!0}:{}};p.set(e.sessionId,n),d.publish(e.sessionId,n)},S=async e=>{let n=u(await l(await b(e,t.hubState)));if(n===void 0)throw Error(`Computer-use API returned an invalid session state.`);return x(e,n),n},C=async(e,n)=>{if(!d.computerUse?.available)throw Error(o);try{let r=await d.computerUse.request(e,{operation:n.operation,payload:n.operation===`act`?{grantId:n.grantId,sequence:n.sequence,action:n.payload}:{grantId:n.grantId}});await l(await b(e,t.hubComplete,`POST`,{id:n.id,result:r}))}catch(r){await l(await b(e,t.hubComplete,`POST`,{id:n.id,error:r instanceof Error?r.message:String(r)}))}},w=async e=>{let n=await l(await b(e,t.hubAuthorization)),r;if(n!==null&&d.computerUse?.available)try{r=await d.computerUse.request(e,{operation:`stop`,payload:{grantId:n.grantId}})}catch(e){let t=e instanceof Error?e.message:String(e);r={artifactId:a(),status:`failed`,failure:{code:`recording_failed`,message:t.slice(0,512)}},d.onNotice(`computer-use Desktop stop was already unavailable (${t})`)}await l(await b(e,t.hubStop,`POST`,{artifact:r})),h.delete(e.sessionId),v===e.sessionId&&(v=void 0)},T=async(e,n)=>{if(!(y||_.has(e.sessionId))){_.add(e.sessionId);try{let r=n??await S(e);if(r.phase===`active`){if(!h.has(e.sessionId)){let n=await l(await b(e,t.hubAuthorization));n!==null&&h.set(e.sessionId,n.grantId)}let n=typeof r.expiresAt==`number`&&r.expiresAt<=Date.now(),i=!1;if(!n&&d.computerUse?.available)try{i=s(await d.computerUse.request(e,{operation:`status`}))?.ownedBySession===!0}catch{i=!0}if(n||!i){await w(e),await S(e);return}}if(r.phase===`awaiting_confirmation`&&(v===void 0||v===e.sessionId)){let n=await l(await b(e,t.hubActivation));if(n!==null&&!d.computerUse?.available){await l(await b(e,t.hubStop,`POST`,{error:o})),await S(e);return}if(n!==null&&d.computerUse?.available){v=e.sessionId;try{let r=await d.computerUse.request(e,{operation:`activate`,payload:n});await l(await b(e,t.hubStop,`POST`,{host:r}));let i=s(r)?.grantId;typeof i==`string`&&h.set(e.sessionId,i)}catch(n){v=void 0,await l(await b(e,t.hubStop,`POST`,{error:n instanceof Error?n.message:String(n)}))}await S(e)}}r.phase===`stopping`&&(await w(e),await S(e));let i=await l(await b(e,t.hubNext));i!==null&&await C(e,i)}catch(t){t instanceof r?p.delete(e.sessionId):d.onNotice(`computer-use state handling failed for ${e.sessionId} (${t instanceof Error?t.message:String(t)})`)}finally{_.delete(e.sessionId)}}},E=(e,t)=>{let n=u(t);n!==void 0&&n.sessionId===e.sessionId&&(x(e,n),T(e,n))};return i=(e,t)=>{let n=c(t);n!==void 0&&(async()=>{try{if(n.action===`targets`){let t=d.computerUse?.available?await d.computerUse.request(e,{operation:`targets`}):[];m.set(e.sessionId,Array.isArray(t)?t.filter(e=>s(e)!==void 0):[])}else n.action===`stop`&&await w(e);await S(e),T(e)}catch(t){t instanceof r?p.delete(e.sessionId):d.onNotice(`computer-use command failed for ${e.sessionId} (${t instanceof Error?t.message:String(t)})`)}})()},{payloadFor:e=>p.get(e.sessionId),sessionAdded(e){f.set(e.sessionId,e);let t=d.directEvents.subscribe(n,e.sessionId,t=>E(e,t),{replayLatest:!0});g.set(e.sessionId,t),T(e)},sessionRemoved(e){let t=f.get(e),n=h.get(e);g.get(e)?.(),g.delete(e),t!==void 0&&n!==void 0&&d.computerUse?.available&&d.computerUse.request(t,{operation:`stop`,payload:{grantId:n}}).catch(e=>d.onNotice(`computer-use session cleanup failed (${e instanceof Error?e.message:String(e)})`)),f.delete(e),p.delete(e),m.delete(e),h.delete(e),_.delete(e),v===e&&(v=void 0)},close(){y=!0;for(let e of g.values())e();if(g.clear(),d.computerUse?.available)for(let[e,t]of h){let n=f.get(e);n!==void 0&&d.computerUse.request(n,{operation:`stop`,payload:{grantId:t}}).catch(()=>void 0)}f.clear(),p.clear(),m.clear(),h.clear()}}},receive(e,t){i?.(e,t)}}}export{n as computerUseChannelType,d as createComputerUseChannel};
|
|
2
2
|
//# sourceMappingURL=webComputerUseChannel.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webComputerUseChannel.mjs","names":[],"sources":["../../src/controllers/webComputerUseChannel.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport type { DoomHubChannelSource, DoomHubSessionScope, DoomHubChannel } from '@agimon-ai/doompi-core/hub-channel';\n\nimport { computerUseSessionApiError, MissingComputerUseApiError } from '../services/webComputerUseAvailability';\nimport {\n API_BASE_PATH,\n computerUseChannelType,\n COMPUTER_USE_ROUTES,\n type ComputerUseActivationRequest,\n type ComputerUseBrokerRequest,\n type ComputerUseBrowserCommand,\n type ComputerUseChannelPayload,\n type ComputerUseSessionView,\n} from '../types/computerUseApi';\nexport { computerUseChannelType };\n\nfunction record(value: unknown): Record<string, unknown> | undefined {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\nfunction command(value: unknown): ComputerUseBrowserCommand | undefined {\n const input = record(value);\n return input?.action === 'status' ||\n input?.action === 'targets' ||\n input?.action === 'stop' ||\n input?.action === 'artifacts'\n ? { action: input.action }\n : undefined;\n}\n\nasync function responseJson(response: Response): Promise<unknown> {\n const value = await response.json();\n if (!response.ok) throw computerUseSessionApiError(response.status, value);\n return value;\n}\n\nfunction sessionState(value: unknown): ComputerUseSessionView | undefined {\n const input = record(value);\n if (\n typeof input?.sessionId !== 'string' ||\n typeof input.revision !== 'number' ||\n typeof input.wake !== 'number' ||\n !['inactive', 'awaiting_confirmation', 'activating', 'active', 'stopping', 'failed'].includes(input.phase as string)\n )\n return undefined;\n return input as unknown as ComputerUseSessionView;\n}\n\nexport function createComputerUseChannel(): DoomHubChannel {\n let receiveCommand: ((scope: DoomHubSessionScope, payload: unknown) => void) | undefined;\n return {\n frameType: computerUseChannelType,\n lifecycle: 'hub',\n start(host) {\n const scopes = new Map<string, DoomHubSessionScope>();\n const latest = new Map<string, ComputerUseChannelPayload>();\n const targets = new Map<string, readonly Record<string, unknown>[]>();\n const grants = new Map<string, string>();\n const subscriptions = new Map<string, () => void>();\n const processing = new Set<string>();\n let activeSessionId: string | undefined;\n let closed = false;\n\n const sessionRequest = async (scope: DoomHubSessionScope, path: string, method = 'GET', value?: unknown) =>\n host.requestSessionApi(scope, {\n basePath: API_BASE_PATH,\n path,\n method,\n ...(value === undefined ? {} : { body: JSON.stringify(value) }),\n });\n\n const publish = (scope: DoomHubSessionScope, state: ComputerUseSessionView): void => {\n if (state.phase === 'active' || state.phase === 'activating' || state.phase === 'stopping') {\n activeSessionId = scope.sessionId;\n } else if (activeSessionId === scope.sessionId) {\n activeSessionId = undefined;\n grants.delete(scope.sessionId);\n }\n const payload: ComputerUseChannelPayload = {\n state,\n targets: targets.get(scope.sessionId) ?? [],\n ...(activeSessionId !== undefined && activeSessionId !== scope.sessionId ? { busy: true } : {}),\n };\n latest.set(scope.sessionId, payload);\n host.publish(scope.sessionId, payload);\n };\n\n const refresh = async (scope: DoomHubSessionScope): Promise<ComputerUseSessionView> => {\n const state = sessionState(await responseJson(await sessionRequest(scope, COMPUTER_USE_ROUTES.hubState)));\n if (state === undefined) throw new Error('Computer-use API returned an invalid session state.');\n publish(scope, state);\n return state;\n };\n\n const completePending = async (scope: DoomHubSessionScope, pending: ComputerUseBrokerRequest): Promise<void> => {\n if (!host.computerUse?.available) throw new Error('DoomPi Desktop computer use is unavailable.');\n try {\n const result = await host.computerUse.request(scope, {\n operation: pending.operation,\n payload:\n pending.operation === 'act'\n ? { grantId: pending.grantId, sequence: pending.sequence, action: pending.payload }\n : { grantId: pending.grantId },\n });\n await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubComplete, 'POST', { id: pending.id, result }),\n );\n } catch (error) {\n await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubComplete, 'POST', {\n id: pending.id,\n error: error instanceof Error ? error.message : String(error),\n }),\n );\n }\n };\n\n const stopSession = async (scope: DoomHubSessionScope): Promise<void> => {\n const authorization = (await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubAuthorization),\n )) as { grantId: string } | null;\n let artifact: unknown;\n if (authorization !== null && host.computerUse?.available) {\n try {\n artifact = await host.computerUse.request(scope, {\n operation: 'stop',\n payload: { grantId: authorization.grantId },\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n artifact = {\n artifactId: randomUUID(),\n status: 'failed',\n failure: { code: 'recording_failed', message: message.slice(0, 512) },\n };\n host.onNotice(`computer-use Desktop stop was already unavailable (${message})`);\n }\n }\n await responseJson(await sessionRequest(scope, COMPUTER_USE_ROUTES.hubStop, 'POST', { artifact }));\n grants.delete(scope.sessionId);\n if (activeSessionId === scope.sessionId) activeSessionId = undefined;\n };\n\n const processScope = async (scope: DoomHubSessionScope, announced?: ComputerUseSessionView): Promise<void> => {\n if (closed || processing.has(scope.sessionId)) return;\n processing.add(scope.sessionId);\n try {\n const state = announced ?? (await refresh(scope));\n if (state.phase === 'active') {\n if (!grants.has(scope.sessionId)) {\n const authorization = (await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubAuthorization),\n )) as { grantId: string } | null;\n if (authorization !== null) grants.set(scope.sessionId, authorization.grantId);\n }\n const expired = typeof state.expiresAt === 'number' && state.expiresAt <= Date.now();\n let owned = false;\n if (!expired && host.computerUse?.available) {\n try {\n const hostStatus = record(await host.computerUse.request(scope, { operation: 'status' }));\n owned = hostStatus?.ownedBySession === true;\n } catch {\n owned = true;\n }\n }\n if (expired || !owned) {\n await stopSession(scope);\n await refresh(scope);\n return;\n }\n }\n if (\n state.phase === 'awaiting_confirmation' &&\n (activeSessionId === undefined || activeSessionId === scope.sessionId)\n ) {\n if (!host.computerUse?.available) throw new Error('DoomPi Desktop computer use is unavailable.');\n const activation = (await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubActivation),\n )) as ComputerUseActivationRequest | null;\n if (activation !== null) {\n activeSessionId = scope.sessionId;\n try {\n const hostResult = await host.computerUse.request(scope, {\n operation: 'activate',\n payload: activation,\n });\n await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubStop, 'POST', { host: hostResult }),\n );\n const grantId = record(hostResult)?.grantId;\n if (typeof grantId === 'string') grants.set(scope.sessionId, grantId);\n } catch (error) {\n activeSessionId = undefined;\n await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubStop, 'POST', {\n error: error instanceof Error ? error.message : String(error),\n }),\n );\n }\n await refresh(scope);\n }\n }\n if (state.phase === 'stopping') {\n await stopSession(scope);\n await refresh(scope);\n }\n const pending = (await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubNext),\n )) as ComputerUseBrokerRequest | null;\n if (pending !== null) await completePending(scope, pending);\n } catch (error) {\n if (error instanceof MissingComputerUseApiError) {\n latest.delete(scope.sessionId);\n } else {\n host.onNotice(\n `computer-use state handling failed for ${scope.sessionId} (${error instanceof Error ? error.message : String(error)})`,\n );\n }\n } finally {\n processing.delete(scope.sessionId);\n }\n };\n\n const onState = (scope: DoomHubSessionScope, payload: unknown): void => {\n const state = sessionState(payload);\n if (state === undefined || state.sessionId !== scope.sessionId) return;\n publish(scope, state);\n void processScope(scope, state);\n };\n\n receiveCommand = (scope, payload) => {\n const parsed = command(payload);\n if (parsed === undefined) return;\n void (async () => {\n try {\n if (parsed.action === 'targets') {\n if (!host.computerUse?.available) throw new Error('DoomPi Desktop computer use is unavailable.');\n const result = await host.computerUse.request(scope, { operation: 'targets' });\n targets.set(\n scope.sessionId,\n Array.isArray(result)\n ? result.filter((item): item is Record<string, unknown> => record(item) !== undefined)\n : [],\n );\n } else if (parsed.action === 'stop') {\n await stopSession(scope);\n }\n await refresh(scope);\n void processScope(scope);\n } catch (error) {\n if (error instanceof MissingComputerUseApiError) {\n latest.delete(scope.sessionId);\n } else {\n host.onNotice(\n `computer-use command failed for ${scope.sessionId} (${error instanceof Error ? error.message : String(error)})`,\n );\n }\n }\n })();\n };\n\n const source: DoomHubChannelSource = {\n payloadFor: (scope) => latest.get(scope.sessionId),\n sessionAdded(scope) {\n scopes.set(scope.sessionId, scope);\n const unsubscribe = host.directEvents.subscribe(\n computerUseChannelType,\n scope.sessionId,\n (payload) => onState(scope, payload),\n { replayLatest: true },\n );\n subscriptions.set(scope.sessionId, unsubscribe);\n void processScope(scope);\n },\n sessionRemoved(sessionId) {\n const scope = scopes.get(sessionId);\n const grantId = grants.get(sessionId);\n subscriptions.get(sessionId)?.();\n subscriptions.delete(sessionId);\n if (scope !== undefined && grantId !== undefined && host.computerUse?.available) {\n void host.computerUse\n .request(scope, { operation: 'stop', payload: { grantId } })\n .catch((error: unknown) =>\n host.onNotice(\n `computer-use session cleanup failed (${error instanceof Error ? error.message : String(error)})`,\n ),\n );\n }\n scopes.delete(sessionId);\n latest.delete(sessionId);\n targets.delete(sessionId);\n grants.delete(sessionId);\n processing.delete(sessionId);\n if (activeSessionId === sessionId) activeSessionId = undefined;\n },\n close() {\n closed = true;\n for (const unsubscribe of subscriptions.values()) unsubscribe();\n subscriptions.clear();\n if (host.computerUse?.available) {\n for (const [sessionId, grantId] of grants) {\n const scope = scopes.get(sessionId);\n if (scope !== undefined)\n void host.computerUse\n .request(scope, { operation: 'stop', payload: { grantId } })\n .catch(() => undefined);\n }\n }\n scopes.clear();\n latest.clear();\n targets.clear();\n grants.clear();\n },\n };\n return source;\n },\n receive(scope, payload) {\n receiveCommand?.(scope, payload);\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"webComputerUseChannel.mjs","names":[],"sources":["../../src/controllers/webComputerUseChannel.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport type { DoomHubChannelSource, DoomHubSessionScope, DoomHubChannel } from '@agimon-ai/doompi-core/hub-channel';\n\nimport { computerUseSessionApiError, MissingComputerUseApiError } from '../services/webComputerUseAvailability';\nimport {\n API_BASE_PATH,\n computerUseChannelType,\n COMPUTER_USE_ROUTES,\n type ComputerUseActivationRequest,\n type ComputerUseBrokerRequest,\n type ComputerUseBrowserCommand,\n type ComputerUseChannelPayload,\n type ComputerUseSessionView,\n} from '../types/computerUseApi';\nexport { computerUseChannelType };\n\n/** Computer use runs on the DoomPi Desktop host, so every other client declines it. */\nconst DESKTOP_UNAVAILABLE = 'DoomPi Desktop computer use is unavailable.';\n\nfunction record(value: unknown): Record<string, unknown> | undefined {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\nfunction command(value: unknown): ComputerUseBrowserCommand | undefined {\n const input = record(value);\n return input?.action === 'status' ||\n input?.action === 'targets' ||\n input?.action === 'stop' ||\n input?.action === 'artifacts'\n ? { action: input.action }\n : undefined;\n}\n\nasync function responseJson(response: Response): Promise<unknown> {\n const value = await response.json();\n if (!response.ok) throw computerUseSessionApiError(response.status, value);\n return value;\n}\n\nfunction sessionState(value: unknown): ComputerUseSessionView | undefined {\n const input = record(value);\n if (\n typeof input?.sessionId !== 'string' ||\n typeof input.revision !== 'number' ||\n typeof input.wake !== 'number' ||\n !['inactive', 'awaiting_confirmation', 'activating', 'active', 'stopping', 'failed'].includes(input.phase as string)\n )\n return undefined;\n return input as unknown as ComputerUseSessionView;\n}\n\nexport function createComputerUseChannel(): DoomHubChannel {\n let receiveCommand: ((scope: DoomHubSessionScope, payload: unknown) => void) | undefined;\n return {\n frameType: computerUseChannelType,\n lifecycle: 'hub',\n start(host) {\n const scopes = new Map<string, DoomHubSessionScope>();\n const latest = new Map<string, ComputerUseChannelPayload>();\n const targets = new Map<string, readonly Record<string, unknown>[]>();\n const grants = new Map<string, string>();\n const subscriptions = new Map<string, () => void>();\n const processing = new Set<string>();\n let activeSessionId: string | undefined;\n let closed = false;\n\n const sessionRequest = async (scope: DoomHubSessionScope, path: string, method = 'GET', value?: unknown) =>\n host.requestSessionApi(scope, {\n basePath: API_BASE_PATH,\n path,\n method,\n ...(value === undefined ? {} : { body: JSON.stringify(value) }),\n });\n\n const publish = (scope: DoomHubSessionScope, state: ComputerUseSessionView): void => {\n if (state.phase === 'active' || state.phase === 'activating' || state.phase === 'stopping') {\n activeSessionId = scope.sessionId;\n } else if (activeSessionId === scope.sessionId) {\n activeSessionId = undefined;\n grants.delete(scope.sessionId);\n }\n const payload: ComputerUseChannelPayload = {\n state,\n targets: targets.get(scope.sessionId) ?? [],\n ...(activeSessionId !== undefined && activeSessionId !== scope.sessionId ? { busy: true } : {}),\n };\n latest.set(scope.sessionId, payload);\n host.publish(scope.sessionId, payload);\n };\n\n const refresh = async (scope: DoomHubSessionScope): Promise<ComputerUseSessionView> => {\n const state = sessionState(await responseJson(await sessionRequest(scope, COMPUTER_USE_ROUTES.hubState)));\n if (state === undefined) throw new Error('Computer-use API returned an invalid session state.');\n publish(scope, state);\n return state;\n };\n\n const completePending = async (scope: DoomHubSessionScope, pending: ComputerUseBrokerRequest): Promise<void> => {\n if (!host.computerUse?.available) throw new Error(DESKTOP_UNAVAILABLE);\n try {\n const result = await host.computerUse.request(scope, {\n operation: pending.operation,\n payload:\n pending.operation === 'act'\n ? { grantId: pending.grantId, sequence: pending.sequence, action: pending.payload }\n : { grantId: pending.grantId },\n });\n await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubComplete, 'POST', { id: pending.id, result }),\n );\n } catch (error) {\n await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubComplete, 'POST', {\n id: pending.id,\n error: error instanceof Error ? error.message : String(error),\n }),\n );\n }\n };\n\n const stopSession = async (scope: DoomHubSessionScope): Promise<void> => {\n const authorization = (await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubAuthorization),\n )) as { grantId: string } | null;\n let artifact: unknown;\n if (authorization !== null && host.computerUse?.available) {\n try {\n artifact = await host.computerUse.request(scope, {\n operation: 'stop',\n payload: { grantId: authorization.grantId },\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n artifact = {\n artifactId: randomUUID(),\n status: 'failed',\n failure: { code: 'recording_failed', message: message.slice(0, 512) },\n };\n host.onNotice(`computer-use Desktop stop was already unavailable (${message})`);\n }\n }\n await responseJson(await sessionRequest(scope, COMPUTER_USE_ROUTES.hubStop, 'POST', { artifact }));\n grants.delete(scope.sessionId);\n if (activeSessionId === scope.sessionId) activeSessionId = undefined;\n };\n\n const processScope = async (scope: DoomHubSessionScope, announced?: ComputerUseSessionView): Promise<void> => {\n if (closed || processing.has(scope.sessionId)) return;\n processing.add(scope.sessionId);\n try {\n const state = announced ?? (await refresh(scope));\n if (state.phase === 'active') {\n if (!grants.has(scope.sessionId)) {\n const authorization = (await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubAuthorization),\n )) as { grantId: string } | null;\n if (authorization !== null) grants.set(scope.sessionId, authorization.grantId);\n }\n const expired = typeof state.expiresAt === 'number' && state.expiresAt <= Date.now();\n let owned = false;\n if (!expired && host.computerUse?.available) {\n try {\n const hostStatus = record(await host.computerUse.request(scope, { operation: 'status' }));\n owned = hostStatus?.ownedBySession === true;\n } catch {\n owned = true;\n }\n }\n if (expired || !owned) {\n await stopSession(scope);\n await refresh(scope);\n return;\n }\n }\n if (\n state.phase === 'awaiting_confirmation' &&\n (activeSessionId === undefined || activeSessionId === scope.sessionId)\n ) {\n const activation = (await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubActivation),\n )) as ComputerUseActivationRequest | null;\n if (activation !== null && !host.computerUse?.available) {\n // No desktop host: decline through the API's own failure path so the browser\n // shows why, instead of leaving the request pending or raising a server error.\n await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubStop, 'POST', { error: DESKTOP_UNAVAILABLE }),\n );\n await refresh(scope);\n return;\n }\n if (activation !== null && host.computerUse?.available) {\n activeSessionId = scope.sessionId;\n try {\n const hostResult = await host.computerUse.request(scope, {\n operation: 'activate',\n payload: activation,\n });\n await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubStop, 'POST', { host: hostResult }),\n );\n const grantId = record(hostResult)?.grantId;\n if (typeof grantId === 'string') grants.set(scope.sessionId, grantId);\n } catch (error) {\n activeSessionId = undefined;\n await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubStop, 'POST', {\n error: error instanceof Error ? error.message : String(error),\n }),\n );\n }\n await refresh(scope);\n }\n }\n if (state.phase === 'stopping') {\n await stopSession(scope);\n await refresh(scope);\n }\n const pending = (await responseJson(\n await sessionRequest(scope, COMPUTER_USE_ROUTES.hubNext),\n )) as ComputerUseBrokerRequest | null;\n if (pending !== null) await completePending(scope, pending);\n } catch (error) {\n if (error instanceof MissingComputerUseApiError) {\n latest.delete(scope.sessionId);\n } else {\n host.onNotice(\n `computer-use state handling failed for ${scope.sessionId} (${error instanceof Error ? error.message : String(error)})`,\n );\n }\n } finally {\n processing.delete(scope.sessionId);\n }\n };\n\n const onState = (scope: DoomHubSessionScope, payload: unknown): void => {\n const state = sessionState(payload);\n if (state === undefined || state.sessionId !== scope.sessionId) return;\n publish(scope, state);\n void processScope(scope, state);\n };\n\n receiveCommand = (scope, payload) => {\n const parsed = command(payload);\n if (parsed === undefined) return;\n void (async () => {\n try {\n if (parsed.action === 'targets') {\n // Without a desktop host there is nothing to capture, so the browser sees an\n // empty target list rather than an error it cannot act on.\n const result = host.computerUse?.available\n ? await host.computerUse.request(scope, { operation: 'targets' })\n : [];\n targets.set(\n scope.sessionId,\n Array.isArray(result)\n ? result.filter((item): item is Record<string, unknown> => record(item) !== undefined)\n : [],\n );\n } else if (parsed.action === 'stop') {\n await stopSession(scope);\n }\n await refresh(scope);\n void processScope(scope);\n } catch (error) {\n if (error instanceof MissingComputerUseApiError) {\n latest.delete(scope.sessionId);\n } else {\n host.onNotice(\n `computer-use command failed for ${scope.sessionId} (${error instanceof Error ? error.message : String(error)})`,\n );\n }\n }\n })();\n };\n\n const source: DoomHubChannelSource = {\n payloadFor: (scope) => latest.get(scope.sessionId),\n sessionAdded(scope) {\n scopes.set(scope.sessionId, scope);\n const unsubscribe = host.directEvents.subscribe(\n computerUseChannelType,\n scope.sessionId,\n (payload) => onState(scope, payload),\n { replayLatest: true },\n );\n subscriptions.set(scope.sessionId, unsubscribe);\n void processScope(scope);\n },\n sessionRemoved(sessionId) {\n const scope = scopes.get(sessionId);\n const grantId = grants.get(sessionId);\n subscriptions.get(sessionId)?.();\n subscriptions.delete(sessionId);\n if (scope !== undefined && grantId !== undefined && host.computerUse?.available) {\n void host.computerUse\n .request(scope, { operation: 'stop', payload: { grantId } })\n .catch((error: unknown) =>\n host.onNotice(\n `computer-use session cleanup failed (${error instanceof Error ? error.message : String(error)})`,\n ),\n );\n }\n scopes.delete(sessionId);\n latest.delete(sessionId);\n targets.delete(sessionId);\n grants.delete(sessionId);\n processing.delete(sessionId);\n if (activeSessionId === sessionId) activeSessionId = undefined;\n },\n close() {\n closed = true;\n for (const unsubscribe of subscriptions.values()) unsubscribe();\n subscriptions.clear();\n if (host.computerUse?.available) {\n for (const [sessionId, grantId] of grants) {\n const scope = scopes.get(sessionId);\n if (scope !== undefined)\n void host.computerUse\n .request(scope, { operation: 'stop', payload: { grantId } })\n .catch(() => undefined);\n }\n }\n scopes.clear();\n latest.clear();\n targets.clear();\n grants.clear();\n },\n };\n return source;\n },\n receive(scope, payload) {\n receiveCommand?.(scope, payload);\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e={activate:`/activate`,agentState:`/agent/state`,agentObserve:`/agent/observe`,agentAction:`/agent/action`,agentStop:`/agent/stop`,hubState:`/hub/state`,hubActivation:`/hub/activation`,hubAuthorization:`/hub/authorization`,hubNext:`/hub/next`,hubComplete:`/hub/complete`,hubStop:`/hub/stop`};exports.API_BASE_PATH=`computer-use`,exports.COMPUTER_USE_CONFIRMATION_WINDOW_MS=12e4,exports.COMPUTER_USE_MAX_DURATION_MS=18e5,exports.COMPUTER_USE_MODE_ID=`computer-use`,exports.COMPUTER_USE_MODE_STATUS_KEY=`doom-computer-use-mode`,exports.COMPUTER_USE_ROUTES=e,exports.COMPUTER_USE_STATUS_KEY=`doom-computer-use`,exports.COMPUTER_USE_WAKE_LIMIT=1e6,exports.computerUseChannelType=`computer_use_state`;
|
|
1
|
+
require("@agimon-ai/doompi-core/web");const e={activate:`/activate`,agentState:`/agent/state`,agentObserve:`/agent/observe`,agentAction:`/agent/action`,agentStop:`/agent/stop`,hubState:`/hub/state`,hubActivation:`/hub/activation`,hubAuthorization:`/hub/authorization`,hubNext:`/hub/next`,hubComplete:`/hub/complete`,hubStop:`/hub/stop`};exports.API_BASE_PATH=`computer-use`,exports.COMPUTER_USE_CONFIRMATION_WINDOW_MS=12e4,exports.COMPUTER_USE_MAX_DURATION_MS=18e5,exports.COMPUTER_USE_MODE_ID=`computer-use`,exports.COMPUTER_USE_MODE_STATUS_KEY=`doom-computer-use-mode`,exports.COMPUTER_USE_ROUTES=e,exports.COMPUTER_USE_STATUS_KEY=`doom-computer-use`,exports.COMPUTER_USE_WAKE_LIMIT=1e6,exports.computerUseChannelType=`computer_use_state`;
|
|
2
2
|
//# sourceMappingURL=computerUseApi.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"computerUseApi.cjs","names":[],"sources":["../../src/types/computerUseApi.ts"],"sourcesContent":["import type { DoomApiCaller } from '@agimon-ai/doompi-core/package-api';\n\nexport const API_BASE_PATH = 'computer-use';\nexport const computerUseChannelType = 'computer_use_state';\nexport const COMPUTER_USE_MODE_ID = 'computer-use';\nexport const COMPUTER_USE_STATUS_KEY = 'doom-computer-use';\nexport const COMPUTER_USE_MODE_STATUS_KEY = 'doom-computer-use-mode';\nexport const SESSION_QUERY_PARAM = 'session';\nexport const COMPUTER_USE_MAX_DURATION_MS = 1_800 * 1_000;\nexport const COMPUTER_USE_DEFAULT_DURATION_MS = 300 * 1_000;\nexport const COMPUTER_USE_CONFIRMATION_WINDOW_MS = 2 * 60 * 1_000;\n\nexport function activationUrl(sessionId: string): string {\n return
|
|
1
|
+
{"version":3,"file":"computerUseApi.cjs","names":[],"sources":["../../src/types/computerUseApi.ts"],"sourcesContent":["import type { DoomApiCaller } from '@agimon-ai/doompi-core/package-api';\nimport { sessionApiPath } from '@agimon-ai/doompi-core/web';\n\nexport const API_BASE_PATH = 'computer-use';\nexport const computerUseChannelType = 'computer_use_state';\nexport const COMPUTER_USE_MODE_ID = 'computer-use';\nexport const COMPUTER_USE_STATUS_KEY = 'doom-computer-use';\nexport const COMPUTER_USE_MODE_STATUS_KEY = 'doom-computer-use-mode';\nexport const SESSION_QUERY_PARAM = 'session';\nexport const COMPUTER_USE_MAX_DURATION_MS = 1_800 * 1_000;\nexport const COMPUTER_USE_DEFAULT_DURATION_MS = 300 * 1_000;\nexport const COMPUTER_USE_CONFIRMATION_WINDOW_MS = 2 * 60 * 1_000;\n\nexport function activationUrl(sessionId: string): string {\n return `${sessionApiPath(sessionId)}/plugins/${API_BASE_PATH}/activate?${SESSION_QUERY_PARAM}=${encodeURIComponent(sessionId)}`;\n}\n\nexport const COMPUTER_USE_ROUTES = {\n activate: '/activate',\n agentState: '/agent/state',\n agentObserve: '/agent/observe',\n agentAction: '/agent/action',\n agentStop: '/agent/stop',\n hubState: '/hub/state',\n hubActivation: '/hub/activation',\n hubAuthorization: '/hub/authorization',\n hubNext: '/hub/next',\n hubComplete: '/hub/complete',\n hubStop: '/hub/stop',\n} as const;\n\nexport const COMPUTER_USE_WAKE_LIMIT = 1_000_000;\n\nexport interface ComputerUseArtifactView {\n readonly artifactId: string;\n readonly status: 'ready' | 'failed';\n readonly downloadUrl?: string;\n readonly previewUrl?: string;\n readonly actionCount?: number;\n readonly completedAt?: string;\n readonly failure?: { readonly code: string; readonly message: string };\n}\n\nexport interface ComputerUseSessionView {\n readonly sessionId: string;\n readonly revision: number;\n readonly wake: number;\n readonly phase: 'inactive' | 'awaiting_confirmation' | 'activating' | 'active' | 'stopping' | 'failed';\n readonly requestId?: string;\n readonly target?: Record<string, unknown>;\n readonly durationMs?: number;\n readonly expiresAt?: number;\n readonly failure?: { readonly code: string; readonly message: string };\n readonly artifact?: ComputerUseArtifactView;\n}\n\nexport interface ComputerUseActivationRequest {\n readonly requestId: string;\n readonly target: Record<string, unknown>;\n readonly durationSeconds: number;\n readonly createdAt: number;\n readonly confirmationExpiresAt: number;\n readonly caller: DoomApiCaller;\n}\n\nexport interface ComputerUseBrokerRequest {\n readonly id: string;\n readonly operation: 'observe' | 'act';\n readonly payload?: unknown;\n readonly grantId: string;\n readonly sequence?: number;\n}\n\nexport interface ComputerUseChannelPayload {\n readonly state: ComputerUseSessionView;\n readonly targets: readonly Record<string, unknown>[];\n readonly busy?: true;\n}\n\nexport type ComputerUseBrowserCommand = { readonly action: 'status' | 'targets' | 'stop' | 'artifacts' };\n"],"mappings":"sCAGA,MAca,EAAsB,CACjC,SAAU,YACV,WAAY,eACZ,aAAc,iBACd,YAAa,gBACb,UAAW,cACX,SAAU,aACV,cAAe,kBACf,iBAAkB,qBAClB,QAAS,YACT,YAAa,gBACb,QAAS,WACX"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"computerUseApi.d.cts","names":[],"sources":["../../src/types/computerUseApi.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"computerUseApi.d.cts","names":[],"sources":["../../src/types/computerUseApi.ts"],"mappings":";;qBAGa;qBACA;qBACA;qBACA;qBACA;qBACA;qBACA;qBACA;qBACA;wBAEG,cAAc;qBAIjB;WACX;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;;qBAGW;iBAEI;WACN;WACA;WACA;WACA;WACA;WACA;WACA;aAAqB;aAAuB;;;iBAGtC;WACN;WACA;WACA;WACA;WACA;WACA,SAAS;WACT;WACA;WACA;aAAqB;aAAuB;;WAC5C,WAAW;;iBAGL;WACN;WACA,QAAQ;WACR;WACA;WACA;WACA,QAAQ;;iBAGF;WACN;WACA;WACA;WACA;WACA;;iBAGM;WACN,OAAO;WACP,kBAAkB;WAClB;;YAGC;WAAuC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"computerUseApi.d.mts","names":[],"sources":["../../src/types/computerUseApi.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"computerUseApi.d.mts","names":[],"sources":["../../src/types/computerUseApi.ts"],"mappings":";;qBAGa;qBACA;qBACA;qBACA;qBACA;qBACA;qBACA;qBACA;qBACA;wBAEG,cAAc;qBAIjB;WACX;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;;qBAGW;iBAEI;WACN;WACA;WACA;WACA;WACA;WACA;WACA;aAAqB;aAAuB;;;iBAGtC;WACN;WACA;WACA;WACA;WACA;WACA,SAAS;WACT;WACA;WACA;aAAqB;aAAuB;;WAC5C,WAAW;;iBAGL;WACN;WACA,QAAQ;WACR;WACA;WACA;WACA,QAAQ;;iBAGF;WACN;WACA;WACA;WACA;WACA;;iBAGM;WACN,OAAO;WACP,kBAAkB;WAClB;;YAGC;WAAuC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=`computer-use`,t=`computer_use_state`,n=`computer-use`,r=`doom-computer-use`,i=`doom-computer-use-mode`,a=18e5,o=12e4,s={activate:`/activate`,agentState:`/agent/state`,agentObserve:`/agent/observe`,agentAction:`/agent/action`,agentStop:`/agent/stop`,hubState:`/hub/state`,hubActivation:`/hub/activation`,hubAuthorization:`/hub/authorization`,hubNext:`/hub/next`,hubComplete:`/hub/complete`,hubStop:`/hub/stop`},c=1e6;export{e as API_BASE_PATH,o as COMPUTER_USE_CONFIRMATION_WINDOW_MS,a as COMPUTER_USE_MAX_DURATION_MS,n as COMPUTER_USE_MODE_ID,i as COMPUTER_USE_MODE_STATUS_KEY,s as COMPUTER_USE_ROUTES,r as COMPUTER_USE_STATUS_KEY,c as COMPUTER_USE_WAKE_LIMIT,t as computerUseChannelType};
|
|
1
|
+
import"@agimon-ai/doompi-core/web";const e=`computer-use`,t=`computer_use_state`,n=`computer-use`,r=`doom-computer-use`,i=`doom-computer-use-mode`,a=18e5,o=12e4,s={activate:`/activate`,agentState:`/agent/state`,agentObserve:`/agent/observe`,agentAction:`/agent/action`,agentStop:`/agent/stop`,hubState:`/hub/state`,hubActivation:`/hub/activation`,hubAuthorization:`/hub/authorization`,hubNext:`/hub/next`,hubComplete:`/hub/complete`,hubStop:`/hub/stop`},c=1e6;export{e as API_BASE_PATH,o as COMPUTER_USE_CONFIRMATION_WINDOW_MS,a as COMPUTER_USE_MAX_DURATION_MS,n as COMPUTER_USE_MODE_ID,i as COMPUTER_USE_MODE_STATUS_KEY,s as COMPUTER_USE_ROUTES,r as COMPUTER_USE_STATUS_KEY,c as COMPUTER_USE_WAKE_LIMIT,t as computerUseChannelType};
|
|
2
2
|
//# sourceMappingURL=computerUseApi.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"computerUseApi.mjs","names":[],"sources":["../../src/types/computerUseApi.ts"],"sourcesContent":["import type { DoomApiCaller } from '@agimon-ai/doompi-core/package-api';\n\nexport const API_BASE_PATH = 'computer-use';\nexport const computerUseChannelType = 'computer_use_state';\nexport const COMPUTER_USE_MODE_ID = 'computer-use';\nexport const COMPUTER_USE_STATUS_KEY = 'doom-computer-use';\nexport const COMPUTER_USE_MODE_STATUS_KEY = 'doom-computer-use-mode';\nexport const SESSION_QUERY_PARAM = 'session';\nexport const COMPUTER_USE_MAX_DURATION_MS = 1_800 * 1_000;\nexport const COMPUTER_USE_DEFAULT_DURATION_MS = 300 * 1_000;\nexport const COMPUTER_USE_CONFIRMATION_WINDOW_MS = 2 * 60 * 1_000;\n\nexport function activationUrl(sessionId: string): string {\n return
|
|
1
|
+
{"version":3,"file":"computerUseApi.mjs","names":[],"sources":["../../src/types/computerUseApi.ts"],"sourcesContent":["import type { DoomApiCaller } from '@agimon-ai/doompi-core/package-api';\nimport { sessionApiPath } from '@agimon-ai/doompi-core/web';\n\nexport const API_BASE_PATH = 'computer-use';\nexport const computerUseChannelType = 'computer_use_state';\nexport const COMPUTER_USE_MODE_ID = 'computer-use';\nexport const COMPUTER_USE_STATUS_KEY = 'doom-computer-use';\nexport const COMPUTER_USE_MODE_STATUS_KEY = 'doom-computer-use-mode';\nexport const SESSION_QUERY_PARAM = 'session';\nexport const COMPUTER_USE_MAX_DURATION_MS = 1_800 * 1_000;\nexport const COMPUTER_USE_DEFAULT_DURATION_MS = 300 * 1_000;\nexport const COMPUTER_USE_CONFIRMATION_WINDOW_MS = 2 * 60 * 1_000;\n\nexport function activationUrl(sessionId: string): string {\n return `${sessionApiPath(sessionId)}/plugins/${API_BASE_PATH}/activate?${SESSION_QUERY_PARAM}=${encodeURIComponent(sessionId)}`;\n}\n\nexport const COMPUTER_USE_ROUTES = {\n activate: '/activate',\n agentState: '/agent/state',\n agentObserve: '/agent/observe',\n agentAction: '/agent/action',\n agentStop: '/agent/stop',\n hubState: '/hub/state',\n hubActivation: '/hub/activation',\n hubAuthorization: '/hub/authorization',\n hubNext: '/hub/next',\n hubComplete: '/hub/complete',\n hubStop: '/hub/stop',\n} as const;\n\nexport const COMPUTER_USE_WAKE_LIMIT = 1_000_000;\n\nexport interface ComputerUseArtifactView {\n readonly artifactId: string;\n readonly status: 'ready' | 'failed';\n readonly downloadUrl?: string;\n readonly previewUrl?: string;\n readonly actionCount?: number;\n readonly completedAt?: string;\n readonly failure?: { readonly code: string; readonly message: string };\n}\n\nexport interface ComputerUseSessionView {\n readonly sessionId: string;\n readonly revision: number;\n readonly wake: number;\n readonly phase: 'inactive' | 'awaiting_confirmation' | 'activating' | 'active' | 'stopping' | 'failed';\n readonly requestId?: string;\n readonly target?: Record<string, unknown>;\n readonly durationMs?: number;\n readonly expiresAt?: number;\n readonly failure?: { readonly code: string; readonly message: string };\n readonly artifact?: ComputerUseArtifactView;\n}\n\nexport interface ComputerUseActivationRequest {\n readonly requestId: string;\n readonly target: Record<string, unknown>;\n readonly durationSeconds: number;\n readonly createdAt: number;\n readonly confirmationExpiresAt: number;\n readonly caller: DoomApiCaller;\n}\n\nexport interface ComputerUseBrokerRequest {\n readonly id: string;\n readonly operation: 'observe' | 'act';\n readonly payload?: unknown;\n readonly grantId: string;\n readonly sequence?: number;\n}\n\nexport interface ComputerUseChannelPayload {\n readonly state: ComputerUseSessionView;\n readonly targets: readonly Record<string, unknown>[];\n readonly busy?: true;\n}\n\nexport type ComputerUseBrowserCommand = { readonly action: 'status' | 'targets' | 'stop' | 'artifacts' };\n"],"mappings":"mCAGA,MAAa,EAAgB,eAChB,EAAyB,qBACzB,EAAuB,eACvB,EAA0B,oBAC1B,EAA+B,yBAE/B,EAA+B,KAE/B,EAAsC,KAMtC,EAAsB,CACjC,SAAU,YACV,WAAY,eACZ,aAAc,iBACd,YAAa,gBACb,UAAW,cACX,SAAU,aACV,cAAe,kBACf,iBAAkB,qBAClB,QAAS,YACT,YAAa,gBACb,QAAS,WACX,EAEa,EAA0B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agimon-ai/doompi-computer-use",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.16",
|
|
4
4
|
"description": "Session scoped semantic computer control through the DoomPi Desktop capability.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -64,26 +64,26 @@
|
|
|
64
64
|
"access": "public"
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
|
+
"@agimon-ai/doompi-config": "0.0.1-alpha.71",
|
|
68
|
+
"@agimon-ai/doompi-core": "0.0.1-alpha.72",
|
|
69
|
+
"@agimon-ai/doompi-minor-mode": "0.0.1-alpha.72",
|
|
70
|
+
"@agimon-ai/doompi-web-components": "0.0.1-alpha.30",
|
|
67
71
|
"@deepseek-ai/cordis": "4.0.2",
|
|
68
|
-
"typebox": "1.3.
|
|
69
|
-
"@agimon-ai/doompi-config": "0.0.1-alpha.69",
|
|
70
|
-
"@agimon-ai/doompi-web-components": "0.0.1-alpha.29",
|
|
71
|
-
"@agimon-ai/doompi-minor-mode": "0.0.1-alpha.70",
|
|
72
|
-
"@agimon-ai/doompi-core": "0.0.1-alpha.70"
|
|
72
|
+
"typebox": "1.3.30"
|
|
73
73
|
},
|
|
74
74
|
"devDependencies": {
|
|
75
|
+
"@agimon-ai/vibe-lint-plugin-doom-extension": "0.0.1-alpha.67",
|
|
75
76
|
"@earendil-works/pi-coding-agent": "0.85.1",
|
|
76
77
|
"@tanstack/react-store": "0.11.1",
|
|
77
78
|
"@tanstack/store": "0.11.1",
|
|
78
|
-
"@types/node": "26.
|
|
79
|
-
"@types/react": "19.
|
|
79
|
+
"@types/node": "26.5.1",
|
|
80
|
+
"@types/react": "19.3.0",
|
|
80
81
|
"@vitest/coverage-v8": "5.0.0",
|
|
81
|
-
"react": "19.
|
|
82
|
-
"react-dom": "19.
|
|
82
|
+
"react": "19.3.0",
|
|
83
|
+
"react-dom": "19.3.0",
|
|
83
84
|
"tsdown": "0.23.0",
|
|
84
85
|
"typescript": "7.0.2",
|
|
85
|
-
"vitest": "5.0.0"
|
|
86
|
-
"@agimon-ai/vibe-lint-plugin-doom-extension": "0.0.1-alpha.66"
|
|
86
|
+
"vitest": "5.0.0"
|
|
87
87
|
},
|
|
88
88
|
"peerDependencies": {
|
|
89
89
|
"@earendil-works/pi-coding-agent": "0.85.1"
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { DoomApiCaller } from '@agimon-ai/doompi-core/package-api';
|
|
2
|
+
import { sessionApiPath } from '@agimon-ai/doompi-core/web';
|
|
2
3
|
|
|
3
4
|
export const API_BASE_PATH = 'computer-use';
|
|
4
5
|
export const computerUseChannelType = 'computer_use_state';
|
|
@@ -11,7 +12,7 @@ export const COMPUTER_USE_DEFAULT_DURATION_MS = 300 * 1_000;
|
|
|
11
12
|
export const COMPUTER_USE_CONFIRMATION_WINDOW_MS = 2 * 60 * 1_000;
|
|
12
13
|
|
|
13
14
|
export function activationUrl(sessionId: string): string {
|
|
14
|
-
return
|
|
15
|
+
return `${sessionApiPath(sessionId)}/plugins/${API_BASE_PATH}/activate?${SESSION_QUERY_PARAM}=${encodeURIComponent(sessionId)}`;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export const COMPUTER_USE_ROUTES = {
|