@messenger-agent/messenger-agent 0.35.0 → 0.36.0-alpha.1

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/app.js CHANGED
@@ -1 +1 @@
1
- import{Hono as r}from"hono";import{agentAuthMiddleware as s}from"@messenger-agent/shared/agent-auth";import{createAgentFileRoutes as a}from"@messenger-agent/shared/agent-files";import{infoRequestLogger as p}from"@messenger-agent/shared/http-logger";import{createAgentWorkspaceRoutes as m}from"@messenger-agent/shared/workspace-routes";import{appConfig as n}from"./config.js";function u(t=n){const e=new r;return e.use(p(["/health"])),e.get("/health",o=>o.json({status:"ok",role:"messenger-agent"})),e.use("/*",s(t.authTokens)),e.route("/v1/files",a()),e.route("/v1/workspaces",m({config:t,textMaxBytes:t.textMaxBytes})),e}var c=u();export{u as createApp,c as default};
1
+ import{Hono as r}from"hono";import{agentAuthMiddleware as s}from"@messenger-agent/shared/agent-auth";import{createAgentFileRoutes as a}from"@messenger-agent/shared/agent-files";import{infoRequestLogger as p}from"@messenger-agent/shared/http-logger";import{createAgentWorkspaceRoutes as m}from"@messenger-agent/shared/workspace-routes";import{appConfig as u}from"./config.js";function i(t=u){const e=new r;return e.use(p(["/health"])),e.get("/health",o=>o.json({status:"ok",role:"messenger-agent"})),e.use("/*",s(t.authTokens)),e.route("/v1/files",a()),e.route("/v1/workspaces",m({config:t,textMaxBytes:t.textMaxBytes,uploadLimits:t.workspaceUploadLimits})),e}var c=i();export{i as createApp,c as default};
package/dist/config.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { type AgentAuthConfig, type AgentWorkspacesConfig } from "@messenger-agent/shared/agent-config";
2
+ import { type WorkspaceUploadLimits } from "@messenger-agent/shared/workspace-upload";
2
3
  import { type LogLevel } from "@messenger-agent/shared/logger";
3
4
  export type MessengerAgentConfig = {
4
5
  configPath: string;
@@ -6,6 +7,7 @@ export type MessengerAgentConfig = {
6
7
  logFile?: string;
7
8
  port: number;
8
9
  textMaxBytes: number;
10
+ workspaceUploadLimits?: WorkspaceUploadLimits;
9
11
  tunnel: {
10
12
  enabled: boolean;
11
13
  serverUrl?: string;
package/dist/config.js CHANGED
@@ -1 +1 @@
1
- import{existsSync as m,readFileSync as _}from"node:fs";import{parse as u}from"yaml";import{z as e}from"zod";import{normalizeOptional as o,parseAgentAuthConfig as f,parseAgentWorkspacesConfig as v,resolveAgentBridgeUrls as d}from"@messenger-agent/shared/agent-config";import{DEFAULT_TEXT_MAX_BYTES as b}from"@messenger-agent/shared/workspace-files";import{logger as i,normalizeLogLevel as x,setLogFile as A,setLogLevel as M}from"@messenger-agent/shared/logger";const k=e.object({server:e.string().optional(),log_level:e.string().optional(),log_file:e.string().optional(),port:e.coerce.number().int().positive().optional(),messenger:e.object({port:e.coerce.number().int().positive().optional(),log_file:e.string().optional()}).optional(),auth_tokens:e.record(e.string(),e.string()).optional(),workspaces:e.array(e.object({id:e.string().optional(),name:e.string().optional(),path:e.string().optional()})).optional(),workspace_file_text_max_bytes:e.coerce.number().int().positive().optional(),tunnel:e.object({enabled:e.boolean().optional(),server_url:e.string().optional(),tunnel_id:e.string().optional(),token:e.string().optional(),reconnect_initial_ms:e.number().int().positive().optional(),reconnect_max_ms:e.number().int().positive().optional(),heartbeat_interval_ms:e.number().int().positive().optional()}).optional()}).loose();function y(t=process.env.MESSENGER_AGENT_CONFIG_PATH??process.env.AGENT_CONFIG_PATH??"./agent-config.yaml"){let s={};if(m(t))try{s=u(_(t,"utf8"))}catch(g){i.error(`Failed to parse messenger-agent config at ${t}:`,g)}else i.warn(`Messenger-agent config file not found at ${t}`);const r=k.safeParse(s??{});r.success||i.error(`Invalid messenger-agent config at ${t}:`,r.error.issues);const n=r.success?r.data:{},l=x(n.log_level),a=o(n.messenger?.log_file)??o(n.log_file),p=n.tunnel?.enabled??!1,c=d(n.server);return M(l),A(a),{configPath:t,logLevel:l,logFile:a,port:n.messenger?.port??n.port??8301,textMaxBytes:n.workspace_file_text_max_bytes??b,tunnel:{enabled:p,serverUrl:o(n.tunnel?.server_url)??(p?c.tunnel:void 0),tunnelId:o(n.tunnel?.tunnel_id),token:o(n.tunnel?.token),reconnectInitialMs:n.tunnel?.reconnect_initial_ms??1e3,reconnectMaxMs:n.tunnel?.reconnect_max_ms??3e4,heartbeatIntervalMs:n.tunnel?.heartbeat_interval_ms??3e4},...f(n),...v(n)}}const S=y();export{S as appConfig,y as loadMessengerAgentConfig};
1
+ import{existsSync as m,readFileSync as _}from"node:fs";import{parse as u}from"yaml";import{z as e}from"zod";import{normalizeOptional as o,parseAgentAuthConfig as f,parseAgentWorkspacesConfig as v,resolveAgentBridgeUrls as d}from"@messenger-agent/shared/agent-config";import{DEFAULT_TEXT_MAX_BYTES as b}from"@messenger-agent/shared/workspace-files";import{workspaceUploadLimitsFromEnvironment as x}from"@messenger-agent/shared/workspace-upload";import{logger as i,normalizeLogLevel as A,setLogFile as k,setLogLevel as E}from"@messenger-agent/shared/logger";const L=e.object({server:e.string().optional(),log_level:e.string().optional(),log_file:e.string().optional(),port:e.coerce.number().int().positive().optional(),messenger:e.object({port:e.coerce.number().int().positive().optional(),log_file:e.string().optional()}).optional(),auth_tokens:e.record(e.string(),e.string()).optional(),workspaces:e.array(e.object({id:e.string().optional(),name:e.string().optional(),path:e.string().optional()})).optional(),workspace_file_text_max_bytes:e.coerce.number().int().positive().optional(),tunnel:e.object({enabled:e.boolean().optional(),server_url:e.string().optional(),tunnel_id:e.string().optional(),token:e.string().optional(),reconnect_initial_ms:e.number().int().positive().optional(),reconnect_max_ms:e.number().int().positive().optional(),heartbeat_interval_ms:e.number().int().positive().optional()}).optional()}).loose();function M(t=process.env.MESSENGER_AGENT_CONFIG_PATH??process.env.AGENT_CONFIG_PATH??"./agent-config.yaml"){let s={};if(m(t))try{s=u(_(t,"utf8"))}catch(g){i.error(`Failed to parse messenger-agent config at ${t}:`,g)}else i.warn(`Messenger-agent config file not found at ${t}`);const r=L.safeParse(s??{});r.success||i.error(`Invalid messenger-agent config at ${t}:`,r.error.issues);const n=r.success?r.data:{},l=A(n.log_level),a=o(n.messenger?.log_file)??o(n.log_file),p=n.tunnel?.enabled??!1,c=d(n.server);return E(l),k(a),{configPath:t,logLevel:l,logFile:a,port:n.messenger?.port??n.port??8301,textMaxBytes:n.workspace_file_text_max_bytes??b,workspaceUploadLimits:x(),tunnel:{enabled:p,serverUrl:o(n.tunnel?.server_url)??(p?c.tunnel:void 0),tunnelId:o(n.tunnel?.tunnel_id),token:o(n.tunnel?.token),reconnectInitialMs:n.tunnel?.reconnect_initial_ms??1e3,reconnectMaxMs:n.tunnel?.reconnect_max_ms??3e4,heartbeatIntervalMs:n.tunnel?.heartbeat_interval_ms??3e4},...f(n),...v(n)}}const U=M();export{U as appConfig,M as loadMessengerAgentConfig};
@@ -4,6 +4,7 @@ export declare class WorkspaceTunnelClient {
4
4
  private stopped;
5
5
  private socket?;
6
6
  private readonly running;
7
+ private readonly streamedRequests;
7
8
  private readonly workspaceRoutes;
8
9
  private readonly fileRoutes;
9
10
  constructor(config?: MessengerAgentConfig);
@@ -11,5 +12,6 @@ export declare class WorkspaceTunnelClient {
11
12
  stop(): void;
12
13
  private connectOnce;
13
14
  private send;
15
+ private startStreamedRequest;
14
16
  private handleRequest;
15
17
  }
@@ -1 +1 @@
1
- import{createRequire as h}from"node:module";import p from"ws";import{logger as a}from"@messenger-agent/shared/logger";import{reconnectDelayWithJitter as d}from"@messenger-agent/shared/reconnect-backoff";import{createAgentFileRoutes as g}from"@messenger-agent/shared/agent-files";import{createPrefixedRouteRequest as k,createWorkspaceRouteRequest as w,isAgentFileHttpTunnelRequest as T,isWorkspaceHttpTunnelRequest as m,sendHttpTunnelResponse as y}from"@messenger-agent/shared/tunnel-http";import{CODING_AGENT_TUNNEL_DUPLICATE_CLOSE_CODE as f,CODING_AGENT_TUNNEL_PROTOCOL_VERSION as R}from"@messenger-agent/shared/tunnel-protocol";import{createAgentWorkspaceRoutes as v}from"@messenger-agent/shared/workspace-routes";import{appConfig as b}from"./config.js";const E=h(import.meta.url)("../package.json").version,M=l=>new Promise(e=>setTimeout(e,l)),I=3;function _(l){try{const e=JSON.parse(l.toString());return e&&typeof e=="object"&&"type"in e?e:void 0}catch{return}}class D{config;stopped=!1;socket;running=new Map;workspaceRoutes;fileRoutes=g();constructor(e=b){this.config=e,this.workspaceRoutes=v({config:e,textMaxBytes:e.textMaxBytes,allowManagement:!0,onWorkspacesChanged:()=>{process.send?.({type:"client.workspaces.changed"})}})}async start(){if(!this.config.tunnel.enabled)return;if(!this.config.tunnel.serverUrl||!this.config.tunnel.tunnelId||!this.config.tunnel.token)throw new Error("Workspace tunnel requires server_url, tunnel_id, and token when enabled");let e=this.config.tunnel.reconnectInitialMs;for(;!this.stopped;){try{e=await this.connectOnce()==="duplicate"?this.config.tunnel.reconnectMaxMs:this.config.tunnel.reconnectInitialMs}catch(s){this.stopped||a.warn("[WorkspaceTunnel] connection failed:",s)}this.stopped||(await M(d(e)),e=Math.min(e*2,this.config.tunnel.reconnectMaxMs))}}stop(){this.stopped=!0;for(const e of this.running.values())e.abort();this.running.clear(),this.socket?.close()}connectOnce(){return new Promise((e,s)=>{const r=new p(this.config.tunnel.serverUrl,{headers:{Authorization:`Bearer ${this.config.tunnel.token}`}});this.socket=r;let o,c=0,i=!1;const u=()=>{o&&clearInterval(o),this.socket===r&&(this.socket=void 0);for(const t of this.running.values())t.abort(new Error("Workspace tunnel closed"));this.running.clear()};r.on("open",()=>{c=Date.now(),this.send({type:"hello",protocolVersion:R,tunnelId:this.config.tunnel.tunnelId,agentType:"workspace",capabilities:["workspace.files","workspace.git","workspace.tasks"],clientVersion:E,workspaces:[...this.config.workspaces.values()].map(({id:t,name:n})=>({id:t,name:n})).sort((t,n)=>t.id.localeCompare(n.id))}),o=setInterval(()=>{if(Date.now()-c>=this.config.tunnel.heartbeatIntervalMs*I){a.warn("[WorkspaceTunnel] websocket tunnel server is not responding; terminating stale connection"),r.terminate();return}this.send({type:"heartbeat"})},this.config.tunnel.heartbeatIntervalMs),a.info(`[WorkspaceTunnel] connected to ${this.config.tunnel.serverUrl} as ${this.config.tunnel.tunnelId}`)}),r.on("message",t=>{c=Date.now();const n=_(t);!n||n.type==="heartbeat"||(n.type==="cancel"?this.running.get(n.id)?.abort():n.type==="request"&&this.handleRequest(n))}),r.once("error",t=>{i||(i=!0,u(),s(t))}),r.once("close",(t,n)=>{u(),t===f?a.warn(`[WorkspaceTunnel] duplicate connection rejected; retrying with backoff: ${n.toString()}`):t===1008&&(this.stopped=!0,a.warn(`[WorkspaceTunnel] connection rejected with code ${t}; reconnect disabled until service restart: ${n.toString()}`)),i||(i=!0,e(t===f?"duplicate":"closed"))})})}send(e){this.socket?.readyState===p.OPEN&&this.socket.send(JSON.stringify(e))}async handleRequest(e){const s=T(e);if(!s&&!m(e)){this.send({type:"response.error",id:e.id,code:"unsupported_request",errorText:`Unsupported workspace tunnel request: ${e.method} ${e.path}`});return}const r=new AbortController;this.running.set(e.id,r);try{const o=s?k(e,"/v1/files",r.signal):w(e,r.signal),c=await(s?this.fileRoutes:this.workspaceRoutes).fetch(o);await y(e.id,c,i=>this.send(i))}catch(o){this.send({type:"response.error",id:e.id,code:"request_failed",errorText:o instanceof Error?o.message:"Workspace tunnel request failed"})}finally{this.running.delete(e.id)}}}export{D as WorkspaceTunnelClient};
1
+ import{createRequire as f}from"node:module";import p from"ws";import{logger as c}from"@messenger-agent/shared/logger";import{createIncomingRequestStream as m}from"@messenger-agent/shared/incoming-request-stream";import{reconnectDelayWithJitter as k}from"@messenger-agent/shared/reconnect-backoff";import{createAgentFileRoutes as w}from"@messenger-agent/shared/agent-files";import{createPrefixedRouteRequest as R,createWorkspaceRouteRequest as q,isAgentFileHttpTunnelRequest as g,isWorkspaceHttpTunnelRequest as y,sendHttpTunnelResponse as b}from"@messenger-agent/shared/tunnel-http";import{CODING_AGENT_TUNNEL_DUPLICATE_CLOSE_CODE as h,CODING_AGENT_TUNNEL_PROTOCOL_VERSION as T}from"@messenger-agent/shared/tunnel-protocol";import{createAgentWorkspaceRoutes as v}from"@messenger-agent/shared/workspace-routes";import{appConfig as E}from"./config.js";const M=f(import.meta.url)("../package.json").version,I=l=>new Promise(e=>setTimeout(e,l)),W=3;function S(l){try{const e=JSON.parse(l.toString());return e&&typeof e=="object"&&"type"in e?e:void 0}catch{return}}class P{config;stopped=!1;socket;running=new Map;streamedRequests=new Map;workspaceRoutes;fileRoutes=w();constructor(e=E){this.config=e,this.workspaceRoutes=v({config:e,textMaxBytes:e.textMaxBytes,uploadLimits:e.workspaceUploadLimits,allowManagement:!0,onWorkspacesChanged:()=>{process.send?.({type:"client.workspaces.changed"})}})}async start(){if(!this.config.tunnel.enabled)return;if(!this.config.tunnel.serverUrl||!this.config.tunnel.tunnelId||!this.config.tunnel.token)throw new Error("Workspace tunnel requires server_url, tunnel_id, and token when enabled");let e=this.config.tunnel.reconnectInitialMs;for(;!this.stopped;){try{e=await this.connectOnce()==="duplicate"?this.config.tunnel.reconnectMaxMs:this.config.tunnel.reconnectInitialMs}catch(r){this.stopped||c.warn("[WorkspaceTunnel] connection failed:",r)}this.stopped||(await I(k(e)),e=Math.min(e*2,this.config.tunnel.reconnectMaxMs))}}stop(){this.stopped=!0;for(const e of this.running.values())e.abort();this.running.clear();for(const e of this.streamedRequests.values())e.stream.error(new Error("Workspace tunnel stopped"));this.streamedRequests.clear(),this.socket?.close()}connectOnce(){return new Promise((e,r)=>{const s=new p(this.config.tunnel.serverUrl,{headers:{Authorization:`Bearer ${this.config.tunnel.token}`}});this.socket=s;let o,i=0,a=!1;const u=()=>{o&&clearInterval(o),this.socket===s&&(this.socket=void 0);for(const n of this.running.values())n.abort(new Error("Workspace tunnel closed"));this.running.clear();for(const n of this.streamedRequests.values())n.stream.error(new Error("Workspace tunnel closed"));this.streamedRequests.clear()};s.on("open",()=>{i=Date.now(),this.send({type:"hello",protocolVersion:T,tunnelId:this.config.tunnel.tunnelId,agentType:"workspace",capabilities:["workspace.files","workspace.files.upload.v1","workspace.git","workspace.tasks"],clientVersion:M,workspaces:[...this.config.workspaces.values()].map(({id:n,name:t,path:d})=>({id:n,name:t,path:d})).sort((n,t)=>n.id.localeCompare(t.id))}),o=setInterval(()=>{if(Date.now()-i>=this.config.tunnel.heartbeatIntervalMs*W){c.warn("[WorkspaceTunnel] websocket tunnel server is not responding; terminating stale connection"),s.terminate();return}this.send({type:"heartbeat"})},this.config.tunnel.heartbeatIntervalMs),c.info(`[WorkspaceTunnel] connected to ${this.config.tunnel.serverUrl} as ${this.config.tunnel.tunnelId}`)}),s.on("message",n=>{i=Date.now();const t=S(n);if(!(!t||t.type==="heartbeat"))if(t.type==="cancel")this.running.get(t.id)?.abort(),this.streamedRequests.get(t.id)?.stream.error(new Error("Workspace tunnel request cancelled")),this.streamedRequests.delete(t.id);else if(t.type==="request")this.handleRequest(t);else if(t.type==="request.start")this.startStreamedRequest(t);else if(t.type==="request.chunk"){const d=this.streamedRequests.get(t.id);d&&!d.stream.enqueue(Buffer.from(t.body.data,"base64"))&&(this.running.get(t.id)?.abort(new Error("Streamed request body exceeded its buffer limit")),this.streamedRequests.delete(t.id))}else t.type==="request.end"&&(this.streamedRequests.get(t.id)?.stream.close(),this.streamedRequests.delete(t.id))}),s.once("error",n=>{a||(a=!0,u(),r(n))}),s.once("close",(n,t)=>{u(),n===h?c.warn(`[WorkspaceTunnel] duplicate connection rejected; retrying with backoff: ${t.toString()}`):n===1008&&(this.stopped=!0,c.warn(`[WorkspaceTunnel] connection rejected with code ${n}; reconnect disabled until service restart: ${t.toString()}`)),a||(a=!0,e(n===h?"duplicate":"closed"))})})}send(e){this.socket?.readyState===p.OPEN&&this.socket.send(JSON.stringify(e))}startStreamedRequest(e){if(this.streamedRequests.has(e.id)||this.running.has(e.id))return;const r=m({onConsumed:s=>this.send({type:"request.ack",id:e.id,bytes:s}),onCancel:()=>this.running.get(e.id)?.abort()});this.streamedRequests.set(e.id,{stream:r,request:e}),this.handleRequest(e,r.body)}async handleRequest(e,r){const s=g(e);if(!s&&!y(e)){this.send({type:"response.error",id:e.id,code:"unsupported_request",errorText:`Unsupported workspace tunnel request: ${e.method} ${e.path}`}),this.streamedRequests.delete(e.id);return}const o=new AbortController;this.running.set(e.id,o);try{const i=s?R(e,"/v1/files",o.signal,r):q(e,o.signal,r),a=await(s?this.fileRoutes:this.workspaceRoutes).fetch(i);await b(e.id,a,u=>this.send(u))}catch(i){this.send({type:"response.error",id:e.id,code:"request_failed",errorText:i instanceof Error?i.message:"Workspace tunnel request failed"})}finally{this.running.delete(e.id),this.streamedRequests.delete(e.id)}}}export{P as WorkspaceTunnelClient};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@messenger-agent/messenger-agent",
3
- "version": "0.35.0",
3
+ "version": "0.36.0-alpha.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -20,7 +20,7 @@
20
20
  "ws": "^8.18.3",
21
21
  "yaml": "^2.9.0",
22
22
  "zod": "^4.4.3",
23
- "@messenger-agent/shared": "0.35.0"
23
+ "@messenger-agent/shared": "0.36.0-alpha.1"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@types/ws": "^8.18.1"