@ai.weget.jp/bot 0.1.9 → 0.1.10

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.
@@ -0,0 +1 @@
1
+ import{createGmoCoinSkill as e}from"@ai.weget.jp/skill-gmo-coin";import{createGmoFxSkill as s}from"@ai.weget.jp/skill-gmo-fx";import{createSkillRegistry as t}from"./skills.js";const i=new Set(["active"]);export const createSkillHostState=({gmoFxConfigPath:o,gmoCoinConfigPath:a,log:n})=>{const r=t(),l=[s(o),e(a),{manifest:{name:"@ai.weget.jp/skill-browser",displayName:"Browser Skill",version:"0.1.0",description:"Browser automation skill scaffold",permissions:["browser:read"],tools:["browser.open_url","browser.extract_text","browser.screenshot"],hasUi:!0},createServices:()=>({})}];for(const e of l)r.register(e);let m=new Map;const p=e=>m.get(String(e||"").trim())||null,f=e=>{const s=p(e);return!s||Boolean(s.enabled)&&i.has(String(s.install_status||"").trim().toLowerCase())},g=()=>r.list().filter(e=>f(e.name)).map(e=>e.module.manifest);return{registry:r,getAvailableSkills:()=>r.list().map(e=>e.module.manifest),getActiveSkills:g,getManagedSkills:()=>r.list().map(e=>{const s=p(e.name);return{name:e.module.manifest.name,displayName:e.module.manifest.displayName,version:e.module.manifest.version,description:e.module.manifest.description,permissions:Array.isArray(e.module.manifest.permissions)?e.module.manifest.permissions:[],tools:Array.isArray(e.module.manifest.tools)?e.module.manifest.tools:[],hasUi:Boolean(e.module.manifest.hasUi),enabled:!s||f(e.name),installStatus:s?String(s.install_status||"active"):"builtin",configuredVersion:s?.version??null,configJson:s?.config_json&&"object"==typeof s.config_json?s.config_json:{}}}),getStateForSkill:p,isSkillEnabled:f,applySnapshots:e=>{m=new Map((Array.isArray(e)?e:[]).filter(e=>e&&"object"==typeof e&&String(e.package_name||"").trim()).map(e=>[String(e.package_name||"").trim(),e])),n?.("[skills] host state updated",{available:r.list().length,active:g().map(e=>e.name)})}}};
@@ -0,0 +1 @@
1
+ export const createSkillRegistry=()=>{const e=new Map,r=r=>e.get(String(r||"").trim())||null;return{register:r=>{const t=String(r?.manifest?.name||"").trim();if(!t)throw new Error("skill manifest name is required");return e.set(t,r),r},unregister:r=>{e.delete(String(r||"").trim())},list:()=>Array.from(e.entries()).map(([e,r])=>({name:e,module:r})),get:r,instantiate:(e,t)=>{const i=r(e);if(!i)throw new Error(`skill not found: ${e}`);return i.createServices(t)}}};export const loadSkillModule=async e=>{const r=await import(e),t=r.default||r.createSkill||r.skill;if(!t||"object"!=typeof t||!t.manifest||!t.createServices)throw new Error(`invalid skill module: ${e}`);return t};
@@ -1 +1 @@
1
- import t from"ws";export class BotWsClient{config;hooks;ws;heartbeatTimer;reconnectTimer;pingTimer;pongTimeoutTimer;awaitingPong;taskSeqMap;session;shouldReconnect;reconnectAttempts;maxReconnectAttempts;currentStatus;constructor(t,e={}){this.config=t,this.hooks=e,this.ws=null,this.heartbeatTimer=null,this.reconnectTimer=null,this.pingTimer=null,this.pongTimeoutTimer=null,this.awaitingPong=!1,this.taskSeqMap=new Map,this.session=null,this.shouldReconnect=!1,this.reconnectAttempts=0,this.maxReconnectAttempts=Number.isFinite(Number(this.config.reconnectMaxAttempts))?Math.max(0,Number(this.config.reconnectMaxAttempts)):10,this.currentStatus="disconnected"}botId(){return this.session?.botId||this.config.botId||""}nowIso(){return(new Date).toISOString()}log(t,e){this.hooks.onLog&&this.hooks.onLog(t,e)}setStatus(t){const e=String(t||"").trim().toLowerCase()||"disconnected";this.currentStatus!==e&&(this.currentStatus=e,this.hooks.onStatus&&this.hooks.onStatus(e))}getStatus(){return this.currentStatus}nextSeq(t){const e=(this.taskSeqMap.get(t)||0)+1;return this.taskSeqMap.set(t,e),e}buildWsUrl(){const t=new URL(this.config.wsUrl);return t.searchParams.set("bot_id",this.botId()),this.session?.userId&&t.searchParams.set("user_id",this.session.userId),this.session?.accessToken&&t.searchParams.set("token",this.session.accessToken),t.toString()}sendJson(e){this.ws&&this.ws.readyState===t.OPEN&&this.ws.send(JSON.stringify(e),t=>{if(t){this.log("[bot] ws send failed",{error:t.message});try{this.ws?.terminate()}catch{}}})}sanitizeLineReplyText(t){const e=String(t||"");return e?e.replace(/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/gi,"$1").replace(/https?:\/\/\S+/gi,"").replace(/\s{2,}/g," ").replace(/\n{3,}/g,"\n\n").trim():""}sendHello(){this.sendJson({action:"bot_hello",bot_id:this.botId(),user_id:this.session?.userId||void 0,version:this.config.version,capabilities:this.config.capabilities,ts:this.nowIso()})}sendHeartbeat(){this.sendJson({action:"task_event",bot_id:this.botId(),user_id:this.session?.userId||void 0,task_id:null,seq:0,event_type:"heartbeat",data_json:{bot_id:this.botId(),ts:this.nowIso()}})}sendTaskEvent(t,e,s){this.sendJson({action:"task_event",bot_id:this.botId(),user_id:this.session?.userId||void 0,task_id:t,seq:this.nextSeq(t),event_type:e,data_json:s})}sendBotState(t,e=""){const s=String(t||"").trim().toLowerCase();s&&this.sendTaskEvent("__bot_state__","bot_state",{state:s,reason:String(e||""),ts:this.nowIso()})}sendWsPing(){if(!this.ws||this.ws.readyState!==t.OPEN)return;if(this.awaitingPong){this.log("[bot] ws pong timeout, reconnecting");try{this.ws.terminate()}catch{}return}this.awaitingPong=!0,this.pongTimeoutTimer&&(clearTimeout(this.pongTimeoutTimer),this.pongTimeoutTimer=null);const e=1e3*Math.max(3,Number(this.config.wsPongTimeoutSec||12));this.pongTimeoutTimer=setTimeout(()=>{if(this.awaitingPong){this.log("[bot] ws pong timeout, reconnecting");try{this.ws?.terminate()}catch{}}},e);try{this.ws.ping()}catch(t){this.log("[bot] ws ping failed",{error:t instanceof Error?t.message:String(t)});try{this.ws.terminate()}catch{}}}async runTaskMock(t){const e=t.task_id;e?(this.sendTaskEvent(e,"task_received",{bot_id:this.botId(),received_at:this.nowIso()}),this.sendTaskEvent(e,"step_started",{step:"open_url",ts:this.nowIso()}),await new Promise(t=>setTimeout(t,600)),this.sendTaskEvent(e,"step_finished",{step:"open_url",ts:this.nowIso()}),this.sendTaskEvent(e,"ai_output",{summary:"mock result from electron bot",next_action:"wait_human_review",risk_flags:[]}),this.sendTaskEvent(e,"task_completed",{ts:this.nowIso()})):this.log("[bot] run_task missing task_id")}async onMessage(t){let e;try{const s=JSON.parse(t.toString("utf8"));if(!s||"object"!=typeof s)return void this.log("[bot] ignored non-object message");e=s}catch{return void this.log("[bot] ignored non-JSON message")}const s=e.type||e.action||"";if("hello_ack"!==s){if("run_task"===s){const t=e,s=String(t.task_id||"").trim();this.log("[bot] run_task received",{task_id:s});const i=Boolean(this.hooks.onRunTask);try{i?await(this.hooks.onRunTask?.(t)):await this.runTaskMock(t)}catch(t){!i&&s&&this.sendTaskEvent(s,"task_failed",{error:t instanceof Error?t.message:String(t),ts:this.nowIso()}),this.log("[bot] run_task failed",{task_id:s,error:t instanceof Error?t.message:String(t)})}return}if("line_message"===s){const t=e,s=`line-${e.line_event_id||Date.now()}`;if(this.log("[bot] line message received",{bot_id:e.bot_id||this.botId(),line_user_id:e.line_user_id||"",text:e.text||""}),this.sendTaskEvent(s,"line_message_received",{line_user_id:e.line_user_id||"",text:e.text||"",ts:this.nowIso()}),this.hooks.onLineMessage)try{const i=await this.hooks.onLineMessage(t,{taskId:s}),n=Array.isArray(i?.messages)?i?.messages.filter(t=>t&&"object"==typeof t):[],o=this.sanitizeLineReplyText(i?.answer||""),r=String(i?.imageUrl||"").trim(),a=String(i?.previewImageUrl||"").trim();(n.length>0||o||r)&&(this.sendJson({action:"line_reply",bot_id:this.botId(),user_id:this.session?.userId||void 0,line_user_id:e.line_user_id||"",line_reply_token:e.line_reply_token||"",text:n.length>0?void 0:o,image_url:r||void 0,preview_image_url:a||void 0,messages:n.length>0?n:void 0,ts:this.nowIso()}),this.sendTaskEvent(s,"line_message_ai_output",{line_user_id:e.line_user_id||"",answer:o,image_url:r||"",ts:this.nowIso()}))}catch(t){this.sendTaskEvent(s,"line_message_process_failed",{line_user_id:e.line_user_id||"",error:t instanceof Error?t.message:String(t),ts:this.nowIso()})}return}if("server_config_update"===s){const t=e;if(this.log("[bot] server_config_update received",{bot_id:e.bot_id||this.botId(),scopes:Array.isArray(e.scopes)?e.scopes:[]}),this.hooks.onServerConfigUpdate)try{await this.hooks.onServerConfigUpdate(t)}catch(t){this.log("[bot] onServerConfigUpdate failed",{error:t instanceof Error?t.message:String(t)})}return}if("workflow_execute"===s){const t=e;if(this.log("[bot] workflow_execute received",{bot_id:e.bot_id||this.botId(),workflow_id:e.workflow_id||"",trigger_source:e.trigger_source||""}),this.hooks.onWorkflowExecute)try{await this.hooks.onWorkflowExecute(t)}catch(t){this.log("[bot] onWorkflowExecute failed",{error:t instanceof Error?t.message:String(t)})}return}"ping"===s&&this.sendJson({type:"pong",ts:this.nowIso()})}else this.log("[bot] hello ack",e)}clearTimers(){this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null),this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.pingTimer&&(clearInterval(this.pingTimer),this.pingTimer=null),this.pongTimeoutTimer&&(clearTimeout(this.pongTimeoutTimer),this.pongTimeoutTimer=null),this.awaitingPong=!1}scheduleReconnect(){if(this.reconnectTimer||!this.session||!this.shouldReconnect)return;if(this.reconnectAttempts>=this.maxReconnectAttempts)return this.log("[bot] reconnect attempts exhausted",{attempts:this.reconnectAttempts,maxAttempts:this.maxReconnectAttempts}),void this.setStatus("reconnect_exhausted");const t=this.reconnectAttempts+1;this.log("[bot] scheduling reconnect",{attempt:t,maxAttempts:this.maxReconnectAttempts,delayMs:this.config.reconnectMs}),this.setStatus("reconnecting"),this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.reconnectAttempts=t,this.connect()},this.config.reconnectMs)}connect(){if(!this.session||!this.shouldReconnect)return;if(this.ws&&(this.ws.readyState===t.OPEN||this.ws.readyState===t.CONNECTING))return;const e=this.buildWsUrl();this.log("[bot] connecting",{target:e}),this.setStatus("connecting"),this.ws=new t(e),this.ws.on("open",()=>{this.log("[bot] connected"),this.reconnectAttempts=0,this.sendHello(),this.heartbeatTimer=setInterval(()=>this.sendHeartbeat(),1e3*this.config.heartbeatSec);const t=Math.max(5,Number(this.config.wsPingIntervalSec||25));this.pingTimer=setInterval(()=>this.sendWsPing(),1e3*t),this.awaitingPong=!1,this.setStatus("connected")}),this.ws.on("message",t=>{this.onMessage(t)}),this.ws.on("pong",()=>{this.awaitingPong=!1,this.pongTimeoutTimer&&(clearTimeout(this.pongTimeoutTimer),this.pongTimeoutTimer=null)}),this.ws.on("error",t=>{if(this.log("[bot] ws error",{message:t.message}),this.setStatus("error"),this.shouldReconnect)try{this.ws?.terminate()}catch{}}),this.ws.on("close",()=>{this.log("[bot] disconnected"),this.clearTimers(),this.ws=null,this.setStatus("disconnected"),this.scheduleReconnect()})}start(e){this.shouldReconnect=!0,this.reconnectAttempts=0,this.session=e,this.clearTimers(),this.ws&&this.ws.readyState===t.OPEN?this.ws.close():this.ws&&this.ws.readyState===t.CONNECTING||this.connect()}stop(){this.shouldReconnect=!1,this.reconnectAttempts=0,this.session=null,this.clearTimers(),this.ws?(this.ws.readyState!==t.OPEN&&this.ws.readyState!==t.CONNECTING||this.ws.close(),this.ws=null,this.setStatus("disconnected")):this.setStatus("disconnected")}}
1
+ import t from"ws";export class BotWsClient{config;hooks;ws;heartbeatTimer;reconnectTimer;pingTimer;pongTimeoutTimer;awaitingPong;taskSeqMap;session;shouldReconnect;reconnectAttempts;maxReconnectAttempts;currentStatus;constructor(t,s={}){this.config=t,this.hooks=s,this.ws=null,this.heartbeatTimer=null,this.reconnectTimer=null,this.pingTimer=null,this.pongTimeoutTimer=null,this.awaitingPong=!1,this.taskSeqMap=new Map,this.session=null,this.shouldReconnect=!1,this.reconnectAttempts=0,this.maxReconnectAttempts=Number.isFinite(Number(this.config.reconnectMaxAttempts))?Math.max(0,Number(this.config.reconnectMaxAttempts)):10,this.currentStatus="disconnected"}botId(){return this.session?.botId||this.config.botId||""}nowIso(){return(new Date).toISOString()}log(t,s){this.hooks.onLog&&this.hooks.onLog(t,s)}setStatus(t){const s=String(t||"").trim().toLowerCase()||"disconnected";this.currentStatus!==s&&(this.currentStatus=s,this.hooks.onStatus&&this.hooks.onStatus(s))}getStatus(){return this.currentStatus}nextSeq(t){const s=(this.taskSeqMap.get(t)||0)+1;return this.taskSeqMap.set(t,s),s}buildWsUrl(){const t=new URL(this.config.wsUrl);return t.searchParams.set("bot_id",this.botId()),this.session?.userId&&t.searchParams.set("user_id",this.session.userId),this.session?.accessToken&&t.searchParams.set("token",this.session.accessToken),t.toString()}sendJson(s){this.ws&&this.ws.readyState===t.OPEN&&this.ws.send(JSON.stringify(s),t=>{if(t){this.log("[bot] ws send failed",{error:t.message});try{this.ws?.terminate()}catch{}}})}sanitizeLineReplyText(t){const s=String(t||"");return s?s.replace(/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/gi,"$1").replace(/https?:\/\/\S+/gi,"").replace(/\s{2,}/g," ").replace(/\n{3,}/g,"\n\n").trim():""}sendHello(){this.sendJson({action:"bot_hello",bot_id:this.botId(),user_id:this.session?.userId||void 0,version:this.config.version,capabilities:this.config.capabilities,ts:this.nowIso()})}sendHeartbeat(){this.sendJson({action:"task_event",bot_id:this.botId(),user_id:this.session?.userId||void 0,task_id:null,seq:0,event_type:"heartbeat",data_json:{bot_id:this.botId(),ts:this.nowIso()}})}sendTaskEvent(t,s,e){this.sendJson({action:"task_event",bot_id:this.botId(),user_id:this.session?.userId||void 0,task_id:t,seq:this.nextSeq(t),event_type:s,data_json:e})}sendBotState(t,s=""){const e=String(t||"").trim().toLowerCase();e&&this.sendTaskEvent("__bot_state__","bot_state",{state:e,reason:String(s||""),ts:this.nowIso()})}sendWsPing(){if(!this.ws||this.ws.readyState!==t.OPEN)return;if(this.awaitingPong){this.log("[bot] ws pong timeout, reconnecting");try{this.ws.terminate()}catch{}return}this.awaitingPong=!0,this.pongTimeoutTimer&&(clearTimeout(this.pongTimeoutTimer),this.pongTimeoutTimer=null);const s=1e3*Math.max(3,Number(this.config.wsPongTimeoutSec||12));this.pongTimeoutTimer=setTimeout(()=>{if(this.awaitingPong){this.log("[bot] ws pong timeout, reconnecting");try{this.ws?.terminate()}catch{}}},s);try{this.ws.ping()}catch(t){this.log("[bot] ws ping failed",{error:t instanceof Error?t.message:String(t)});try{this.ws.terminate()}catch{}}}async runTaskMock(t){const s=t.task_id;s?(this.sendTaskEvent(s,"task_received",{bot_id:this.botId(),received_at:this.nowIso()}),this.sendTaskEvent(s,"step_started",{step:"open_url",ts:this.nowIso()}),await new Promise(t=>setTimeout(t,600)),this.sendTaskEvent(s,"step_finished",{step:"open_url",ts:this.nowIso()}),this.sendTaskEvent(s,"ai_output",{summary:"mock result from electron bot",next_action:"wait_human_review",risk_flags:[]}),this.sendTaskEvent(s,"task_completed",{ts:this.nowIso()})):this.log("[bot] run_task missing task_id")}async onMessage(t){let s;try{const e=JSON.parse(t.toString("utf8"));if(!e||"object"!=typeof e)return void this.log("[bot] ignored non-object message");s=e}catch{return void this.log("[bot] ignored non-JSON message")}const e=s.type||s.action||"";if("hello_ack"!==e){if("run_task"===e){const t=s,e=String(t.task_id||"").trim();this.log("[bot] run_task received",{task_id:e});const i=Boolean(this.hooks.onRunTask);try{i?await(this.hooks.onRunTask?.(t)):await this.runTaskMock(t)}catch(t){!i&&e&&this.sendTaskEvent(e,"task_failed",{error:t instanceof Error?t.message:String(t),ts:this.nowIso()}),this.log("[bot] run_task failed",{task_id:e,error:t instanceof Error?t.message:String(t)})}return}if("task_control"===e){const t=s,e=String(t.task_id||"").trim(),i=String(t.action||"").trim();if(this.log("[bot] task_control received",{task_id:e,action:i}),this.hooks.onTaskControl)try{await this.hooks.onTaskControl(t)}catch(t){this.log("[bot] onTaskControl failed",{task_id:e,action:i,error:t instanceof Error?t.message:String(t)})}else e&&"cancel_task"===i&&this.sendTaskEvent(e,"task_cancelled",{reason:"server_cancelled",ts:this.nowIso()});return}if("line_message"===e){const t=s,e=`line-${s.line_event_id||Date.now()}`;if(this.log("[bot] line message received",{bot_id:s.bot_id||this.botId(),line_user_id:s.line_user_id||"",text:s.text||""}),this.sendTaskEvent(e,"line_message_received",{line_user_id:s.line_user_id||"",text:s.text||"",ts:this.nowIso()}),this.hooks.onLineMessage)try{const i=await this.hooks.onLineMessage(t,{taskId:e}),n=Array.isArray(i?.messages)?i?.messages.filter(t=>t&&"object"==typeof t):[],o=this.sanitizeLineReplyText(i?.answer||""),r=String(i?.imageUrl||"").trim(),a=String(i?.previewImageUrl||"").trim();(n.length>0||o||r)&&(this.sendJson({action:"line_reply",bot_id:this.botId(),user_id:this.session?.userId||void 0,line_user_id:s.line_user_id||"",line_reply_token:s.line_reply_token||"",text:n.length>0?void 0:o,image_url:r||void 0,preview_image_url:a||void 0,messages:n.length>0?n:void 0,ts:this.nowIso()}),this.sendTaskEvent(e,"line_message_ai_output",{line_user_id:s.line_user_id||"",answer:o,image_url:r||"",ts:this.nowIso()}))}catch(t){this.sendTaskEvent(e,"line_message_process_failed",{line_user_id:s.line_user_id||"",error:t instanceof Error?t.message:String(t),ts:this.nowIso()})}return}if("server_config_update"===e){const t=s;if(this.log("[bot] server_config_update received",{bot_id:s.bot_id||this.botId(),scopes:Array.isArray(s.scopes)?s.scopes:[]}),this.hooks.onServerConfigUpdate)try{await this.hooks.onServerConfigUpdate(t)}catch(t){this.log("[bot] onServerConfigUpdate failed",{error:t instanceof Error?t.message:String(t)})}return}"ping"===e&&this.sendJson({type:"pong",ts:this.nowIso()})}else this.log("[bot] hello ack",s)}clearTimers(){this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null),this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.pingTimer&&(clearInterval(this.pingTimer),this.pingTimer=null),this.pongTimeoutTimer&&(clearTimeout(this.pongTimeoutTimer),this.pongTimeoutTimer=null),this.awaitingPong=!1}scheduleReconnect(){if(this.reconnectTimer||!this.session||!this.shouldReconnect)return;if(this.reconnectAttempts>=this.maxReconnectAttempts)return this.log("[bot] reconnect attempts exhausted",{attempts:this.reconnectAttempts,maxAttempts:this.maxReconnectAttempts}),void this.setStatus("reconnect_exhausted");const t=this.reconnectAttempts+1;this.log("[bot] scheduling reconnect",{attempt:t,maxAttempts:this.maxReconnectAttempts,delayMs:this.config.reconnectMs}),this.setStatus("reconnecting"),this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.reconnectAttempts=t,this.connect()},this.config.reconnectMs)}connect(){if(!this.session||!this.shouldReconnect)return;if(this.ws&&(this.ws.readyState===t.OPEN||this.ws.readyState===t.CONNECTING))return;const s=this.buildWsUrl();this.log("[bot] connecting",{target:s}),this.setStatus("connecting"),this.ws=new t(s),this.ws.on("open",()=>{this.log("[bot] connected"),this.reconnectAttempts=0,this.sendHello(),this.heartbeatTimer=setInterval(()=>this.sendHeartbeat(),1e3*this.config.heartbeatSec);const t=Math.max(5,Number(this.config.wsPingIntervalSec||25));this.pingTimer=setInterval(()=>this.sendWsPing(),1e3*t),this.awaitingPong=!1,this.setStatus("connected")}),this.ws.on("message",t=>{this.onMessage(t)}),this.ws.on("pong",()=>{this.awaitingPong=!1,this.pongTimeoutTimer&&(clearTimeout(this.pongTimeoutTimer),this.pongTimeoutTimer=null)}),this.ws.on("error",t=>{if(this.log("[bot] ws error",{message:t.message}),this.setStatus("error"),this.shouldReconnect)try{this.ws?.terminate()}catch{}}),this.ws.on("close",()=>{this.log("[bot] disconnected"),this.clearTimers(),this.ws=null,this.setStatus("disconnected"),this.scheduleReconnect()})}start(s){this.shouldReconnect=!0,this.reconnectAttempts=0,this.session=s,this.clearTimers(),this.ws&&this.ws.readyState===t.OPEN?this.ws.close():this.ws&&this.ws.readyState===t.CONNECTING||this.connect()}stop(){this.shouldReconnect=!1,this.reconnectAttempts=0,this.session=null,this.clearTimers(),this.ws?(this.ws.readyState!==t.OPEN&&this.ws.readyState!==t.CONNECTING||this.ws.close(),this.ws=null,this.setStatus("disconnected")):this.setStatus("disconnected")}}
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@ai.weget.jp/bot",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "private": false,
5
5
  "type": "module",
6
- "description": "Weget bot client for ai.weget.jp",
6
+ "description": "WeGet bot host for Codex-centered skill runtime",
7
7
  "author": "weget.jp",
8
8
  "bin": {
9
9
  "weget-bot": "bin/weget-bot.cjs"
@@ -18,16 +18,6 @@
18
18
  "package.json",
19
19
  "README.md"
20
20
  ],
21
- "info": {
22
- "api_gateway_template": "../infra/cloudformation/template.yaml",
23
- "websocket_routes": [
24
- "$connect",
25
- "$disconnect",
26
- "bot_hello",
27
- "task_event",
28
- "line_reply"
29
- ]
30
- },
31
21
  "main": "dist/src/main.js",
32
22
  "scripts": {
33
23
  "start": "npm run build:ts && electron dist/src/main.js",
@@ -38,8 +28,11 @@
38
28
  "build": "npm run build:ts"
39
29
  },
40
30
  "dependencies": {
31
+ "@ai.weget.jp/skill-gmo-coin": "^0.1.0",
32
+ "@ai.weget.jp/skill-gmo-core": "^0.1.0",
33
+ "@ai.weget.jp/skill-gmo-fx": "^0.1.0",
34
+ "@ai.weget.jp/skill-sdk": "^0.1.0",
41
35
  "electron": "^31.7.7",
42
- "openai": "^6.22.0",
43
36
  "ws": "^8.18.3"
44
37
  },
45
38
  "devDependencies": {
@@ -1,7 +1,13 @@
1
1
  const fs = require('node:fs/promises');
2
2
  const path = require('node:path');
3
3
  const { spawnSync } = require('node:child_process');
4
- const { minify } = require('terser');
4
+
5
+ let minify;
6
+ try {
7
+ ({ minify } = require('terser'));
8
+ } catch {
9
+ minify = null;
10
+ }
5
11
 
6
12
  const root = process.cwd();
7
13
  const srcDir = path.join(root, 'src');
@@ -59,7 +65,7 @@ const listFilesRecursive = async (dir) => {
59
65
 
60
66
  const minifyBuildJs = async () => {
61
67
  const minifyEnabled = String(process.env.BOT_MINIFY || 'true').toLowerCase() !== 'false';
62
- if (!minifyEnabled) return;
68
+ if (!minifyEnabled || !minify) return;
63
69
 
64
70
  const files = await listFilesRecursive(buildSrcDir);
65
71
  const jsFiles = files.filter((file) => path.extname(file).toLowerCase() === '.js');
@@ -1 +0,0 @@
1
- import e from"openai";export const createAiChatService=({getApiKey:t,getModel:i})=>({sendChat:async n=>{const r=String(t()||"").trim();if(!r)throw new Error("OpenAI API Key is not configured");const o=String(i()||"gpt-4.1-mini").trim()||"gpt-4.1-mini",c=new e({apiKey:r}),s=await c.chat.completions.create({model:o,messages:[{role:"user",content:String(n||"")}]});return String(s.choices?.[0]?.message?.content||"").trim()}});
@@ -1 +0,0 @@
1
- import t from"node:crypto";export class GmoGatewayError extends Error{code;status;constructor(t,e,r=0){super(e),this.name="GmoGatewayError",this.code=t,this.status=r}}const e=t=>t&&"object"==typeof t?t:{},r=(t,e=0)=>{const r="string"==typeof t?t.trim().replace(/,/g,"").replace(/[^\d.+-]/g,""):t,a=Number(r);return Number.isFinite(a)?a:e};export const createGmoCoinGateway=a=>{let o=()=>a,s=()=>({apiKey:"",apiSecret:""}),i=()=>{};const n=async({method:r,path:a,body:n,query:d,market:c="crypto"})=>{const p=s(c);if(!p.apiKey||!p.apiSecret)throw i("[trade] private api skipped: credentials missing",{market:c,method:r,path:a}),new GmoGatewayError("API_KEY_MISSING","GMO API key/secret is missing");const m=o(),l=(t=>{const e=String(t||"").trim();return e?e.startsWith("/")?e:`/${e}`:"/"})(a),u="fx"===c?m.fxApiBaseUrl:m.cryptoApiBaseUrl,y=String(u||"").trim().replace(/\/$/,""),h=d?Object.entries(d).filter(([,t])=>null!=t&&""!==String(t)).map(([t,e])=>`${encodeURIComponent(t)}=${encodeURIComponent(String(e))}`).join("&"):"",g=h?`${y}${l}?${h}`:`${y}${l}`,b=n?JSON.stringify(n):"",A=Date.now().toString(),w=t.createHmac("sha256",p.apiSecret).update(`${A}${r}${l}${b}`).digest("hex");let f;i("[trade] private api request",{market:c,method:r,path:l,query:d||null});try{f=await fetch(g,{method:r,headers:{"Content-Type":"application/json","API-KEY":p.apiKey,"API-TIMESTAMP":A,"API-SIGN":w},body:"POST"===r?b:void 0})}catch(t){throw i("[trade] private api network error",{market:c,method:r,path:l,error:t instanceof Error?t.message:String(t)}),new GmoGatewayError("NETWORK_ERROR",t instanceof Error?t.message:String(t))}const T=await(async t=>{const r=await t.text();if(!r)return{};try{return e(JSON.parse(r))}catch{return{raw:r}}})(f),I=(t=>{const r=t.messages;if(Array.isArray(r)){const t=r.map(t=>e(t)).map(t=>{const e=String(t.message_code||"").trim(),r=String(t.message_string||"").trim();return e&&r?`${e}: ${r}`:e||r}).filter(Boolean);if(t.length)return t.join(" | ")}return String(t.message||t.messages||"gmo error")})(T);if(i("[trade] private api response",{market:c,method:r,path:l,query:d||null,status:f.status,ok:f.ok,gmoStatus:T.status??null,messages:I}),!(t=>{const e=t.status;return null==e||""===e||"0"===String(e)})(T))throw i("[trade] private api business error",{market:c,method:r,path:l,query:d||null,gmoStatus:T.status??null,messages:I}),new GmoGatewayError("HTTP_ERROR",String(I||"gmo business error"),f.status||200);if(401===f.status||403===f.status)throw i("[trade] private api auth error",{market:c,method:r,path:l,query:d||null,status:f.status,message:I}),new GmoGatewayError("AUTH_ERROR",I,f.status);if(!f.ok)throw i("[trade] private api http error",{market:c,method:r,path:l,query:d||null,status:f.status,message:I}),new GmoGatewayError("HTTP_ERROR",String(I||`http error: ${f.status}`),f.status);return T},d=async()=>{const t=o(),e=await n({method:"GET",path:t.assetsPath,market:"crypto"});return Array.isArray(e.data)?e.data:[]};return{getCredentialsState:(t="crypto")=>{const e=s(t);return e.apiKey&&e.apiSecret?"ready":"api_key_missing"},setRuntimeConfigProvider:t=>{o=t},setCredentialsProvider:t=>{s=t},setLogEmitter:t=>{i=t},getAssets:d,getOpenPositions:async({market:t="crypto",symbol:r,page:a,prevId:s,count:d}={})=>{const c=o(),p="fx"===t?{symbol:r,prevId:s,count:d}:{symbol:r,page:a,count:d},m=await n({method:"GET",path:c.positionsPath,market:t,query:p}),l=e(m.data),u=Array.isArray(m.data)?m.data:Array.isArray(l.list)?l.list:[];return i("[trade] open positions parsed",{market:t,symbol:r||null,page:a??null,prevId:s??null,count:d??null,rows:u.length}),u},testConnection:async()=>({ok:!0,assetRows:(await d()).length}),getBuyingPower:async t=>{const a=o();if("fx"===t){const o=a.fxAssetsPath||a.assetsPath||"/v1/account/assets",s=await n({method:"GET",path:o,market:"fx"}),d=(t=>{const a=e(t.data);if(Array.isArray(t.data)){const a=e(t.data[0]);return r(a.availableAmount||0)}return r(a.availableAmount||0)})(s);return i("[trade] buying power parsed",{market:t,path:o,availableJpy:d,dataType:Array.isArray(s.data)?"array":typeof s.data,dataKeys:Object.keys(e(s.data||{}))}),d}const s=a.coinMarginPath||"/v1/account/margin",d=await n({method:"GET",path:s,market:"crypto"}),c=(t=>{const a=e(t.data),o=e(t);return r(a.availableAmount||o.availableAmount||0)})(d);return i("[trade] buying power parsed",{market:t,path:s,availableJpy:c,dataKeys:Object.keys(e(d.data||{}))}),c},getAccountMetrics:async t=>{const a=o();if("fx"===t){const o=a.fxAssetsPath||a.assetsPath||"/v1/account/assets",s=await n({method:"GET",path:o,market:"fx"}),d=e(s.data),c=Array.isArray(s.data)?e(s.data[0]):d,p=r(c.availableAmount||0),m=r(c.margin||0),l=r(c.marginRatio||0),u=r(c.positionLossGain||0)+r(c.totalSwap||0);return i("[trade] account metrics parsed",{market:t,availableAmount:p,margin:m,marginRatio:l,pnlWithSwap:u}),{market:t,availableAmount:p,margin:m,marginRatio:l,pnlWithSwap:u}}const s=a.coinMarginPath||"/v1/account/margin",d=await n({method:"GET",path:s,market:"crypto"}),c=e(d.data),p=r(c.availableAmount||0),m=r(c.margin||0),l=r(c.marginRatio||0),u=r(c.profitLoss||0);return i("[trade] account metrics parsed",{market:t,availableAmount:p,margin:m,marginRatio:l,pnlWithSwap:u}),{market:t,availableAmount:p,margin:m,marginRatio:l,pnlWithSwap:u}},getPositionSummary:async({market:t,symbol:r})=>{const a="fx"===t?"fx":"crypto",s=o().positionSummaryPath||"/v1/positionSummary",d=await n({method:"GET",path:s,market:a,query:{symbol:r||void 0}}),c=e(d.data),p=Array.isArray(c.list)?c.list:Array.isArray(d.data)?d.data:[];return i("[trade] position summary parsed",{market:t,symbol:r||null,rows:p.length}),p.map(t=>e(t))},placeFxOrder:async t=>{const r=o().fxOrderPath||"/v1/order",a={symbol:t.symbol,side:t.side,size:t.size,executionType:t.executionType||"MARKET"};t.clientOrderId&&(a.clientOrderId=t.clientOrderId),t.limitPrice&&(a.limitPrice=t.limitPrice),t.stopPrice&&(a.stopPrice=t.stopPrice),t.lowerBound&&(a.lowerBound=t.lowerBound),t.upperBound&&(a.upperBound=t.upperBound);const s=await n({method:"POST",path:r,market:"fx",body:a}),d=Array.isArray(s.data)?s.data:[],c=e(d[0]);return i("[trade] fx order placed",{symbol:t.symbol,side:t.side,size:t.size,executionType:t.executionType||"MARKET",orderId:c.orderId??null,rootOrderId:c.rootOrderId??null,status:c.status??null}),c},placeCoinOrder:async t=>{const e={symbol:t.symbol,side:t.side,executionType:t.executionType||"MARKET",size:t.size};t.timeInForce&&(e.timeInForce=t.timeInForce),t.price&&(e.price=t.price),t.losscutPrice&&(e.losscutPrice=t.losscutPrice),"boolean"==typeof t.cancelBefore&&(e.cancelBefore=t.cancelBefore);const r=o().fxOrderPath||"/v1/order",a=await n({method:"POST",path:r,market:"crypto",body:e}),s=String(a.data??"").trim();return i("[trade] coin order placed",{symbol:t.symbol,side:t.side,size:t.size,executionType:t.executionType||"MARKET",orderId:s||null}),{orderId:s,rawData:a.data??null}},placeFxCloseOrder:async t=>{const r=o().fxCloseOrderPath||"/v1/closeOrder",a={symbol:t.symbol,side:t.side,executionType:t.executionType||"MARKET",settlePosition:[{positionId:t.positionId,size:t.size}]},s=await n({method:"POST",path:r,market:"fx",body:a}),d=Array.isArray(s.data)?s.data:[],c=e(d[0]);return i("[trade] fx close order placed",{symbol:t.symbol,side:t.side,positionId:t.positionId,size:t.size,executionType:t.executionType||"MARKET",orderId:c.orderId??null,rootOrderId:c.rootOrderId??null,status:c.status??null}),c},placeCoinCloseOrder:async t=>{const e=o().coinCloseOrderPath||"/v1/closeOrder",r={symbol:t.symbol,side:t.side,executionType:t.executionType||"MARKET",settlePosition:[{positionId:t.positionId,size:t.size}]};t.timeInForce&&(r.timeInForce=t.timeInForce),t.price&&(r.price=t.price),"boolean"==typeof t.cancelBefore&&(r.cancelBefore=t.cancelBefore);const a=await n({method:"POST",path:e,market:"crypto",body:r}),s=String(a.data??"").trim();return i("[trade] coin close order placed",{symbol:t.symbol,side:t.side,positionId:t.positionId,size:t.size,executionType:t.executionType||"MARKET",orderId:s||null}),{orderId:s,rawData:a.data??null}}}};
@@ -1 +0,0 @@
1
- const t=(t,e=0)=>{const r=Number(t);return Number.isFinite(r)?r:e},e=t=>{const e=Number(t);return Number.isFinite(e)?e:null};export const createGmoKlineService=({getCoinPublicBaseUrl:r,getFxPublicBaseUrl:a})=>{const n=t=>("fx"===t?a():r()).replace(/\/$/,"");return{fetchQuotes:async({market:t="coin",symbols:r})=>{const a=`${n(t)}/v1/ticker`,i=await fetch(a,{method:"GET"}),s=await i.json().catch(()=>({}));if(!i.ok)throw new Error(String(s.message||s.messages||`ticker http error: ${i.status}`));const o=Array.isArray(s.data)?s.data:[],m=new Set((r||[]).map(t=>String(t||"").trim().toUpperCase()).filter(Boolean));return{market:t,quotes:o.map(t=>{const r=t&&"object"==typeof t?t:{},a=String(r.symbol||"").trim().toUpperCase();if(!a)return null;const n=e(r.bid),i=e(r.ask),s=e(r.last),o=e(r.open);return{symbol:a,bid:n,ask:i,spread:null!==i&&null!==n?i-n:null,changePct:null!==s&&null!==o&&0!==o?(s-o)/o*100:null}}).filter(t=>Boolean(t)).filter(t=>0===m.size||m.has(t.symbol))}},fetchKline:async({symbol:e,interval:r,market:a="coin"})=>{const i=String(e||"BTC_JPY").trim().toUpperCase(),s=(t=>{const e=String(t||"1m").trim().toLowerCase();return"1m"===e?"1min":"5m"===e?"5min":"10m"===e?"10min":"15m"===e?"15min":"30m"===e?"30min":"1h"===e?"1hour":"4h"===e?"4hour":"8h"===e?"8hour":"12h"===e?"12hour":"1d"===e?"1day":e})(r),o=n(a),m=(()=>{const t=new Date;return`${t.getUTCFullYear()}${String(t.getUTCMonth()+1).padStart(2,"0")}${String(t.getUTCDate()).padStart(2,"0")}`})(),l=new URLSearchParams({symbol:i,interval:s,date:m});"fx"===a&&l.set("priceType","ASK");const c=`${o}/v1/klines?${l.toString()}`,u=await fetch(c,{method:"GET"}),h=await u.json().catch(()=>({}));if(!u.ok)throw new Error(String(h.message||h.messages||`kline http error: ${u.status}`));return{symbol:i,interval:s,market:a,candles:(Array.isArray(h.data)?h.data:[]).map(e=>{const r=e&&"object"==typeof e?e:{};return{time:t(r.openTime||r.timestamp||0),open:t(r.open),high:t(r.high),low:t(r.low),close:t(r.close),volume:t(r.volume)}}).filter(t=>t.time>0&&t.high>=t.low).slice(-120)}},fetchSymbolRules:async({market:t="coin"})=>{const e=`${n(t)}/v1/symbols`,r=await fetch(e,{method:"GET"}),a=await r.json().catch(()=>({}));if(!r.ok)throw new Error(String(a.message||a.messages||`symbols http error: ${r.status}`));return{market:t,rules:(Array.isArray(a.data)?a.data:[]).map(t=>t&&"object"==typeof t?t:{}).map(t=>({symbol:String(t.symbol||"").trim().toUpperCase(),minOrderSize:String(t.minOrderSize||""),maxOrderSize:String(t.maxOrderSize||""),sizeStep:String(t.sizeStep||""),tickSize:String(t.tickSize||"")})).filter(t=>t.symbol)}}}};
@@ -1 +0,0 @@
1
- const e=e=>String(e||"BTC_JPY").trim().toUpperCase(),n=e=>String(e||"1m").trim().toLowerCase();export const createGmoWindowService=({BrowserWindow:o,preloadPath:t,getMainWindow:r,emitLog:i,getKlineBaseUrl:l})=>{let s=null;return{ensureKlineWindow:async({symbol:a,interval:d})=>{if(s&&!s.isDestroyed())return s.focus(),{url:s.webContents.getURL(),reused:!0};const c=((o,t)=>{const r=e(o),i=n(t),s=String(l()||"").trim().replace(/\/$/,"");return s?`${s}/${encodeURIComponent(r)}?interval=${encodeURIComponent(i)}`:`https://coin.z.com/jp/corp/chart/${encodeURIComponent(r)}?interval=${encodeURIComponent(i)}`})(a,d),u=r()||void 0;return s=new o({width:1300,height:900,autoHideMenuBar:!0,parent:u,modal:!1,webPreferences:{preload:t,contextIsolation:!0,nodeIntegration:!1}}),s.on("closed",()=>{s=null}),await s.loadURL(c),i("[trade] gmo kline window opened",{symbol:e(a),interval:n(d),url:c}),{url:c,reused:!1}},closeAllTradeWindows:()=>{s&&!s.isDestroyed()&&s.close(),s=null}}};
@@ -1 +0,0 @@
1
- import i from"node:fs/promises";export const defaultTradeApiConfig=()=>({errorLogDir:"",riskDailyLossLimitJpy:5e4,cryptoApiKey:"",cryptoApiSecret:"",fxApiKey:"",fxApiSecret:"",aiModel:"gpt-4.1-mini",openAiApiKey:""});const e=i=>{const e=(i=>i&&"object"==typeof i?i:{})(i),r={errorLogDir:"",riskDailyLossLimitJpy:5e4,cryptoApiKey:"",cryptoApiSecret:"",fxApiKey:"",fxApiSecret:"",aiModel:"gpt-4.1-mini",openAiApiKey:""},t=Number(e.riskDailyLossLimitJpy);return{errorLogDir:String(e.errorLogDir||"").trim(),riskDailyLossLimitJpy:Number.isFinite(t)&&t>0?t:r.riskDailyLossLimitJpy,cryptoApiKey:String(e.cryptoApiKey||"").trim(),cryptoApiSecret:String(e.cryptoApiSecret||"").trim(),fxApiKey:String(e.fxApiKey||"").trim(),fxApiSecret:String(e.fxApiSecret||"").trim(),aiModel:String(e.aiModel||"gpt-4.1-mini").trim()||"gpt-4.1-mini",openAiApiKey:String(e.openAiApiKey||"").trim()}};export const createTradeConfigService=({configPath:r,emitLog:t})=>{let p={errorLogDir:"",riskDailyLossLimitJpy:5e4,cryptoApiKey:"",cryptoApiSecret:"",fxApiKey:"",fxApiSecret:"",aiModel:"gpt-4.1-mini",openAiApiKey:""};return{load:async()=>{try{const t=await i.readFile(r,"utf8");return p=e(JSON.parse(t)),p}catch(i){return"ENOENT"!==(i?.code||"")&&t("[trade-config] load failed",{error:i instanceof Error?i.message:String(i)}),p={errorLogDir:"",riskDailyLossLimitJpy:5e4,cryptoApiKey:"",cryptoApiSecret:"",fxApiKey:"",fxApiSecret:"",aiModel:"gpt-4.1-mini",openAiApiKey:""},p}},save:async o=>{const y=e(o);return p=y,await i.writeFile(r,JSON.stringify({...y,updatedAt:(new Date).toISOString()},null,2),"utf8"),t("[trade-config] saved",{hasCryptoKey:Boolean(y.cryptoApiKey),hasCryptoSecret:Boolean(y.cryptoApiSecret),hasFxKey:Boolean(y.fxApiKey),hasFxSecret:Boolean(y.fxApiSecret)}),p},get:()=>p,getCredentials:i=>"fx"===i?{apiKey:p.fxApiKey,apiSecret:p.fxApiSecret}:{apiKey:p.cryptoApiKey,apiSecret:p.cryptoApiSecret}}};
@@ -1 +0,0 @@
1
- import{GmoGatewayError as e}from"./gmoCoinGateway.js";const r=e=>e&&"object"==typeof e?e:{},i=(e,r=0)=>{const i=Number(e);return Number.isFinite(i)?i:r},t=e=>Math.round(100*e)/100,a=({marginRatio:e,drawdownPct:r})=>e>0&&e<120?"high":e>0&&e<200?"medium":r>=15?"high":r>=8?"medium":"low";export const createTradeStatusService=({gateway:s,env:o,emitLog:n})=>{let p="unknown";const l=r=>r instanceof e?"API_KEY_MISSING"===r.code?"api_key_missing":"AUTH_ERROR"===r.code?"auth_error":"HTTP_ERROR"===r.code?"http_error":"network_error":"network_error";return{buildSnapshot:async()=>{const e=Number(o.getRiskDailyLossLimitJpy()||0);if("api_key_missing"===s.getCredentialsState())return p="api_key_missing",{broker:"gmocoin",as_of:(new Date).toISOString(),asset:{equity_jpy:0,available_jpy:0,margin_ratio:0},positions:{count:0,gross_exposure_jpy:0,items:[]},risk:{risk_level:"low",daily_pnl_jpy:0,max_daily_loss_limit_jpy:t(e),drawdown_pct:0,alerts:["GMO API key/secret is missing"]},api_state:p};try{const[o,n]=await Promise.all([s.getAssets(),s.getOpenPositions()]),l=Array.isArray(o)?o:[],_=Array.isArray(n)?n:[],y=_.map(e=>{const t=r(e),a=String(t.side||t.positionSide||"BUY").toUpperCase();return{symbol:String(t.symbol||t.symbolName||t.currencyPair||""),side:"SELL"===a?"SELL":"BUY",size:i(t.size||t.positionSize||t.orderSize),entryPrice:i(t.price||t.entryPrice),markPrice:i(t.currentPrice||t.markPrice||t.price),unrealizedPnlJpy:i(t.lossGain||t.unrealizedProfitLoss||0)}}),c=l.map(r).find(e=>"JPY"===String(e.symbol||e.currency||"").toUpperCase()),m=i(c?.amount||c?.equity||0),d=i(c?.available||c?.availableAmount||m),u=i(c?.marginRatio||0),g=t(y.reduce((e,r)=>e+i(r.unrealizedPnlJpy),0)),w=0,S=t(y.reduce((e,r)=>e+Math.abs(i(r.markPrice)*i(r.size)),0)),k=[];return u>0&&u<120&&k.push("Margin ratio below warning threshold"),g<0&&Math.abs(g)>=e&&k.push("Daily loss limit exceeded"),p="ready",{broker:"gmocoin",as_of:(new Date).toISOString(),asset:{equity_jpy:t(m),available_jpy:t(d),margin_ratio:t(u)},positions:{count:y.length,gross_exposure_jpy:S,items:y},risk:{risk_level:a({marginRatio:u,drawdownPct:w}),daily_pnl_jpy:g,max_daily_loss_limit_jpy:t(e),drawdown_pct:t(w),alerts:k},api_state:p}}catch(r){p=l(r);const i=r instanceof Error?r.message:String(r);return n("[trade] gmo snapshot failed",{apiState:p,error:i}),{broker:"gmocoin",as_of:(new Date).toISOString(),asset:{equity_jpy:0,available_jpy:0,margin_ratio:0},positions:{count:0,gross_exposure_jpy:0,items:[]},risk:{risk_level:"medium",daily_pnl_jpy:0,max_daily_loss_limit_jpy:t(e),drawdown_pct:0,alerts:[i]},api_state:p}}},testApi:async()=>{try{const e=await s.testConnection();return p="ready",{ok:e.ok,apiState:p,assetRows:e.assetRows}}catch(e){return p=l(e),{ok:!1,apiState:p,error:e instanceof Error?e.message:String(e)}}},getApiState:()=>p}};
@@ -1 +0,0 @@
1
- export{};