@agimon-ai/foundation-process-registry 0.2.9 → 0.3.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/ProcessRegistryService.cjs +1 -1
- package/dist/ProcessRegistryService.cjs.map +1 -1
- package/dist/ProcessRegistryService.mjs +1 -1
- package/dist/ProcessRegistryService.mjs.map +1 -1
- package/dist/index.cjs +2 -1
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +25 -1
- package/dist/index.d.mts +26 -2
- package/dist/index.mjs +2 -1
- package/dist/index.mjs.map +1 -0
- package/package.json +2 -2
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require(`node:path`);c=s(c);let l=require(`node:crypto`),u=require(`node:fs`);u=s(u);let d=require(`node:fs/promises`);d=s(d);let f=require(`zod`),p=require(`node:os`);p=s(p);let m=require(`@agimon-ai/foundation-port-registry`);const h=1,g=f.z.enum([`service`,`tool`]),_=f.z.record(f.z.string(),f.z.unknown()),v=f.z.object({repositoryPath:f.z.string().trim().min(1,`repositoryPath is required`),serviceName:f.z.string().trim().min(1,`serviceName is required`),serviceType:g,environment:f.z.string().trim().min(1).optional(),pid:f.z.number().int().min(1),port:f.z.number().int().min(1).max(65535).optional(),host:f.z.string().trim().min(1).optional(),command:f.z.string().trim().min(1).optional(),args:f.z.array(f.z.string()).optional(),metadata:_.optional(),createdAt:f.z.string().trim().min(1),updatedAt:f.z.string().trim().min(1)}),y=f.z.object({version:f.z.literal(1),updatedAt:f.z.string().trim().min(1),entries:f.z.array(v)}),b=f.z.object({repositoryPath:f.z.string().trim().min(1),serviceName:f.z.string().trim().min(1),serviceType:g.default(`service`),environment:f.z.string().trim().min(1).optional(),pid:f.z.number().int().min(1),port:f.z.number().int().min(1).max(65535).optional(),host:f.z.string().trim().min(1).optional(),command:f.z.string().trim().min(1).optional(),args:f.z.array(f.z.string()).optional(),metadata:_.optional(),force:f.z.boolean().optional()}),x=f.z.object({repositoryPath:f.z.string().trim().min(1),serviceName:f.z.string().trim().min(1),serviceType:g.optional(),pid:f.z.number().int().min(1).optional(),environment:f.z.string().trim().min(1).optional(),force:f.z.boolean().optional(),kill:f.z.boolean().optional().default(!0),releasePort:f.z.boolean().optional().default(!0)}),S=f.z.object({repositoryPath:f.z.string().trim().min(1).optional(),serviceType:g.optional(),serviceName:f.z.string().trim().min(1).optional(),environment:f.z.string().trim().min(1).optional()}),C=f.z.object({success:f.z.boolean(),pid:f.z.number().int().min(1).optional(),record:v.optional(),error:f.z.string().optional()});var w=class extends Error{code;constructor(e,t,n){super(e,n),this.name=`ProcessRegistryError`,this.code=t}};const T=c.default.join(p.default.homedir(),`.process-registry`,`processes.json`),E=`${T}.lock`,D=75,O=80,k=5e3,A=e=>c.default.resolve(e),j=e=>`${e}.${(0,l.randomBytes)(6).toString(`hex`)}.tmp`;var M=class e{registryPath;lockPath;activeLockToken;lockCleanupHandlers=new Map;constructor(t=T,n,r=new m.PortRegistryService(process.env.PORT_REGISTRY_PATH)){this.portRegistry=r,this.registryPath=e.resolveRegistryPath(t),this.lockPath=n??`${this.registryPath}.lock`}static resolveRegistryPath(e){if(!e)return T;let t=c.default.isAbsolute(e)?e:c.default.join(process.cwd(),e);return c.default.extname(t)===`.json`?t:c.default.join(t,`processes.json`)}async registerProcess(e){let t=b.parse(e);return this.withLock(async()=>{let e=await this.loadState(),n=A(t.repositoryPath),r=t.serviceType??`service`,i=t.environment??process.env.NODE_ENV??`development`,a=await this.pruneState(e),o=this.findEntry(a,{repositoryPath:n,serviceName:t.serviceName,serviceType:r,environment:i});if(o){if(o.pid===t.pid)return o.updatedAt=new Date().toISOString(),o.port=t.port??o.port,o.host=t.host??o.host,o.command=t.command??o.command,o.args=t.args??o.args,o.metadata=t.metadata??o.metadata,await this.saveState(a),this.createSuccessResponse(t.pid,o);let e=this.isProcessRunning(o.pid);if(e&&!t.force)return this.createFailureResponse(`Process already registered for ${t.serviceName} in requested scope`);e&&await this.terminateProcess(o.pid),await this.releaseAssociatedPort(o),this.removeMatchingEntries(a,{repositoryPath:n,serviceName:t.serviceName,serviceType:r,environment:i})}let s=new Date().toISOString(),c={repositoryPath:n,serviceName:t.serviceName,serviceType:r,environment:i,pid:t.pid,port:t.port,host:t.host,command:t.command,args:t.args,metadata:t.metadata,createdAt:s,updatedAt:s};return a.entries.push(c),await this.saveState(a),this.createSuccessResponse(t.pid,c)})}async releaseProcess(e){let t=x.parse(e);return this.withLock(async()=>{let e=await this.loadState(),n=await this.pruneMissingRepositories(e),r=A(t.repositoryPath),i=t.serviceType??`service`,a=t.environment??process.env.NODE_ENV??`development`,o=n.entries.filter(e=>!(e.repositoryPath!==r||e.serviceName!==t.serviceName||e.serviceType!==i||t.environment&&e.environment!==a||typeof t.pid==`number`&&e.pid!==t.pid));if(o.length===0)return this.createFailureResponse(`No matching process entry for ${t.serviceName}`);let s=[],c=new Set;for(let e of o){let n=this.entryKey(e);try{(t.kill??!0)&&this.isProcessRunning(e.pid)&&await this.terminateProcess(e.pid),(t.releasePort??!0)&&await this.releaseAssociatedPort(e),c.add(n)}catch(t){s.push(`${e.serviceName} (pid ${e.pid}): ${t instanceof Error?t.message:String(t)}`)}}return c.size>0&&(n.entries=n.entries.filter(e=>!c.has(this.entryKey(e))),await this.saveState(n)),s.length>0?this.createFailureResponse(s.join(`; `)):this.createSuccessResponse()})}async listProcesses(e={}){let t=S.parse(e);return this.withLock(async()=>{let e=await this.loadState();return[...(await this.pruneState(e)).entries].filter(e=>!(t.repositoryPath&&e.repositoryPath!==A(t.repositoryPath)||t.serviceType&&e.serviceType!==t.serviceType||t.serviceName&&e.serviceName!==t.serviceName||t.environment&&e.environment!==t.environment))})}async withLock(e){let t=`${process.pid}-${(0,l.randomBytes)(6).toString(`hex`)}`;await this.acquireLock(t);try{return await e()}finally{await this.releaseLock(t)}}async loadState(){await d.default.mkdir(c.default.dirname(this.registryPath),{recursive:!0});try{let e=await d.default.readFile(this.registryPath,`utf-8`),t=JSON.parse(e);return y.parse(t)}catch(e){let t=e;if(t.code===`ENOENT`)return{version:1,updatedAt:new Date().toISOString(),entries:[]};if(t instanceof SyntaxError){let e=`${this.registryPath}.corrupt.${Date.now()}`;await d.default.rename(this.registryPath,e).catch(()=>void 0)}throw new w(`Failed to read registry file: ${e instanceof Error?e.message:String(e)}`,`REGISTRY_READ_FAILED`,{cause:e})}}async saveState(e){await d.default.mkdir(c.default.dirname(this.registryPath),{recursive:!0});let t={...e,updatedAt:new Date().toISOString(),entries:[...e.entries]},n=j(this.registryPath);await d.default.writeFile(n,JSON.stringify(t,null,2),`utf-8`),await d.default.rename(n,this.registryPath)}async pruneState(e){let t=await this.pruneMissingRepositories(e);return this.pruneDeadProcesses(t)}async pruneMissingRepositories(e){let t=(await Promise.all(e.entries.map(async e=>({entry:e,exists:await this.pathExists(e.repositoryPath)})))).filter(e=>e.exists).map(e=>e.entry);return t.length!==e.entries.length&&(e.entries=t,await this.saveState(e)),e}async pruneDeadProcesses(e){let t=[],n=!1;for(let r of e.entries){if(this.isProcessRunning(r.pid)){t.push(r);continue}n=!0,await this.releaseAssociatedPort(r)}return n&&(e.entries=t,await this.saveState(e)),e}async acquireLock(e){await d.default.mkdir(c.default.dirname(this.lockPath),{recursive:!0});for(let t=0;t<80;t+=1)try{let t={pid:process.pid,token:e,createdAt:new Date().toISOString()};await d.default.writeFile(this.lockPath,JSON.stringify(t),{flag:`wx`}),this.registerLockCleanup(e);return}catch(e){if(e.code!==`EEXIST`)throw new w(`Failed to acquire registry lock: ${e instanceof Error?e.message:String(e)}`,`REGISTRY_LOCK_FAILED`,{cause:e});if(await this.isStaleLock()){await d.default.unlink(this.lockPath).catch(()=>void 0),--t;continue}await this.delay(75)}throw new w(`Unable to acquire registry lock (timeout)`,`REGISTRY_LOCK_FAILED`)}async releaseLock(e){try{let t=await d.default.readFile(this.lockPath,`utf-8`);JSON.parse(t).token===e&&await d.default.unlink(this.lockPath)}catch{}finally{this.unregisterLockCleanup(e)}}async isStaleLock(){try{let e=await d.default.readFile(this.lockPath,`utf-8`),t=JSON.parse(e);if(t.pid&&this.isProcessRunning(t.pid)){let e=Date.now()-new Date(t.createdAt).getTime();return!(Number.isFinite(e)&&e<k)}return!0}catch{return!0}}isProcessRunning(e){try{return process.kill(e,0),!0}catch{return!1}}registerLockCleanup(e){this.unregisterLockCleanup(this.activeLockToken),this.activeLockToken=e;let t=()=>{this.releaseLockSync(e)};for(let e of[`exit`,`SIGINT`,`SIGTERM`,`uncaughtException`,`unhandledRejection`])process.once(e,t),this.lockCleanupHandlers.set(e,t)}unregisterLockCleanup(e){if(!(!e||this.activeLockToken!==e)){for(let[e,t]of this.lockCleanupHandlers)process.off(e,t);this.lockCleanupHandlers.clear(),this.activeLockToken=void 0}}releaseLockSync(e){try{let t=u.default.readFileSync(this.lockPath,`utf-8`);JSON.parse(t).token===e&&u.default.unlinkSync(this.lockPath)}catch{}}async terminateProcess(e){try{process.kill(e,0)}catch{return}try{process.kill(e,`SIGTERM`)}catch(t){if(t.code===`ESRCH`)return;throw Error(`Failed to send SIGTERM to process ${e}: ${t instanceof Error?t.message:String(t)}`,{cause:t})}if(await this.delay(500),this.isProcessRunning(e)){try{process.kill(e,`SIGKILL`)}catch(t){if(t.code===`ESRCH`)return;throw Error(`Failed to send SIGKILL to process ${e}: ${t instanceof Error?t.message:String(t)}`,{cause:t})}if(await this.delay(250),this.isProcessRunning(e))throw Error(`Process ${e} did not exit after SIGKILL`)}}async releaseAssociatedPort(e){if(!(!this.portRegistry||!e.port))try{let t=await this.portRegistry.releasePort({repositoryPath:e.repositoryPath,serviceName:e.serviceName,serviceType:e.serviceType,environment:e.environment,pid:e.pid,force:!0});if(!t.success&&t.error&&!t.error.includes(`No matching registry entry`))throw Error(t.error)}catch{}}entryKey(e){return[e.repositoryPath,e.serviceName,e.serviceType,e.environment??``,String(e.pid)].join(`|`)}findEntry(e,t){return e.entries.find(e=>e.repositoryPath===t.repositoryPath&&e.serviceName===t.serviceName&&e.serviceType===t.serviceType&&(t.environment?e.environment===t.environment:!0))}removeMatchingEntries(e,t){e.entries=e.entries.filter(e=>!(e.repositoryPath===t.repositoryPath&&e.serviceName===t.serviceName&&e.serviceType===t.serviceType&&(!t.environment||e.environment===t.environment)))}createSuccessResponse(e,t){return C.parse({success:!0,...typeof e==`number`?{pid:e}:{},...t?{record:t}:{}})}createFailureResponse(e){return C.parse({success:!1,error:e})}async pathExists(e){try{return await d.default.access(e),!0}catch{return!1}}delay(e){return new Promise(t=>setTimeout(t,e))}};Object.defineProperty(exports,`_`,{enumerable:!0,get:function(){return x}}),Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return 75}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return A}}),Object.defineProperty(exports,`d`,{enumerable:!0,get:function(){return w}}),Object.defineProperty(exports,`f`,{enumerable:!0,get:function(){return v}}),Object.defineProperty(exports,`g`,{enumerable:!0,get:function(){return b}}),Object.defineProperty(exports,`h`,{enumerable:!0,get:function(){return 1}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return 80}}),Object.defineProperty(exports,`l`,{enumerable:!0,get:function(){return S}}),Object.defineProperty(exports,`m`,{enumerable:!0,get:function(){return y}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return E}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return k}}),Object.defineProperty(exports,`p`,{enumerable:!0,get:function(){return C}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return T}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return j}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return M}}),Object.defineProperty(exports,`u`,{enumerable:!0,get:function(){return _}}),Object.defineProperty(exports,`v`,{enumerable:!0,get:function(){return g}}),Object.defineProperty(exports,`y`,{enumerable:!0,get:function(){return s}});
|
|
1
|
+
var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require(`node:path`);c=s(c);let l=require(`node:crypto`),u=require(`node:fs`);u=s(u);let d=require(`node:fs/promises`);d=s(d);let f=require(`@agimon-ai/foundation-port-registry`),p=require(`zod`),m=require(`node:os`);m=s(m);const h=1,g=p.z.enum([`service`,`tool`]),_=p.z.record(p.z.string(),p.z.unknown()),v=p.z.object({repositoryPath:p.z.string().trim().min(1,`repositoryPath is required`),serviceName:p.z.string().trim().min(1,`serviceName is required`),serviceType:g,environment:p.z.string().trim().min(1).optional(),pid:p.z.number().int().min(1),port:p.z.number().int().min(1).max(65535).optional(),host:p.z.string().trim().min(1).optional(),command:p.z.string().trim().min(1).optional(),args:p.z.array(p.z.string()).optional(),metadata:_.optional(),createdAt:p.z.string().trim().min(1),updatedAt:p.z.string().trim().min(1)}),y=p.z.object({version:p.z.literal(1),updatedAt:p.z.string().trim().min(1),entries:p.z.array(v)}),b=p.z.object({repositoryPath:p.z.string().trim().min(1),serviceName:p.z.string().trim().min(1),serviceType:g.default(`service`),environment:p.z.string().trim().min(1).optional(),pid:p.z.number().int().min(1),port:p.z.number().int().min(1).max(65535).optional(),host:p.z.string().trim().min(1).optional(),command:p.z.string().trim().min(1).optional(),args:p.z.array(p.z.string()).optional(),metadata:_.optional(),force:p.z.boolean().optional()}),x=p.z.object({repositoryPath:p.z.string().trim().min(1),serviceName:p.z.string().trim().min(1),serviceType:g.optional(),pid:p.z.number().int().min(1).optional(),environment:p.z.string().trim().min(1).optional(),force:p.z.boolean().optional(),kill:p.z.boolean().optional().default(!0),releasePort:p.z.boolean().optional().default(!0)}),S=p.z.object({repositoryPath:p.z.string().trim().min(1).optional(),serviceType:g.optional(),serviceName:p.z.string().trim().min(1).optional(),environment:p.z.string().trim().min(1).optional()}),C=p.z.object({success:p.z.boolean(),pid:p.z.number().int().min(1).optional(),record:v.optional(),error:p.z.string().optional()});var w=class extends Error{code;constructor(e,t,n){super(e,n),this.name=`ProcessRegistryError`,this.code=t}};const T=c.default.join(m.default.homedir(),`.process-registry`,`processes.json`),E=`${T}.lock`,D=75,O=80,k=5e3,A=e=>c.default.resolve(e),j=e=>`${e}.${(0,l.randomBytes)(6).toString(`hex`)}.tmp`;function M(e,t){if(!e)return;let n=c.default.resolve(e);return c.default.extname(n)===`.json`?c.default.join(c.default.dirname(n),t):c.default.join(n,t)}var N=class e{registryPath;lockPath;activeLockToken;lockCleanupHandlers=new Map;constructor(t=T,n,r=new f.PortRegistryService(process.env.PORT_REGISTRY_PATH)){this.portRegistry=r,this.registryPath=e.resolveRegistryPath(t),this.lockPath=n??`${this.registryPath}.lock`}static resolveRegistryPath(e){if(!e)return T;let t=c.default.isAbsolute(e)?e:c.default.join(process.cwd(),e);return c.default.extname(t)===`.json`?t:c.default.join(t,`processes.json`)}async registerProcess(e){let t=b.parse(e);return this.withLock(async()=>{let e=await this.loadState(),n=A(t.repositoryPath),r=t.serviceType??`service`,i=t.environment??process.env.NODE_ENV??`development`,a=await this.pruneState(e),o=this.findEntry(a,{repositoryPath:n,serviceName:t.serviceName,serviceType:r,environment:i});if(o){if(o.pid===t.pid)return o.updatedAt=new Date().toISOString(),o.port=t.port??o.port,o.host=t.host??o.host,o.command=t.command??o.command,o.args=t.args??o.args,o.metadata=t.metadata??o.metadata,await this.saveState(a),this.createSuccessResponse(t.pid,o);let e=this.isProcessRunning(o.pid);if(e&&!(t.force??!0))return this.createFailureResponse(`Process already registered for ${t.serviceName} in requested scope`);e&&await this.terminateProcess(o.pid),await this.releaseAssociatedPort(o),this.removeMatchingEntries(a,{repositoryPath:n,serviceName:t.serviceName,serviceType:r,environment:i})}let s=new Date().toISOString(),c={repositoryPath:n,serviceName:t.serviceName,serviceType:r,environment:i,pid:t.pid,port:t.port,host:t.host,command:t.command,args:t.args,metadata:t.metadata,createdAt:s,updatedAt:s};return a.entries.push(c),await this.saveState(a),this.createSuccessResponse(t.pid,c)})}async releaseProcess(e){let t=x.parse(e);return this.withLock(async()=>{let e=await this.loadState(),n=await this.pruneMissingRepositories(e),r=A(t.repositoryPath),i=t.serviceType??`service`,a=t.environment??process.env.NODE_ENV??`development`,o=n.entries.filter(e=>!(e.repositoryPath!==r||e.serviceName!==t.serviceName||e.serviceType!==i||t.environment&&e.environment!==a||typeof t.pid==`number`&&e.pid!==t.pid));if(o.length===0)return this.createFailureResponse(`No matching process entry for ${t.serviceName}`);let s=[],c=new Set;for(let e of o){let n=this.entryKey(e);try{(t.kill??!0)&&this.isProcessRunning(e.pid)&&await this.terminateProcess(e.pid),(t.releasePort??!0)&&await this.releaseAssociatedPort(e),c.add(n)}catch(t){s.push(`${e.serviceName} (pid ${e.pid}): ${t instanceof Error?t.message:String(t)}`)}}return c.size>0&&(n.entries=n.entries.filter(e=>!c.has(this.entryKey(e))),await this.saveState(n)),s.length>0?this.createFailureResponse(s.join(`; `)):this.createSuccessResponse()})}async listProcesses(e={}){let t=S.parse(e);return this.withLock(async()=>{let e=await this.loadState();return[...(await this.pruneState(e)).entries].filter(e=>!(t.repositoryPath&&e.repositoryPath!==A(t.repositoryPath)||t.serviceType&&e.serviceType!==t.serviceType||t.serviceName&&e.serviceName!==t.serviceName||t.environment&&e.environment!==t.environment))})}async withLock(e){let t=`${process.pid}-${(0,l.randomBytes)(6).toString(`hex`)}`;await this.acquireLock(t);try{return await e()}finally{await this.releaseLock(t)}}async loadState(){await d.default.mkdir(c.default.dirname(this.registryPath),{recursive:!0});try{let e=await d.default.readFile(this.registryPath,`utf-8`),t=JSON.parse(e);return y.parse(t)}catch(e){let t=e;if(t.code===`ENOENT`)return{version:1,updatedAt:new Date().toISOString(),entries:[]};if(t instanceof SyntaxError){let e=`${this.registryPath}.corrupt.${Date.now()}`;await d.default.rename(this.registryPath,e).catch(()=>void 0)}throw new w(`Failed to read registry file: ${e instanceof Error?e.message:String(e)}`,`REGISTRY_READ_FAILED`,{cause:e})}}async saveState(e){await d.default.mkdir(c.default.dirname(this.registryPath),{recursive:!0});let t={...e,updatedAt:new Date().toISOString(),entries:[...e.entries]},n=j(this.registryPath);await d.default.writeFile(n,JSON.stringify(t,null,2),`utf-8`),await d.default.rename(n,this.registryPath)}async pruneState(e){let t=await this.pruneMissingRepositories(e);return this.pruneDeadProcesses(t)}async pruneMissingRepositories(e){let t=(await Promise.all(e.entries.map(async e=>({entry:e,exists:await this.pathExists(e.repositoryPath)})))).filter(e=>e.exists).map(e=>e.entry);return t.length!==e.entries.length&&(e.entries=t,await this.saveState(e)),e}async pruneDeadProcesses(e){let t=[],n=!1;for(let r of e.entries){if(this.isProcessRunning(r.pid)){t.push(r);continue}n=!0,await this.releaseAssociatedPort(r)}return n&&(e.entries=t,await this.saveState(e)),e}async acquireLock(e){await d.default.mkdir(c.default.dirname(this.lockPath),{recursive:!0});for(let t=0;t<80;t+=1)try{let t={pid:process.pid,token:e,createdAt:new Date().toISOString()};await d.default.writeFile(this.lockPath,JSON.stringify(t),{flag:`wx`}),this.registerLockCleanup(e);return}catch(e){if(e.code!==`EEXIST`)throw new w(`Failed to acquire registry lock: ${e instanceof Error?e.message:String(e)}`,`REGISTRY_LOCK_FAILED`,{cause:e});if(await this.isStaleLock()){await d.default.unlink(this.lockPath).catch(()=>void 0),--t;continue}await this.delay(75)}throw new w(`Unable to acquire registry lock (timeout)`,`REGISTRY_LOCK_FAILED`)}async releaseLock(e){try{let t=await d.default.readFile(this.lockPath,`utf-8`);JSON.parse(t).token===e&&await d.default.unlink(this.lockPath)}catch{}finally{this.unregisterLockCleanup(e)}}async isStaleLock(){try{let e=await d.default.readFile(this.lockPath,`utf-8`),t=JSON.parse(e);if(t.pid&&this.isProcessRunning(t.pid)){let e=Date.now()-new Date(t.createdAt).getTime();return!(Number.isFinite(e)&&e<k)}return!0}catch{return!0}}isProcessRunning(e){try{return process.kill(e,0),!0}catch{return!1}}registerLockCleanup(e){this.unregisterLockCleanup(this.activeLockToken),this.activeLockToken=e;let t=()=>{this.releaseLockSync(e)};for(let e of[`exit`,`SIGINT`,`SIGTERM`,`uncaughtException`,`unhandledRejection`])process.once(e,t),this.lockCleanupHandlers.set(e,t)}unregisterLockCleanup(e){if(!(!e||this.activeLockToken!==e)){for(let[e,t]of this.lockCleanupHandlers)process.off(e,t);this.lockCleanupHandlers.clear(),this.activeLockToken=void 0}}releaseLockSync(e){try{let t=u.default.readFileSync(this.lockPath,`utf-8`);JSON.parse(t).token===e&&u.default.unlinkSync(this.lockPath)}catch{}}async terminateProcess(e){try{process.kill(e,0)}catch{return}try{process.kill(e,`SIGTERM`)}catch(t){if(t.code===`ESRCH`)return;throw Error(`Failed to send SIGTERM to process ${e}: ${t instanceof Error?t.message:String(t)}`,{cause:t})}if(await this.delay(500),this.isProcessRunning(e)){try{process.kill(e,`SIGKILL`)}catch(t){if(t.code===`ESRCH`)return;throw Error(`Failed to send SIGKILL to process ${e}: ${t instanceof Error?t.message:String(t)}`,{cause:t})}if(await this.delay(250),this.isProcessRunning(e))throw Error(`Process ${e} did not exit after SIGKILL`)}}async releaseAssociatedPort(e){if(!(!this.portRegistry||!e.port))try{let t=await this.portRegistry.releasePort({repositoryPath:e.repositoryPath,serviceName:e.serviceName,serviceType:e.serviceType,environment:e.environment,pid:e.pid,force:!0});if(!t.success&&t.error&&!t.error.includes(`No matching registry entry`))throw Error(t.error)}catch{}}entryKey(e){return[e.repositoryPath,e.serviceName,e.serviceType,e.environment??``,String(e.pid)].join(`|`)}findEntry(e,t){return e.entries.find(e=>e.repositoryPath===t.repositoryPath&&e.serviceName===t.serviceName&&e.serviceType===t.serviceType&&(t.environment?e.environment===t.environment:!0))}removeMatchingEntries(e,t){e.entries=e.entries.filter(e=>!(e.repositoryPath===t.repositoryPath&&e.serviceName===t.serviceName&&e.serviceType===t.serviceType&&(!t.environment||e.environment===t.environment)))}createSuccessResponse(e,t){return C.parse({success:!0,...typeof e==`number`?{pid:e}:{},...t?{record:t}:{}})}createFailureResponse(e){return C.parse({success:!1,error:e})}async pathExists(e){try{return await d.default.access(e),!0}catch{return!1}}delay(e){return new Promise(t=>setTimeout(t,e))}};Object.defineProperty(exports,`_`,{enumerable:!0,get:function(){return b}}),Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return 75}}),Object.defineProperty(exports,`b`,{enumerable:!0,get:function(){return s}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return A}}),Object.defineProperty(exports,`d`,{enumerable:!0,get:function(){return _}}),Object.defineProperty(exports,`f`,{enumerable:!0,get:function(){return w}}),Object.defineProperty(exports,`g`,{enumerable:!0,get:function(){return 1}}),Object.defineProperty(exports,`h`,{enumerable:!0,get:function(){return y}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return 80}}),Object.defineProperty(exports,`l`,{enumerable:!0,get:function(){return M}}),Object.defineProperty(exports,`m`,{enumerable:!0,get:function(){return C}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return E}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return k}}),Object.defineProperty(exports,`p`,{enumerable:!0,get:function(){return v}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return T}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return j}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return N}}),Object.defineProperty(exports,`u`,{enumerable:!0,get:function(){return S}}),Object.defineProperty(exports,`v`,{enumerable:!0,get:function(){return x}}),Object.defineProperty(exports,`y`,{enumerable:!0,get:function(){return g}});
|
|
2
2
|
//# sourceMappingURL=ProcessRegistryService.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProcessRegistryService.cjs","names":["z","path","os","portRegistry: PortRegistryCleanup","PortRegistryService","path","record: ProcessRegistryRecord","errors: string[]","fs","payload: ProcessRegistryState","aliveEntries: ProcessRegistryRecord[]","lockState: LockState","fsSync"],"sources":["../src/types/index.ts","../src/utils/index.ts","../src/services/ProcessRegistryService.ts"],"sourcesContent":["import { z } from 'zod';\n\nexport const REGISTRY_VERSION = 1 as const;\n\nexport const ServiceCategorySchema = z.enum(['service', 'tool']);\n\nexport const ProcessMetadataSchema = z.record(z.string(), z.unknown());\n\nexport const ProcessRegistryRecordSchema = z.object({\n repositoryPath: z.string().trim().min(1, 'repositoryPath is required'),\n serviceName: z.string().trim().min(1, 'serviceName is required'),\n serviceType: ServiceCategorySchema,\n environment: z.string().trim().min(1).optional(),\n pid: z.number().int().min(1),\n port: z.number().int().min(1).max(65535).optional(),\n host: z.string().trim().min(1).optional(),\n command: z.string().trim().min(1).optional(),\n args: z.array(z.string()).optional(),\n metadata: ProcessMetadataSchema.optional(),\n createdAt: z.string().trim().min(1),\n updatedAt: z.string().trim().min(1),\n});\n\nexport const ProcessRegistryStateSchema = z.object({\n version: z.literal(REGISTRY_VERSION),\n updatedAt: z.string().trim().min(1),\n entries: z.array(ProcessRegistryRecordSchema),\n});\n\nexport const RegisterProcessRequestSchema = z.object({\n repositoryPath: z.string().trim().min(1),\n serviceName: z.string().trim().min(1),\n serviceType: ServiceCategorySchema.default('service'),\n environment: z.string().trim().min(1).optional(),\n pid: z.number().int().min(1),\n port: z.number().int().min(1).max(65535).optional(),\n host: z.string().trim().min(1).optional(),\n command: z.string().trim().min(1).optional(),\n args: z.array(z.string()).optional(),\n metadata: ProcessMetadataSchema.optional(),\n force: z.boolean().optional(),\n});\n\nexport const ReleaseProcessRequestSchema = z.object({\n repositoryPath: z.string().trim().min(1),\n serviceName: z.string().trim().min(1),\n serviceType: ServiceCategorySchema.optional(),\n pid: z.number().int().min(1).optional(),\n environment: z.string().trim().min(1).optional(),\n force: z.boolean().optional(),\n kill: z.boolean().optional().default(true),\n releasePort: z.boolean().optional().default(true),\n});\n\nexport const ListProcessFiltersSchema = z.object({\n repositoryPath: z.string().trim().min(1).optional(),\n serviceType: ServiceCategorySchema.optional(),\n serviceName: z.string().trim().min(1).optional(),\n environment: z.string().trim().min(1).optional(),\n});\n\nexport const ProcessRegistryResponseSchema = z.object({\n success: z.boolean(),\n pid: z.number().int().min(1).optional(),\n record: ProcessRegistryRecordSchema.optional(),\n error: z.string().optional(),\n});\n\nexport type ServiceCategory = z.infer<typeof ServiceCategorySchema>;\nexport type ProcessMetadata = z.infer<typeof ProcessMetadataSchema>;\nexport type ProcessRegistryRecord = z.infer<typeof ProcessRegistryRecordSchema>;\nexport type ProcessRegistryState = z.infer<typeof ProcessRegistryStateSchema>;\nexport type RegisterProcessRequest = z.infer<typeof RegisterProcessRequestSchema>;\nexport type ReleaseProcessRequest = z.infer<typeof ReleaseProcessRequestSchema>;\nexport type ListProcessFilters = z.infer<typeof ListProcessFiltersSchema>;\nexport type ProcessRegistryResponse = z.infer<typeof ProcessRegistryResponseSchema>;\n\nexport type ProcessRegistryErrorCode =\n | 'INVALID_REQUEST'\n | 'REGISTRY_READ_FAILED'\n | 'REGISTRY_WRITE_FAILED'\n | 'REGISTRY_LOCK_FAILED'\n | 'NO_PROCESS_FOUND';\n\nexport class ProcessRegistryError extends Error {\n readonly code: ProcessRegistryErrorCode;\n\n constructor(message: string, code: ProcessRegistryErrorCode, options?: ErrorOptions) {\n super(message, options);\n this.name = 'ProcessRegistryError';\n this.code = code;\n }\n}\n","import { randomBytes } from 'node:crypto';\nimport os from 'node:os';\nimport path from 'node:path';\n\nexport const DEFAULT_REGISTRY_PATH = path.join(os.homedir(), '.process-registry', 'processes.json');\nexport const DEFAULT_REGISTRY_LOCK_PATH = `${DEFAULT_REGISTRY_PATH}.lock`;\nexport const LOCK_RETRY_DELAY_MS = 75;\nexport const LOCK_MAX_RETRIES = 80;\nexport const LOCK_STALE_AFTER_MS = 5_000;\n\nexport const normalizeRepositoryPath = (value: string): string => path.resolve(value);\n\nexport const makeTempPath = (filePath: string): string => {\n const random = randomBytes(6).toString('hex');\n return `${filePath}.${random}.tmp`;\n};\n","import { randomBytes } from 'node:crypto';\nimport fsSync from 'node:fs';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport {\n type ListProcessFilters,\n ListProcessFiltersSchema,\n type ProcessRegistryRecord,\n ProcessRegistryError,\n type ProcessRegistryResponse,\n ProcessRegistryResponseSchema,\n type ProcessRegistryState,\n ProcessRegistryStateSchema,\n REGISTRY_VERSION,\n type RegisterProcessRequest,\n RegisterProcessRequestSchema,\n type ReleaseProcessRequest,\n ReleaseProcessRequestSchema,\n} from '../types';\nimport {\n DEFAULT_REGISTRY_PATH,\n LOCK_MAX_RETRIES,\n LOCK_RETRY_DELAY_MS,\n LOCK_STALE_AFTER_MS,\n makeTempPath,\n normalizeRepositoryPath,\n} from '../utils';\nimport { PortRegistryService } from '@agimon-ai/foundation-port-registry';\n\ninterface LockState {\n pid: number;\n token: string;\n createdAt: string;\n}\n\ninterface NormalizedFilters {\n repositoryPath: string;\n serviceName: string;\n serviceType: 'service' | 'tool';\n environment?: string;\n}\n\ntype LockCleanupEvent = 'exit' | 'SIGINT' | 'SIGTERM' | 'uncaughtException' | 'unhandledRejection';\n\ntype PortRegistryCleanup = Pick<PortRegistryService, 'releasePort'>;\n\nexport class ProcessRegistryService {\n private readonly registryPath: string;\n private readonly lockPath: string;\n private activeLockToken?: string;\n private readonly lockCleanupHandlers = new Map<LockCleanupEvent, (...args: unknown[]) => void>();\n\n constructor(\n registryPath: string = DEFAULT_REGISTRY_PATH,\n lockPath?: string,\n private readonly portRegistry: PortRegistryCleanup = new PortRegistryService(process.env.PORT_REGISTRY_PATH),\n ) {\n this.registryPath = ProcessRegistryService.resolveRegistryPath(registryPath);\n this.lockPath = lockPath ?? `${this.registryPath}.lock`;\n }\n\n static resolveRegistryPath(inputPath?: string): string {\n if (!inputPath) {\n return DEFAULT_REGISTRY_PATH;\n }\n\n const resolvedPath = path.isAbsolute(inputPath) ? inputPath : path.join(process.cwd(), inputPath);\n if (path.extname(resolvedPath) === '.json') {\n return resolvedPath;\n }\n\n return path.join(resolvedPath, 'processes.json');\n }\n\n async registerProcess(rawRequest: RegisterProcessRequest): Promise<ProcessRegistryResponse> {\n const request = RegisterProcessRequestSchema.parse(rawRequest);\n\n return this.withLock(async () => {\n const state = await this.loadState();\n const normalizedRepo = normalizeRepositoryPath(request.repositoryPath);\n const serviceType = request.serviceType ?? 'service';\n const environment = request.environment ?? process.env.NODE_ENV ?? 'development';\n const registry = await this.pruneState(state);\n const existing = this.findEntry(registry, {\n repositoryPath: normalizedRepo,\n serviceName: request.serviceName,\n serviceType,\n environment,\n });\n\n if (existing) {\n if (existing.pid === request.pid) {\n existing.updatedAt = new Date().toISOString();\n existing.port = request.port ?? existing.port;\n existing.host = request.host ?? existing.host;\n existing.command = request.command ?? existing.command;\n existing.args = request.args ?? existing.args;\n existing.metadata = request.metadata ?? existing.metadata;\n await this.saveState(registry);\n return this.createSuccessResponse(request.pid, existing);\n }\n\n const isAlive = this.isProcessRunning(existing.pid);\n if (isAlive && !request.force) {\n return this.createFailureResponse(`Process already registered for ${request.serviceName} in requested scope`);\n }\n\n if (isAlive) {\n await this.terminateProcess(existing.pid);\n }\n\n await this.releaseAssociatedPort(existing);\n this.removeMatchingEntries(registry, {\n repositoryPath: normalizedRepo,\n serviceName: request.serviceName,\n serviceType,\n environment,\n });\n }\n\n const now = new Date().toISOString();\n const record: ProcessRegistryRecord = {\n repositoryPath: normalizedRepo,\n serviceName: request.serviceName,\n serviceType,\n environment,\n pid: request.pid,\n port: request.port,\n host: request.host,\n command: request.command,\n args: request.args,\n metadata: request.metadata,\n createdAt: now,\n updatedAt: now,\n };\n\n registry.entries.push(record);\n await this.saveState(registry);\n return this.createSuccessResponse(request.pid, record);\n });\n }\n\n async releaseProcess(rawRequest: ReleaseProcessRequest): Promise<ProcessRegistryResponse> {\n const request = ReleaseProcessRequestSchema.parse(rawRequest);\n\n return this.withLock(async () => {\n const state = await this.loadState();\n const registry = await this.pruneMissingRepositories(state);\n const normalizedRepo = normalizeRepositoryPath(request.repositoryPath);\n const serviceType = request.serviceType ?? 'service';\n const environment = request.environment ?? process.env.NODE_ENV ?? 'development';\n const matches = registry.entries.filter((entry) => {\n if (entry.repositoryPath !== normalizedRepo) return false;\n if (entry.serviceName !== request.serviceName) return false;\n if (entry.serviceType !== serviceType) return false;\n if (request.environment && entry.environment !== environment) return false;\n if (typeof request.pid === 'number' && entry.pid !== request.pid) return false;\n return true;\n });\n\n if (matches.length === 0) {\n return this.createFailureResponse(`No matching process entry for ${request.serviceName}`);\n }\n\n const errors: string[] = [];\n const removable = new Set<string>();\n\n for (const entry of matches) {\n const entryKey = this.entryKey(entry);\n\n try {\n if (request.kill ?? true) {\n if (this.isProcessRunning(entry.pid)) {\n await this.terminateProcess(entry.pid);\n }\n }\n\n if (request.releasePort ?? true) {\n await this.releaseAssociatedPort(entry);\n }\n\n removable.add(entryKey);\n } catch (error) {\n errors.push(\n `${entry.serviceName} (pid ${entry.pid}): ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n if (removable.size > 0) {\n registry.entries = registry.entries.filter((entry) => !removable.has(this.entryKey(entry)));\n await this.saveState(registry);\n }\n\n if (errors.length > 0) {\n return this.createFailureResponse(errors.join('; '));\n }\n\n return this.createSuccessResponse();\n });\n }\n\n async listProcesses(filters: ListProcessFilters = {}): Promise<ProcessRegistryRecord[]> {\n const request = ListProcessFiltersSchema.parse(filters);\n\n return this.withLock(async () => {\n const state = await this.loadState();\n const registry = await this.pruneState(state);\n\n return [...registry.entries].filter((entry) => {\n if (request.repositoryPath && entry.repositoryPath !== normalizeRepositoryPath(request.repositoryPath)) {\n return false;\n }\n if (request.serviceType && entry.serviceType !== request.serviceType) return false;\n if (request.serviceName && entry.serviceName !== request.serviceName) return false;\n if (request.environment && entry.environment !== request.environment) return false;\n return true;\n });\n });\n }\n\n private async withLock<T>(callback: () => Promise<T>): Promise<T> {\n const lockToken = `${process.pid}-${randomBytes(6).toString('hex')}`;\n await this.acquireLock(lockToken);\n\n try {\n return await callback();\n } finally {\n await this.releaseLock(lockToken);\n }\n }\n\n private async loadState(): Promise<ProcessRegistryState> {\n await fs.mkdir(path.dirname(this.registryPath), { recursive: true });\n\n try {\n const content = await fs.readFile(this.registryPath, 'utf-8');\n const parsed = JSON.parse(content);\n return ProcessRegistryStateSchema.parse(parsed);\n } catch (error) {\n const sysError = error as NodeJS.ErrnoException;\n if (sysError.code === 'ENOENT') {\n return {\n version: REGISTRY_VERSION,\n updatedAt: new Date().toISOString(),\n entries: [],\n };\n }\n\n if (sysError instanceof SyntaxError) {\n const backupPath = `${this.registryPath}.corrupt.${Date.now()}`;\n await fs.rename(this.registryPath, backupPath).catch(() => undefined);\n }\n\n throw new ProcessRegistryError(\n `Failed to read registry file: ${error instanceof Error ? error.message : String(error)}`,\n 'REGISTRY_READ_FAILED',\n { cause: error },\n );\n }\n }\n\n private async saveState(state: ProcessRegistryState): Promise<void> {\n await fs.mkdir(path.dirname(this.registryPath), { recursive: true });\n\n const payload: ProcessRegistryState = {\n ...state,\n updatedAt: new Date().toISOString(),\n entries: [...state.entries],\n };\n\n const tempPath = makeTempPath(this.registryPath);\n await fs.writeFile(tempPath, JSON.stringify(payload, null, 2), 'utf-8');\n await fs.rename(tempPath, this.registryPath);\n }\n\n private async pruneState(state: ProcessRegistryState): Promise<ProcessRegistryState> {\n const repositoryPruned = await this.pruneMissingRepositories(state);\n return this.pruneDeadProcesses(repositoryPruned);\n }\n\n private async pruneMissingRepositories(state: ProcessRegistryState): Promise<ProcessRegistryState> {\n const entries = await Promise.all(\n state.entries.map(async (entry) => ({\n entry,\n exists: await this.pathExists(entry.repositoryPath),\n })),\n );\n\n const pruned = entries.filter((value) => value.exists).map((value) => value.entry);\n if (pruned.length !== state.entries.length) {\n state.entries = pruned;\n await this.saveState(state);\n }\n\n return state;\n }\n\n private async pruneDeadProcesses(state: ProcessRegistryState): Promise<ProcessRegistryState> {\n const aliveEntries: ProcessRegistryRecord[] = [];\n let changed = false;\n\n for (const entry of state.entries) {\n if (this.isProcessRunning(entry.pid)) {\n aliveEntries.push(entry);\n continue;\n }\n\n changed = true;\n await this.releaseAssociatedPort(entry);\n }\n\n if (changed) {\n state.entries = aliveEntries;\n await this.saveState(state);\n }\n\n return state;\n }\n\n private async acquireLock(token: string): Promise<void> {\n await fs.mkdir(path.dirname(this.lockPath), { recursive: true });\n\n for (let attempt = 0; attempt < LOCK_MAX_RETRIES; attempt += 1) {\n try {\n const lockState: LockState = {\n pid: process.pid,\n token,\n createdAt: new Date().toISOString(),\n };\n await fs.writeFile(this.lockPath, JSON.stringify(lockState), { flag: 'wx' });\n this.registerLockCleanup(token);\n return;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') {\n throw new ProcessRegistryError(\n `Failed to acquire registry lock: ${error instanceof Error ? error.message : String(error)}`,\n 'REGISTRY_LOCK_FAILED',\n { cause: error },\n );\n }\n\n const stale = await this.isStaleLock();\n if (stale) {\n await fs.unlink(this.lockPath).catch(() => undefined);\n attempt -= 1;\n continue;\n }\n\n await this.delay(LOCK_RETRY_DELAY_MS);\n }\n }\n\n throw new ProcessRegistryError('Unable to acquire registry lock (timeout)', 'REGISTRY_LOCK_FAILED');\n }\n\n private async releaseLock(token: string): Promise<void> {\n try {\n const existing = await fs.readFile(this.lockPath, 'utf-8');\n const parsed = JSON.parse(existing) as { token: string };\n if (parsed.token === token) {\n await fs.unlink(this.lockPath);\n }\n } catch {\n // ignore\n } finally {\n this.unregisterLockCleanup(token);\n }\n }\n\n private async isStaleLock(): Promise<boolean> {\n try {\n const content = await fs.readFile(this.lockPath, 'utf-8');\n const parsed = JSON.parse(content) as LockState;\n\n if (parsed.pid) {\n const pidAlive = this.isProcessRunning(parsed.pid);\n if (pidAlive) {\n const age = Date.now() - new Date(parsed.createdAt).getTime();\n return !(Number.isFinite(age) && age < LOCK_STALE_AFTER_MS);\n }\n }\n\n return true;\n } catch {\n return true;\n }\n }\n\n private isProcessRunning(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n }\n\n private registerLockCleanup(token: string): void {\n this.unregisterLockCleanup(this.activeLockToken);\n this.activeLockToken = token;\n\n const cleanup = (): void => {\n this.releaseLockSync(token);\n };\n\n for (const event of [\n 'exit',\n 'SIGINT',\n 'SIGTERM',\n 'uncaughtException',\n 'unhandledRejection',\n ] satisfies LockCleanupEvent[]) {\n process.once(event, cleanup);\n this.lockCleanupHandlers.set(event, cleanup);\n }\n }\n\n private unregisterLockCleanup(token: string | undefined): void {\n if (!token || this.activeLockToken !== token) {\n return;\n }\n\n for (const [event, handler] of this.lockCleanupHandlers) {\n process.off(event, handler);\n }\n\n this.lockCleanupHandlers.clear();\n this.activeLockToken = undefined;\n }\n\n private releaseLockSync(token: string): void {\n try {\n const existing = fsSync.readFileSync(this.lockPath, 'utf-8');\n const parsed = JSON.parse(existing) as { token?: string };\n if (parsed.token === token) {\n fsSync.unlinkSync(this.lockPath);\n }\n } catch {\n // ignore best-effort cleanup\n }\n }\n\n private async terminateProcess(pid: number): Promise<void> {\n try {\n process.kill(pid, 0);\n } catch {\n return;\n }\n\n try {\n process.kill(pid, 'SIGTERM');\n } catch (error) {\n const sysError = error as NodeJS.ErrnoException;\n if (sysError.code === 'ESRCH') {\n return;\n }\n\n throw new Error(\n `Failed to send SIGTERM to process ${pid}: ${error instanceof Error ? error.message : String(error)}`,\n {\n cause: error,\n },\n );\n }\n\n await this.delay(500);\n\n if (!this.isProcessRunning(pid)) {\n return;\n }\n\n try {\n process.kill(pid, 'SIGKILL');\n } catch (error) {\n const sysError = error as NodeJS.ErrnoException;\n if (sysError.code === 'ESRCH') {\n return;\n }\n\n throw new Error(\n `Failed to send SIGKILL to process ${pid}: ${error instanceof Error ? error.message : String(error)}`,\n {\n cause: error,\n },\n );\n }\n\n await this.delay(250);\n\n if (this.isProcessRunning(pid)) {\n throw new Error(`Process ${pid} did not exit after SIGKILL`);\n }\n }\n\n private async releaseAssociatedPort(entry: ProcessRegistryRecord): Promise<void> {\n if (!this.portRegistry || !entry.port) {\n return;\n }\n\n try {\n const result = await this.portRegistry.releasePort({\n repositoryPath: entry.repositoryPath,\n serviceName: entry.serviceName,\n serviceType: entry.serviceType,\n environment: entry.environment,\n pid: entry.pid,\n force: true,\n });\n\n if (!result.success && result.error && !result.error.includes('No matching registry entry')) {\n throw new Error(result.error);\n }\n } catch {\n // Best-effort cleanup: port release failures should not block process cleanup.\n }\n }\n\n private entryKey(entry: ProcessRegistryRecord): string {\n return [\n entry.repositoryPath,\n entry.serviceName,\n entry.serviceType,\n entry.environment ?? '',\n String(entry.pid),\n ].join('|');\n }\n\n private findEntry(state: ProcessRegistryState, filters: NormalizedFilters): ProcessRegistryRecord | undefined {\n return state.entries.find(\n (entry) =>\n entry.repositoryPath === filters.repositoryPath &&\n entry.serviceName === filters.serviceName &&\n entry.serviceType === filters.serviceType &&\n (filters.environment ? entry.environment === filters.environment : true),\n );\n }\n\n private removeMatchingEntries(state: ProcessRegistryState, filters: NormalizedFilters): void {\n state.entries = state.entries.filter(\n (entry) =>\n !(\n entry.repositoryPath === filters.repositoryPath &&\n entry.serviceName === filters.serviceName &&\n entry.serviceType === filters.serviceType &&\n (!filters.environment || entry.environment === filters.environment)\n ),\n );\n }\n\n private createSuccessResponse(pid?: number, record?: ProcessRegistryRecord): ProcessRegistryResponse {\n return ProcessRegistryResponseSchema.parse({\n success: true,\n ...(typeof pid === 'number' ? { pid } : {}),\n ...(record ? { record } : {}),\n });\n }\n\n private createFailureResponse(error: string): ProcessRegistryResponse {\n return ProcessRegistryResponseSchema.parse({\n success: false,\n error,\n });\n }\n\n private async pathExists(candidate: string): Promise<boolean> {\n try {\n await fs.access(candidate);\n return true;\n } catch {\n return false;\n }\n }\n\n private delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ProcessRegistryService.cjs","names":["z","path","os","portRegistry: PortRegistryCleanup","PortRegistryService","path","record: ProcessRegistryRecord","errors: string[]","fs","payload: ProcessRegistryState","aliveEntries: ProcessRegistryRecord[]","lockState: LockState","fsSync"],"sources":["../src/types/index.ts","../src/utils/index.ts","../src/services/ProcessRegistryService.ts"],"sourcesContent":["import { z } from 'zod';\n\nexport const REGISTRY_VERSION = 1 as const;\n\nexport const ServiceCategorySchema = z.enum(['service', 'tool']);\n\nexport const ProcessMetadataSchema = z.record(z.string(), z.unknown());\n\nexport const ProcessRegistryRecordSchema = z.object({\n repositoryPath: z.string().trim().min(1, 'repositoryPath is required'),\n serviceName: z.string().trim().min(1, 'serviceName is required'),\n serviceType: ServiceCategorySchema,\n environment: z.string().trim().min(1).optional(),\n pid: z.number().int().min(1),\n port: z.number().int().min(1).max(65535).optional(),\n host: z.string().trim().min(1).optional(),\n command: z.string().trim().min(1).optional(),\n args: z.array(z.string()).optional(),\n metadata: ProcessMetadataSchema.optional(),\n createdAt: z.string().trim().min(1),\n updatedAt: z.string().trim().min(1),\n});\n\nexport const ProcessRegistryStateSchema = z.object({\n version: z.literal(REGISTRY_VERSION),\n updatedAt: z.string().trim().min(1),\n entries: z.array(ProcessRegistryRecordSchema),\n});\n\nexport const RegisterProcessRequestSchema = z.object({\n repositoryPath: z.string().trim().min(1),\n serviceName: z.string().trim().min(1),\n serviceType: ServiceCategorySchema.default('service'),\n environment: z.string().trim().min(1).optional(),\n pid: z.number().int().min(1),\n port: z.number().int().min(1).max(65535).optional(),\n host: z.string().trim().min(1).optional(),\n command: z.string().trim().min(1).optional(),\n args: z.array(z.string()).optional(),\n metadata: ProcessMetadataSchema.optional(),\n force: z.boolean().optional(),\n});\n\nexport const ReleaseProcessRequestSchema = z.object({\n repositoryPath: z.string().trim().min(1),\n serviceName: z.string().trim().min(1),\n serviceType: ServiceCategorySchema.optional(),\n pid: z.number().int().min(1).optional(),\n environment: z.string().trim().min(1).optional(),\n force: z.boolean().optional(),\n kill: z.boolean().optional().default(true),\n releasePort: z.boolean().optional().default(true),\n});\n\nexport const ListProcessFiltersSchema = z.object({\n repositoryPath: z.string().trim().min(1).optional(),\n serviceType: ServiceCategorySchema.optional(),\n serviceName: z.string().trim().min(1).optional(),\n environment: z.string().trim().min(1).optional(),\n});\n\nexport const ProcessRegistryResponseSchema = z.object({\n success: z.boolean(),\n pid: z.number().int().min(1).optional(),\n record: ProcessRegistryRecordSchema.optional(),\n error: z.string().optional(),\n});\n\nexport type ServiceCategory = z.infer<typeof ServiceCategorySchema>;\nexport type ProcessMetadata = z.infer<typeof ProcessMetadataSchema>;\nexport type ProcessRegistryRecord = z.infer<typeof ProcessRegistryRecordSchema>;\nexport type ProcessRegistryState = z.infer<typeof ProcessRegistryStateSchema>;\nexport type RegisterProcessRequest = z.infer<typeof RegisterProcessRequestSchema>;\nexport type ReleaseProcessRequest = z.infer<typeof ReleaseProcessRequestSchema>;\nexport type ListProcessFilters = z.infer<typeof ListProcessFiltersSchema>;\nexport type ProcessRegistryResponse = z.infer<typeof ProcessRegistryResponseSchema>;\n\nexport type ProcessRegistryErrorCode =\n | 'INVALID_REQUEST'\n | 'REGISTRY_READ_FAILED'\n | 'REGISTRY_WRITE_FAILED'\n | 'REGISTRY_LOCK_FAILED'\n | 'NO_PROCESS_FOUND';\n\nexport class ProcessRegistryError extends Error {\n readonly code: ProcessRegistryErrorCode;\n\n constructor(message: string, code: ProcessRegistryErrorCode, options?: ErrorOptions) {\n super(message, options);\n this.name = 'ProcessRegistryError';\n this.code = code;\n }\n}\n","import { randomBytes } from 'node:crypto';\nimport os from 'node:os';\nimport path from 'node:path';\n\nexport const DEFAULT_REGISTRY_PATH = path.join(os.homedir(), '.process-registry', 'processes.json');\nexport const DEFAULT_REGISTRY_LOCK_PATH = `${DEFAULT_REGISTRY_PATH}.lock`;\nexport const LOCK_RETRY_DELAY_MS = 75;\nexport const LOCK_MAX_RETRIES = 80;\nexport const LOCK_STALE_AFTER_MS = 5_000;\n\nexport const normalizeRepositoryPath = (value: string): string => path.resolve(value);\n\nexport const makeTempPath = (filePath: string): string => {\n const random = randomBytes(6).toString('hex');\n return `${filePath}.${random}.tmp`;\n};\n\nexport function resolveSiblingRegistryPath(registryPath: string | undefined, fileName: string): string | undefined {\n if (!registryPath) {\n return undefined;\n }\n\n const resolved = path.resolve(registryPath);\n if (path.extname(resolved) === '.json') {\n return path.join(path.dirname(resolved), fileName);\n }\n\n return path.join(resolved, fileName);\n}\n","import { randomBytes } from 'node:crypto';\nimport fsSync from 'node:fs';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { PortRegistryService } from '@agimon-ai/foundation-port-registry';\nimport {\n type ListProcessFilters,\n ListProcessFiltersSchema,\n ProcessRegistryError,\n type ProcessRegistryRecord,\n type ProcessRegistryResponse,\n ProcessRegistryResponseSchema,\n type ProcessRegistryState,\n ProcessRegistryStateSchema,\n REGISTRY_VERSION,\n type RegisterProcessRequest,\n RegisterProcessRequestSchema,\n type ReleaseProcessRequest,\n ReleaseProcessRequestSchema,\n} from '../types';\nimport {\n DEFAULT_REGISTRY_PATH,\n LOCK_MAX_RETRIES,\n LOCK_RETRY_DELAY_MS,\n LOCK_STALE_AFTER_MS,\n makeTempPath,\n normalizeRepositoryPath,\n} from '../utils';\n\ninterface LockState {\n pid: number;\n token: string;\n createdAt: string;\n}\n\ninterface NormalizedFilters {\n repositoryPath: string;\n serviceName: string;\n serviceType: 'service' | 'tool';\n environment?: string;\n}\n\ntype LockCleanupEvent = 'exit' | 'SIGINT' | 'SIGTERM' | 'uncaughtException' | 'unhandledRejection';\n\ntype PortRegistryCleanup = Pick<PortRegistryService, 'releasePort'>;\n\nexport class ProcessRegistryService {\n private readonly registryPath: string;\n private readonly lockPath: string;\n private activeLockToken?: string;\n private readonly lockCleanupHandlers = new Map<LockCleanupEvent, (...args: unknown[]) => void>();\n\n constructor(\n registryPath: string = DEFAULT_REGISTRY_PATH,\n lockPath?: string,\n private readonly portRegistry: PortRegistryCleanup = new PortRegistryService(process.env.PORT_REGISTRY_PATH),\n ) {\n this.registryPath = ProcessRegistryService.resolveRegistryPath(registryPath);\n this.lockPath = lockPath ?? `${this.registryPath}.lock`;\n }\n\n static resolveRegistryPath(inputPath?: string): string {\n if (!inputPath) {\n return DEFAULT_REGISTRY_PATH;\n }\n\n const resolvedPath = path.isAbsolute(inputPath) ? inputPath : path.join(process.cwd(), inputPath);\n if (path.extname(resolvedPath) === '.json') {\n return resolvedPath;\n }\n\n return path.join(resolvedPath, 'processes.json');\n }\n\n async registerProcess(rawRequest: RegisterProcessRequest): Promise<ProcessRegistryResponse> {\n const request = RegisterProcessRequestSchema.parse(rawRequest);\n\n return this.withLock(async () => {\n const state = await this.loadState();\n const normalizedRepo = normalizeRepositoryPath(request.repositoryPath);\n const serviceType = request.serviceType ?? 'service';\n const environment = request.environment ?? process.env.NODE_ENV ?? 'development';\n const registry = await this.pruneState(state);\n const existing = this.findEntry(registry, {\n repositoryPath: normalizedRepo,\n serviceName: request.serviceName,\n serviceType,\n environment,\n });\n\n if (existing) {\n if (existing.pid === request.pid) {\n existing.updatedAt = new Date().toISOString();\n existing.port = request.port ?? existing.port;\n existing.host = request.host ?? existing.host;\n existing.command = request.command ?? existing.command;\n existing.args = request.args ?? existing.args;\n existing.metadata = request.metadata ?? existing.metadata;\n await this.saveState(registry);\n return this.createSuccessResponse(request.pid, existing);\n }\n\n const isAlive = this.isProcessRunning(existing.pid);\n if (isAlive && !(request.force ?? true)) {\n return this.createFailureResponse(`Process already registered for ${request.serviceName} in requested scope`);\n }\n\n if (isAlive) {\n await this.terminateProcess(existing.pid);\n }\n\n await this.releaseAssociatedPort(existing);\n this.removeMatchingEntries(registry, {\n repositoryPath: normalizedRepo,\n serviceName: request.serviceName,\n serviceType,\n environment,\n });\n }\n\n const now = new Date().toISOString();\n const record: ProcessRegistryRecord = {\n repositoryPath: normalizedRepo,\n serviceName: request.serviceName,\n serviceType,\n environment,\n pid: request.pid,\n port: request.port,\n host: request.host,\n command: request.command,\n args: request.args,\n metadata: request.metadata,\n createdAt: now,\n updatedAt: now,\n };\n\n registry.entries.push(record);\n await this.saveState(registry);\n return this.createSuccessResponse(request.pid, record);\n });\n }\n\n async releaseProcess(rawRequest: ReleaseProcessRequest): Promise<ProcessRegistryResponse> {\n const request = ReleaseProcessRequestSchema.parse(rawRequest);\n\n return this.withLock(async () => {\n const state = await this.loadState();\n const registry = await this.pruneMissingRepositories(state);\n const normalizedRepo = normalizeRepositoryPath(request.repositoryPath);\n const serviceType = request.serviceType ?? 'service';\n const environment = request.environment ?? process.env.NODE_ENV ?? 'development';\n const matches = registry.entries.filter((entry) => {\n if (entry.repositoryPath !== normalizedRepo) return false;\n if (entry.serviceName !== request.serviceName) return false;\n if (entry.serviceType !== serviceType) return false;\n if (request.environment && entry.environment !== environment) return false;\n if (typeof request.pid === 'number' && entry.pid !== request.pid) return false;\n return true;\n });\n\n if (matches.length === 0) {\n return this.createFailureResponse(`No matching process entry for ${request.serviceName}`);\n }\n\n const errors: string[] = [];\n const removable = new Set<string>();\n\n for (const entry of matches) {\n const entryKey = this.entryKey(entry);\n\n try {\n if (request.kill ?? true) {\n if (this.isProcessRunning(entry.pid)) {\n await this.terminateProcess(entry.pid);\n }\n }\n\n if (request.releasePort ?? true) {\n await this.releaseAssociatedPort(entry);\n }\n\n removable.add(entryKey);\n } catch (error) {\n errors.push(\n `${entry.serviceName} (pid ${entry.pid}): ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n if (removable.size > 0) {\n registry.entries = registry.entries.filter((entry) => !removable.has(this.entryKey(entry)));\n await this.saveState(registry);\n }\n\n if (errors.length > 0) {\n return this.createFailureResponse(errors.join('; '));\n }\n\n return this.createSuccessResponse();\n });\n }\n\n async listProcesses(filters: ListProcessFilters = {}): Promise<ProcessRegistryRecord[]> {\n const request = ListProcessFiltersSchema.parse(filters);\n\n return this.withLock(async () => {\n const state = await this.loadState();\n const registry = await this.pruneState(state);\n\n return [...registry.entries].filter((entry) => {\n if (request.repositoryPath && entry.repositoryPath !== normalizeRepositoryPath(request.repositoryPath)) {\n return false;\n }\n if (request.serviceType && entry.serviceType !== request.serviceType) return false;\n if (request.serviceName && entry.serviceName !== request.serviceName) return false;\n if (request.environment && entry.environment !== request.environment) return false;\n return true;\n });\n });\n }\n\n private async withLock<T>(callback: () => Promise<T>): Promise<T> {\n const lockToken = `${process.pid}-${randomBytes(6).toString('hex')}`;\n await this.acquireLock(lockToken);\n\n try {\n return await callback();\n } finally {\n await this.releaseLock(lockToken);\n }\n }\n\n private async loadState(): Promise<ProcessRegistryState> {\n await fs.mkdir(path.dirname(this.registryPath), { recursive: true });\n\n try {\n const content = await fs.readFile(this.registryPath, 'utf-8');\n const parsed = JSON.parse(content);\n return ProcessRegistryStateSchema.parse(parsed);\n } catch (error) {\n const sysError = error as NodeJS.ErrnoException;\n if (sysError.code === 'ENOENT') {\n return {\n version: REGISTRY_VERSION,\n updatedAt: new Date().toISOString(),\n entries: [],\n };\n }\n\n if (sysError instanceof SyntaxError) {\n const backupPath = `${this.registryPath}.corrupt.${Date.now()}`;\n await fs.rename(this.registryPath, backupPath).catch(() => undefined);\n }\n\n throw new ProcessRegistryError(\n `Failed to read registry file: ${error instanceof Error ? error.message : String(error)}`,\n 'REGISTRY_READ_FAILED',\n { cause: error },\n );\n }\n }\n\n private async saveState(state: ProcessRegistryState): Promise<void> {\n await fs.mkdir(path.dirname(this.registryPath), { recursive: true });\n\n const payload: ProcessRegistryState = {\n ...state,\n updatedAt: new Date().toISOString(),\n entries: [...state.entries],\n };\n\n const tempPath = makeTempPath(this.registryPath);\n await fs.writeFile(tempPath, JSON.stringify(payload, null, 2), 'utf-8');\n await fs.rename(tempPath, this.registryPath);\n }\n\n private async pruneState(state: ProcessRegistryState): Promise<ProcessRegistryState> {\n const repositoryPruned = await this.pruneMissingRepositories(state);\n return this.pruneDeadProcesses(repositoryPruned);\n }\n\n private async pruneMissingRepositories(state: ProcessRegistryState): Promise<ProcessRegistryState> {\n const entries = await Promise.all(\n state.entries.map(async (entry) => ({\n entry,\n exists: await this.pathExists(entry.repositoryPath),\n })),\n );\n\n const pruned = entries.filter((value) => value.exists).map((value) => value.entry);\n if (pruned.length !== state.entries.length) {\n state.entries = pruned;\n await this.saveState(state);\n }\n\n return state;\n }\n\n private async pruneDeadProcesses(state: ProcessRegistryState): Promise<ProcessRegistryState> {\n const aliveEntries: ProcessRegistryRecord[] = [];\n let changed = false;\n\n for (const entry of state.entries) {\n if (this.isProcessRunning(entry.pid)) {\n aliveEntries.push(entry);\n continue;\n }\n\n changed = true;\n await this.releaseAssociatedPort(entry);\n }\n\n if (changed) {\n state.entries = aliveEntries;\n await this.saveState(state);\n }\n\n return state;\n }\n\n private async acquireLock(token: string): Promise<void> {\n await fs.mkdir(path.dirname(this.lockPath), { recursive: true });\n\n for (let attempt = 0; attempt < LOCK_MAX_RETRIES; attempt += 1) {\n try {\n const lockState: LockState = {\n pid: process.pid,\n token,\n createdAt: new Date().toISOString(),\n };\n await fs.writeFile(this.lockPath, JSON.stringify(lockState), { flag: 'wx' });\n this.registerLockCleanup(token);\n return;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') {\n throw new ProcessRegistryError(\n `Failed to acquire registry lock: ${error instanceof Error ? error.message : String(error)}`,\n 'REGISTRY_LOCK_FAILED',\n { cause: error },\n );\n }\n\n const stale = await this.isStaleLock();\n if (stale) {\n await fs.unlink(this.lockPath).catch(() => undefined);\n attempt -= 1;\n continue;\n }\n\n await this.delay(LOCK_RETRY_DELAY_MS);\n }\n }\n\n throw new ProcessRegistryError('Unable to acquire registry lock (timeout)', 'REGISTRY_LOCK_FAILED');\n }\n\n private async releaseLock(token: string): Promise<void> {\n try {\n const existing = await fs.readFile(this.lockPath, 'utf-8');\n const parsed = JSON.parse(existing) as { token: string };\n if (parsed.token === token) {\n await fs.unlink(this.lockPath);\n }\n } catch {\n // ignore\n } finally {\n this.unregisterLockCleanup(token);\n }\n }\n\n private async isStaleLock(): Promise<boolean> {\n try {\n const content = await fs.readFile(this.lockPath, 'utf-8');\n const parsed = JSON.parse(content) as LockState;\n\n if (parsed.pid) {\n const pidAlive = this.isProcessRunning(parsed.pid);\n if (pidAlive) {\n const age = Date.now() - new Date(parsed.createdAt).getTime();\n return !(Number.isFinite(age) && age < LOCK_STALE_AFTER_MS);\n }\n }\n\n return true;\n } catch {\n return true;\n }\n }\n\n private isProcessRunning(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n }\n\n private registerLockCleanup(token: string): void {\n this.unregisterLockCleanup(this.activeLockToken);\n this.activeLockToken = token;\n\n const cleanup = (): void => {\n this.releaseLockSync(token);\n };\n\n for (const event of [\n 'exit',\n 'SIGINT',\n 'SIGTERM',\n 'uncaughtException',\n 'unhandledRejection',\n ] satisfies LockCleanupEvent[]) {\n process.once(event, cleanup);\n this.lockCleanupHandlers.set(event, cleanup);\n }\n }\n\n private unregisterLockCleanup(token: string | undefined): void {\n if (!token || this.activeLockToken !== token) {\n return;\n }\n\n for (const [event, handler] of this.lockCleanupHandlers) {\n process.off(event, handler);\n }\n\n this.lockCleanupHandlers.clear();\n this.activeLockToken = undefined;\n }\n\n private releaseLockSync(token: string): void {\n try {\n const existing = fsSync.readFileSync(this.lockPath, 'utf-8');\n const parsed = JSON.parse(existing) as { token?: string };\n if (parsed.token === token) {\n fsSync.unlinkSync(this.lockPath);\n }\n } catch {\n // ignore best-effort cleanup\n }\n }\n\n private async terminateProcess(pid: number): Promise<void> {\n try {\n process.kill(pid, 0);\n } catch {\n return;\n }\n\n try {\n process.kill(pid, 'SIGTERM');\n } catch (error) {\n const sysError = error as NodeJS.ErrnoException;\n if (sysError.code === 'ESRCH') {\n return;\n }\n\n throw new Error(\n `Failed to send SIGTERM to process ${pid}: ${error instanceof Error ? error.message : String(error)}`,\n {\n cause: error,\n },\n );\n }\n\n await this.delay(500);\n\n if (!this.isProcessRunning(pid)) {\n return;\n }\n\n try {\n process.kill(pid, 'SIGKILL');\n } catch (error) {\n const sysError = error as NodeJS.ErrnoException;\n if (sysError.code === 'ESRCH') {\n return;\n }\n\n throw new Error(\n `Failed to send SIGKILL to process ${pid}: ${error instanceof Error ? error.message : String(error)}`,\n {\n cause: error,\n },\n );\n }\n\n await this.delay(250);\n\n if (this.isProcessRunning(pid)) {\n throw new Error(`Process ${pid} did not exit after SIGKILL`);\n }\n }\n\n private async releaseAssociatedPort(entry: ProcessRegistryRecord): Promise<void> {\n if (!this.portRegistry || !entry.port) {\n return;\n }\n\n try {\n const result = await this.portRegistry.releasePort({\n repositoryPath: entry.repositoryPath,\n serviceName: entry.serviceName,\n serviceType: entry.serviceType,\n environment: entry.environment,\n pid: entry.pid,\n force: true,\n });\n\n if (!result.success && result.error && !result.error.includes('No matching registry entry')) {\n throw new Error(result.error);\n }\n } catch {\n // Best-effort cleanup: port release failures should not block process cleanup.\n }\n }\n\n private entryKey(entry: ProcessRegistryRecord): string {\n return [\n entry.repositoryPath,\n entry.serviceName,\n entry.serviceType,\n entry.environment ?? '',\n String(entry.pid),\n ].join('|');\n }\n\n private findEntry(state: ProcessRegistryState, filters: NormalizedFilters): ProcessRegistryRecord | undefined {\n return state.entries.find(\n (entry) =>\n entry.repositoryPath === filters.repositoryPath &&\n entry.serviceName === filters.serviceName &&\n entry.serviceType === filters.serviceType &&\n (filters.environment ? entry.environment === filters.environment : true),\n );\n }\n\n private removeMatchingEntries(state: ProcessRegistryState, filters: NormalizedFilters): void {\n state.entries = state.entries.filter(\n (entry) =>\n !(\n entry.repositoryPath === filters.repositoryPath &&\n entry.serviceName === filters.serviceName &&\n entry.serviceType === filters.serviceType &&\n (!filters.environment || entry.environment === filters.environment)\n ),\n );\n }\n\n private createSuccessResponse(pid?: number, record?: ProcessRegistryRecord): ProcessRegistryResponse {\n return ProcessRegistryResponseSchema.parse({\n success: true,\n ...(typeof pid === 'number' ? { pid } : {}),\n ...(record ? { record } : {}),\n });\n }\n\n private createFailureResponse(error: string): ProcessRegistryResponse {\n return ProcessRegistryResponseSchema.parse({\n success: false,\n error,\n });\n }\n\n private async pathExists(candidate: string): Promise<boolean> {\n try {\n await fs.access(candidate);\n return true;\n } catch {\n return false;\n }\n }\n\n private delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import e from"node:path";import{randomBytes as t}from"node:crypto";import n from"node:fs";import r from"node:fs/promises";import{z as i}from"zod";import a from"node:os";import{PortRegistryService as o}from"@agimon-ai/foundation-port-registry";const s=1,c=i.enum([`service`,`tool`]),l=i.record(i.string(),i.unknown()),u=i.object({repositoryPath:i.string().trim().min(1,`repositoryPath is required`),serviceName:i.string().trim().min(1,`serviceName is required`),serviceType:c,environment:i.string().trim().min(1).optional(),pid:i.number().int().min(1),port:i.number().int().min(1).max(65535).optional(),host:i.string().trim().min(1).optional(),command:i.string().trim().min(1).optional(),args:i.array(i.string()).optional(),metadata:l.optional(),createdAt:i.string().trim().min(1),updatedAt:i.string().trim().min(1)}),d=i.object({version:i.literal(1),updatedAt:i.string().trim().min(1),entries:i.array(u)}),f=i.object({repositoryPath:i.string().trim().min(1),serviceName:i.string().trim().min(1),serviceType:c.default(`service`),environment:i.string().trim().min(1).optional(),pid:i.number().int().min(1),port:i.number().int().min(1).max(65535).optional(),host:i.string().trim().min(1).optional(),command:i.string().trim().min(1).optional(),args:i.array(i.string()).optional(),metadata:l.optional(),force:i.boolean().optional()}),p=i.object({repositoryPath:i.string().trim().min(1),serviceName:i.string().trim().min(1),serviceType:c.optional(),pid:i.number().int().min(1).optional(),environment:i.string().trim().min(1).optional(),force:i.boolean().optional(),kill:i.boolean().optional().default(!0),releasePort:i.boolean().optional().default(!0)}),m=i.object({repositoryPath:i.string().trim().min(1).optional(),serviceType:c.optional(),serviceName:i.string().trim().min(1).optional(),environment:i.string().trim().min(1).optional()}),h=i.object({success:i.boolean(),pid:i.number().int().min(1).optional(),record:u.optional(),error:i.string().optional()});var g=class extends Error{code;constructor(e,t,n){super(e,n),this.name=`ProcessRegistryError`,this.code=t}};const _=e.join(a.homedir(),`.process-registry`,`processes.json`),v=`${_}.lock`,y=75,b=80,x=5e3,S=t=>e.resolve(t),C=e=>`${e}.${t(6).toString(`hex`)}.tmp`;var w=class i{registryPath;lockPath;activeLockToken;lockCleanupHandlers=new Map;constructor(e=_,t,n=new o(process.env.PORT_REGISTRY_PATH)){this.portRegistry=n,this.registryPath=i.resolveRegistryPath(e),this.lockPath=t??`${this.registryPath}.lock`}static resolveRegistryPath(t){if(!t)return _;let n=e.isAbsolute(t)?t:e.join(process.cwd(),t);return e.extname(n)===`.json`?n:e.join(n,`processes.json`)}async registerProcess(e){let t=f.parse(e);return this.withLock(async()=>{let e=await this.loadState(),n=S(t.repositoryPath),r=t.serviceType??`service`,i=t.environment??process.env.NODE_ENV??`development`,a=await this.pruneState(e),o=this.findEntry(a,{repositoryPath:n,serviceName:t.serviceName,serviceType:r,environment:i});if(o){if(o.pid===t.pid)return o.updatedAt=new Date().toISOString(),o.port=t.port??o.port,o.host=t.host??o.host,o.command=t.command??o.command,o.args=t.args??o.args,o.metadata=t.metadata??o.metadata,await this.saveState(a),this.createSuccessResponse(t.pid,o);let e=this.isProcessRunning(o.pid);if(e&&!t.force)return this.createFailureResponse(`Process already registered for ${t.serviceName} in requested scope`);e&&await this.terminateProcess(o.pid),await this.releaseAssociatedPort(o),this.removeMatchingEntries(a,{repositoryPath:n,serviceName:t.serviceName,serviceType:r,environment:i})}let s=new Date().toISOString(),c={repositoryPath:n,serviceName:t.serviceName,serviceType:r,environment:i,pid:t.pid,port:t.port,host:t.host,command:t.command,args:t.args,metadata:t.metadata,createdAt:s,updatedAt:s};return a.entries.push(c),await this.saveState(a),this.createSuccessResponse(t.pid,c)})}async releaseProcess(e){let t=p.parse(e);return this.withLock(async()=>{let e=await this.loadState(),n=await this.pruneMissingRepositories(e),r=S(t.repositoryPath),i=t.serviceType??`service`,a=t.environment??process.env.NODE_ENV??`development`,o=n.entries.filter(e=>!(e.repositoryPath!==r||e.serviceName!==t.serviceName||e.serviceType!==i||t.environment&&e.environment!==a||typeof t.pid==`number`&&e.pid!==t.pid));if(o.length===0)return this.createFailureResponse(`No matching process entry for ${t.serviceName}`);let s=[],c=new Set;for(let e of o){let n=this.entryKey(e);try{(t.kill??!0)&&this.isProcessRunning(e.pid)&&await this.terminateProcess(e.pid),(t.releasePort??!0)&&await this.releaseAssociatedPort(e),c.add(n)}catch(t){s.push(`${e.serviceName} (pid ${e.pid}): ${t instanceof Error?t.message:String(t)}`)}}return c.size>0&&(n.entries=n.entries.filter(e=>!c.has(this.entryKey(e))),await this.saveState(n)),s.length>0?this.createFailureResponse(s.join(`; `)):this.createSuccessResponse()})}async listProcesses(e={}){let t=m.parse(e);return this.withLock(async()=>{let e=await this.loadState();return[...(await this.pruneState(e)).entries].filter(e=>!(t.repositoryPath&&e.repositoryPath!==S(t.repositoryPath)||t.serviceType&&e.serviceType!==t.serviceType||t.serviceName&&e.serviceName!==t.serviceName||t.environment&&e.environment!==t.environment))})}async withLock(e){let n=`${process.pid}-${t(6).toString(`hex`)}`;await this.acquireLock(n);try{return await e()}finally{await this.releaseLock(n)}}async loadState(){await r.mkdir(e.dirname(this.registryPath),{recursive:!0});try{let e=await r.readFile(this.registryPath,`utf-8`),t=JSON.parse(e);return d.parse(t)}catch(e){let t=e;if(t.code===`ENOENT`)return{version:1,updatedAt:new Date().toISOString(),entries:[]};if(t instanceof SyntaxError){let e=`${this.registryPath}.corrupt.${Date.now()}`;await r.rename(this.registryPath,e).catch(()=>void 0)}throw new g(`Failed to read registry file: ${e instanceof Error?e.message:String(e)}`,`REGISTRY_READ_FAILED`,{cause:e})}}async saveState(t){await r.mkdir(e.dirname(this.registryPath),{recursive:!0});let n={...t,updatedAt:new Date().toISOString(),entries:[...t.entries]},i=C(this.registryPath);await r.writeFile(i,JSON.stringify(n,null,2),`utf-8`),await r.rename(i,this.registryPath)}async pruneState(e){let t=await this.pruneMissingRepositories(e);return this.pruneDeadProcesses(t)}async pruneMissingRepositories(e){let t=(await Promise.all(e.entries.map(async e=>({entry:e,exists:await this.pathExists(e.repositoryPath)})))).filter(e=>e.exists).map(e=>e.entry);return t.length!==e.entries.length&&(e.entries=t,await this.saveState(e)),e}async pruneDeadProcesses(e){let t=[],n=!1;for(let r of e.entries){if(this.isProcessRunning(r.pid)){t.push(r);continue}n=!0,await this.releaseAssociatedPort(r)}return n&&(e.entries=t,await this.saveState(e)),e}async acquireLock(t){await r.mkdir(e.dirname(this.lockPath),{recursive:!0});for(let e=0;e<80;e+=1)try{let e={pid:process.pid,token:t,createdAt:new Date().toISOString()};await r.writeFile(this.lockPath,JSON.stringify(e),{flag:`wx`}),this.registerLockCleanup(t);return}catch(t){if(t.code!==`EEXIST`)throw new g(`Failed to acquire registry lock: ${t instanceof Error?t.message:String(t)}`,`REGISTRY_LOCK_FAILED`,{cause:t});if(await this.isStaleLock()){await r.unlink(this.lockPath).catch(()=>void 0),--e;continue}await this.delay(75)}throw new g(`Unable to acquire registry lock (timeout)`,`REGISTRY_LOCK_FAILED`)}async releaseLock(e){try{let t=await r.readFile(this.lockPath,`utf-8`);JSON.parse(t).token===e&&await r.unlink(this.lockPath)}catch{}finally{this.unregisterLockCleanup(e)}}async isStaleLock(){try{let e=await r.readFile(this.lockPath,`utf-8`),t=JSON.parse(e);if(t.pid&&this.isProcessRunning(t.pid)){let e=Date.now()-new Date(t.createdAt).getTime();return!(Number.isFinite(e)&&e<x)}return!0}catch{return!0}}isProcessRunning(e){try{return process.kill(e,0),!0}catch{return!1}}registerLockCleanup(e){this.unregisterLockCleanup(this.activeLockToken),this.activeLockToken=e;let t=()=>{this.releaseLockSync(e)};for(let e of[`exit`,`SIGINT`,`SIGTERM`,`uncaughtException`,`unhandledRejection`])process.once(e,t),this.lockCleanupHandlers.set(e,t)}unregisterLockCleanup(e){if(!(!e||this.activeLockToken!==e)){for(let[e,t]of this.lockCleanupHandlers)process.off(e,t);this.lockCleanupHandlers.clear(),this.activeLockToken=void 0}}releaseLockSync(e){try{let t=n.readFileSync(this.lockPath,`utf-8`);JSON.parse(t).token===e&&n.unlinkSync(this.lockPath)}catch{}}async terminateProcess(e){try{process.kill(e,0)}catch{return}try{process.kill(e,`SIGTERM`)}catch(t){if(t.code===`ESRCH`)return;throw Error(`Failed to send SIGTERM to process ${e}: ${t instanceof Error?t.message:String(t)}`,{cause:t})}if(await this.delay(500),this.isProcessRunning(e)){try{process.kill(e,`SIGKILL`)}catch(t){if(t.code===`ESRCH`)return;throw Error(`Failed to send SIGKILL to process ${e}: ${t instanceof Error?t.message:String(t)}`,{cause:t})}if(await this.delay(250),this.isProcessRunning(e))throw Error(`Process ${e} did not exit after SIGKILL`)}}async releaseAssociatedPort(e){if(!(!this.portRegistry||!e.port))try{let t=await this.portRegistry.releasePort({repositoryPath:e.repositoryPath,serviceName:e.serviceName,serviceType:e.serviceType,environment:e.environment,pid:e.pid,force:!0});if(!t.success&&t.error&&!t.error.includes(`No matching registry entry`))throw Error(t.error)}catch{}}entryKey(e){return[e.repositoryPath,e.serviceName,e.serviceType,e.environment??``,String(e.pid)].join(`|`)}findEntry(e,t){return e.entries.find(e=>e.repositoryPath===t.repositoryPath&&e.serviceName===t.serviceName&&e.serviceType===t.serviceType&&(t.environment?e.environment===t.environment:!0))}removeMatchingEntries(e,t){e.entries=e.entries.filter(e=>!(e.repositoryPath===t.repositoryPath&&e.serviceName===t.serviceName&&e.serviceType===t.serviceType&&(!t.environment||e.environment===t.environment)))}createSuccessResponse(e,t){return h.parse({success:!0,...typeof e==`number`?{pid:e}:{},...t?{record:t}:{}})}createFailureResponse(e){return h.parse({success:!1,error:e})}async pathExists(e){try{return await r.access(e),!0}catch{return!1}}delay(e){return new Promise(t=>setTimeout(t,e))}};export{p as _,y as a,S as c,g as d,u as f,f as g,s as h,b as i,m as l,d as m,v as n,x as o,h as p,_ as r,C as s,w as t,l as u,c as v};
|
|
1
|
+
import e from"node:path";import{randomBytes as t}from"node:crypto";import n from"node:fs";import r from"node:fs/promises";import{PortRegistryService as i}from"@agimon-ai/foundation-port-registry";import{z as a}from"zod";import o from"node:os";const s=1,c=a.enum([`service`,`tool`]),l=a.record(a.string(),a.unknown()),u=a.object({repositoryPath:a.string().trim().min(1,`repositoryPath is required`),serviceName:a.string().trim().min(1,`serviceName is required`),serviceType:c,environment:a.string().trim().min(1).optional(),pid:a.number().int().min(1),port:a.number().int().min(1).max(65535).optional(),host:a.string().trim().min(1).optional(),command:a.string().trim().min(1).optional(),args:a.array(a.string()).optional(),metadata:l.optional(),createdAt:a.string().trim().min(1),updatedAt:a.string().trim().min(1)}),d=a.object({version:a.literal(1),updatedAt:a.string().trim().min(1),entries:a.array(u)}),f=a.object({repositoryPath:a.string().trim().min(1),serviceName:a.string().trim().min(1),serviceType:c.default(`service`),environment:a.string().trim().min(1).optional(),pid:a.number().int().min(1),port:a.number().int().min(1).max(65535).optional(),host:a.string().trim().min(1).optional(),command:a.string().trim().min(1).optional(),args:a.array(a.string()).optional(),metadata:l.optional(),force:a.boolean().optional()}),p=a.object({repositoryPath:a.string().trim().min(1),serviceName:a.string().trim().min(1),serviceType:c.optional(),pid:a.number().int().min(1).optional(),environment:a.string().trim().min(1).optional(),force:a.boolean().optional(),kill:a.boolean().optional().default(!0),releasePort:a.boolean().optional().default(!0)}),m=a.object({repositoryPath:a.string().trim().min(1).optional(),serviceType:c.optional(),serviceName:a.string().trim().min(1).optional(),environment:a.string().trim().min(1).optional()}),h=a.object({success:a.boolean(),pid:a.number().int().min(1).optional(),record:u.optional(),error:a.string().optional()});var g=class extends Error{code;constructor(e,t,n){super(e,n),this.name=`ProcessRegistryError`,this.code=t}};const _=e.join(o.homedir(),`.process-registry`,`processes.json`),v=`${_}.lock`,y=75,b=80,x=5e3,S=t=>e.resolve(t),C=e=>`${e}.${t(6).toString(`hex`)}.tmp`;function w(t,n){if(!t)return;let r=e.resolve(t);return e.extname(r)===`.json`?e.join(e.dirname(r),n):e.join(r,n)}var T=class a{registryPath;lockPath;activeLockToken;lockCleanupHandlers=new Map;constructor(e=_,t,n=new i(process.env.PORT_REGISTRY_PATH)){this.portRegistry=n,this.registryPath=a.resolveRegistryPath(e),this.lockPath=t??`${this.registryPath}.lock`}static resolveRegistryPath(t){if(!t)return _;let n=e.isAbsolute(t)?t:e.join(process.cwd(),t);return e.extname(n)===`.json`?n:e.join(n,`processes.json`)}async registerProcess(e){let t=f.parse(e);return this.withLock(async()=>{let e=await this.loadState(),n=S(t.repositoryPath),r=t.serviceType??`service`,i=t.environment??process.env.NODE_ENV??`development`,a=await this.pruneState(e),o=this.findEntry(a,{repositoryPath:n,serviceName:t.serviceName,serviceType:r,environment:i});if(o){if(o.pid===t.pid)return o.updatedAt=new Date().toISOString(),o.port=t.port??o.port,o.host=t.host??o.host,o.command=t.command??o.command,o.args=t.args??o.args,o.metadata=t.metadata??o.metadata,await this.saveState(a),this.createSuccessResponse(t.pid,o);let e=this.isProcessRunning(o.pid);if(e&&!(t.force??!0))return this.createFailureResponse(`Process already registered for ${t.serviceName} in requested scope`);e&&await this.terminateProcess(o.pid),await this.releaseAssociatedPort(o),this.removeMatchingEntries(a,{repositoryPath:n,serviceName:t.serviceName,serviceType:r,environment:i})}let s=new Date().toISOString(),c={repositoryPath:n,serviceName:t.serviceName,serviceType:r,environment:i,pid:t.pid,port:t.port,host:t.host,command:t.command,args:t.args,metadata:t.metadata,createdAt:s,updatedAt:s};return a.entries.push(c),await this.saveState(a),this.createSuccessResponse(t.pid,c)})}async releaseProcess(e){let t=p.parse(e);return this.withLock(async()=>{let e=await this.loadState(),n=await this.pruneMissingRepositories(e),r=S(t.repositoryPath),i=t.serviceType??`service`,a=t.environment??process.env.NODE_ENV??`development`,o=n.entries.filter(e=>!(e.repositoryPath!==r||e.serviceName!==t.serviceName||e.serviceType!==i||t.environment&&e.environment!==a||typeof t.pid==`number`&&e.pid!==t.pid));if(o.length===0)return this.createFailureResponse(`No matching process entry for ${t.serviceName}`);let s=[],c=new Set;for(let e of o){let n=this.entryKey(e);try{(t.kill??!0)&&this.isProcessRunning(e.pid)&&await this.terminateProcess(e.pid),(t.releasePort??!0)&&await this.releaseAssociatedPort(e),c.add(n)}catch(t){s.push(`${e.serviceName} (pid ${e.pid}): ${t instanceof Error?t.message:String(t)}`)}}return c.size>0&&(n.entries=n.entries.filter(e=>!c.has(this.entryKey(e))),await this.saveState(n)),s.length>0?this.createFailureResponse(s.join(`; `)):this.createSuccessResponse()})}async listProcesses(e={}){let t=m.parse(e);return this.withLock(async()=>{let e=await this.loadState();return[...(await this.pruneState(e)).entries].filter(e=>!(t.repositoryPath&&e.repositoryPath!==S(t.repositoryPath)||t.serviceType&&e.serviceType!==t.serviceType||t.serviceName&&e.serviceName!==t.serviceName||t.environment&&e.environment!==t.environment))})}async withLock(e){let n=`${process.pid}-${t(6).toString(`hex`)}`;await this.acquireLock(n);try{return await e()}finally{await this.releaseLock(n)}}async loadState(){await r.mkdir(e.dirname(this.registryPath),{recursive:!0});try{let e=await r.readFile(this.registryPath,`utf-8`),t=JSON.parse(e);return d.parse(t)}catch(e){let t=e;if(t.code===`ENOENT`)return{version:1,updatedAt:new Date().toISOString(),entries:[]};if(t instanceof SyntaxError){let e=`${this.registryPath}.corrupt.${Date.now()}`;await r.rename(this.registryPath,e).catch(()=>void 0)}throw new g(`Failed to read registry file: ${e instanceof Error?e.message:String(e)}`,`REGISTRY_READ_FAILED`,{cause:e})}}async saveState(t){await r.mkdir(e.dirname(this.registryPath),{recursive:!0});let n={...t,updatedAt:new Date().toISOString(),entries:[...t.entries]},i=C(this.registryPath);await r.writeFile(i,JSON.stringify(n,null,2),`utf-8`),await r.rename(i,this.registryPath)}async pruneState(e){let t=await this.pruneMissingRepositories(e);return this.pruneDeadProcesses(t)}async pruneMissingRepositories(e){let t=(await Promise.all(e.entries.map(async e=>({entry:e,exists:await this.pathExists(e.repositoryPath)})))).filter(e=>e.exists).map(e=>e.entry);return t.length!==e.entries.length&&(e.entries=t,await this.saveState(e)),e}async pruneDeadProcesses(e){let t=[],n=!1;for(let r of e.entries){if(this.isProcessRunning(r.pid)){t.push(r);continue}n=!0,await this.releaseAssociatedPort(r)}return n&&(e.entries=t,await this.saveState(e)),e}async acquireLock(t){await r.mkdir(e.dirname(this.lockPath),{recursive:!0});for(let e=0;e<80;e+=1)try{let e={pid:process.pid,token:t,createdAt:new Date().toISOString()};await r.writeFile(this.lockPath,JSON.stringify(e),{flag:`wx`}),this.registerLockCleanup(t);return}catch(t){if(t.code!==`EEXIST`)throw new g(`Failed to acquire registry lock: ${t instanceof Error?t.message:String(t)}`,`REGISTRY_LOCK_FAILED`,{cause:t});if(await this.isStaleLock()){await r.unlink(this.lockPath).catch(()=>void 0),--e;continue}await this.delay(75)}throw new g(`Unable to acquire registry lock (timeout)`,`REGISTRY_LOCK_FAILED`)}async releaseLock(e){try{let t=await r.readFile(this.lockPath,`utf-8`);JSON.parse(t).token===e&&await r.unlink(this.lockPath)}catch{}finally{this.unregisterLockCleanup(e)}}async isStaleLock(){try{let e=await r.readFile(this.lockPath,`utf-8`),t=JSON.parse(e);if(t.pid&&this.isProcessRunning(t.pid)){let e=Date.now()-new Date(t.createdAt).getTime();return!(Number.isFinite(e)&&e<x)}return!0}catch{return!0}}isProcessRunning(e){try{return process.kill(e,0),!0}catch{return!1}}registerLockCleanup(e){this.unregisterLockCleanup(this.activeLockToken),this.activeLockToken=e;let t=()=>{this.releaseLockSync(e)};for(let e of[`exit`,`SIGINT`,`SIGTERM`,`uncaughtException`,`unhandledRejection`])process.once(e,t),this.lockCleanupHandlers.set(e,t)}unregisterLockCleanup(e){if(!(!e||this.activeLockToken!==e)){for(let[e,t]of this.lockCleanupHandlers)process.off(e,t);this.lockCleanupHandlers.clear(),this.activeLockToken=void 0}}releaseLockSync(e){try{let t=n.readFileSync(this.lockPath,`utf-8`);JSON.parse(t).token===e&&n.unlinkSync(this.lockPath)}catch{}}async terminateProcess(e){try{process.kill(e,0)}catch{return}try{process.kill(e,`SIGTERM`)}catch(t){if(t.code===`ESRCH`)return;throw Error(`Failed to send SIGTERM to process ${e}: ${t instanceof Error?t.message:String(t)}`,{cause:t})}if(await this.delay(500),this.isProcessRunning(e)){try{process.kill(e,`SIGKILL`)}catch(t){if(t.code===`ESRCH`)return;throw Error(`Failed to send SIGKILL to process ${e}: ${t instanceof Error?t.message:String(t)}`,{cause:t})}if(await this.delay(250),this.isProcessRunning(e))throw Error(`Process ${e} did not exit after SIGKILL`)}}async releaseAssociatedPort(e){if(!(!this.portRegistry||!e.port))try{let t=await this.portRegistry.releasePort({repositoryPath:e.repositoryPath,serviceName:e.serviceName,serviceType:e.serviceType,environment:e.environment,pid:e.pid,force:!0});if(!t.success&&t.error&&!t.error.includes(`No matching registry entry`))throw Error(t.error)}catch{}}entryKey(e){return[e.repositoryPath,e.serviceName,e.serviceType,e.environment??``,String(e.pid)].join(`|`)}findEntry(e,t){return e.entries.find(e=>e.repositoryPath===t.repositoryPath&&e.serviceName===t.serviceName&&e.serviceType===t.serviceType&&(t.environment?e.environment===t.environment:!0))}removeMatchingEntries(e,t){e.entries=e.entries.filter(e=>!(e.repositoryPath===t.repositoryPath&&e.serviceName===t.serviceName&&e.serviceType===t.serviceType&&(!t.environment||e.environment===t.environment)))}createSuccessResponse(e,t){return h.parse({success:!0,...typeof e==`number`?{pid:e}:{},...t?{record:t}:{}})}createFailureResponse(e){return h.parse({success:!1,error:e})}async pathExists(e){try{return await r.access(e),!0}catch{return!1}}delay(e){return new Promise(t=>setTimeout(t,e))}};export{f as _,y as a,S as c,l as d,g as f,s as g,d as h,b as i,w as l,h as m,v as n,x as o,u as p,_ as r,C as s,T as t,m as u,p as v,c as y};
|
|
2
2
|
//# sourceMappingURL=ProcessRegistryService.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProcessRegistryService.mjs","names":["portRegistry: PortRegistryCleanup","record: ProcessRegistryRecord","errors: string[]","payload: ProcessRegistryState","aliveEntries: ProcessRegistryRecord[]","lockState: LockState"],"sources":["../src/types/index.ts","../src/utils/index.ts","../src/services/ProcessRegistryService.ts"],"sourcesContent":["import { z } from 'zod';\n\nexport const REGISTRY_VERSION = 1 as const;\n\nexport const ServiceCategorySchema = z.enum(['service', 'tool']);\n\nexport const ProcessMetadataSchema = z.record(z.string(), z.unknown());\n\nexport const ProcessRegistryRecordSchema = z.object({\n repositoryPath: z.string().trim().min(1, 'repositoryPath is required'),\n serviceName: z.string().trim().min(1, 'serviceName is required'),\n serviceType: ServiceCategorySchema,\n environment: z.string().trim().min(1).optional(),\n pid: z.number().int().min(1),\n port: z.number().int().min(1).max(65535).optional(),\n host: z.string().trim().min(1).optional(),\n command: z.string().trim().min(1).optional(),\n args: z.array(z.string()).optional(),\n metadata: ProcessMetadataSchema.optional(),\n createdAt: z.string().trim().min(1),\n updatedAt: z.string().trim().min(1),\n});\n\nexport const ProcessRegistryStateSchema = z.object({\n version: z.literal(REGISTRY_VERSION),\n updatedAt: z.string().trim().min(1),\n entries: z.array(ProcessRegistryRecordSchema),\n});\n\nexport const RegisterProcessRequestSchema = z.object({\n repositoryPath: z.string().trim().min(1),\n serviceName: z.string().trim().min(1),\n serviceType: ServiceCategorySchema.default('service'),\n environment: z.string().trim().min(1).optional(),\n pid: z.number().int().min(1),\n port: z.number().int().min(1).max(65535).optional(),\n host: z.string().trim().min(1).optional(),\n command: z.string().trim().min(1).optional(),\n args: z.array(z.string()).optional(),\n metadata: ProcessMetadataSchema.optional(),\n force: z.boolean().optional(),\n});\n\nexport const ReleaseProcessRequestSchema = z.object({\n repositoryPath: z.string().trim().min(1),\n serviceName: z.string().trim().min(1),\n serviceType: ServiceCategorySchema.optional(),\n pid: z.number().int().min(1).optional(),\n environment: z.string().trim().min(1).optional(),\n force: z.boolean().optional(),\n kill: z.boolean().optional().default(true),\n releasePort: z.boolean().optional().default(true),\n});\n\nexport const ListProcessFiltersSchema = z.object({\n repositoryPath: z.string().trim().min(1).optional(),\n serviceType: ServiceCategorySchema.optional(),\n serviceName: z.string().trim().min(1).optional(),\n environment: z.string().trim().min(1).optional(),\n});\n\nexport const ProcessRegistryResponseSchema = z.object({\n success: z.boolean(),\n pid: z.number().int().min(1).optional(),\n record: ProcessRegistryRecordSchema.optional(),\n error: z.string().optional(),\n});\n\nexport type ServiceCategory = z.infer<typeof ServiceCategorySchema>;\nexport type ProcessMetadata = z.infer<typeof ProcessMetadataSchema>;\nexport type ProcessRegistryRecord = z.infer<typeof ProcessRegistryRecordSchema>;\nexport type ProcessRegistryState = z.infer<typeof ProcessRegistryStateSchema>;\nexport type RegisterProcessRequest = z.infer<typeof RegisterProcessRequestSchema>;\nexport type ReleaseProcessRequest = z.infer<typeof ReleaseProcessRequestSchema>;\nexport type ListProcessFilters = z.infer<typeof ListProcessFiltersSchema>;\nexport type ProcessRegistryResponse = z.infer<typeof ProcessRegistryResponseSchema>;\n\nexport type ProcessRegistryErrorCode =\n | 'INVALID_REQUEST'\n | 'REGISTRY_READ_FAILED'\n | 'REGISTRY_WRITE_FAILED'\n | 'REGISTRY_LOCK_FAILED'\n | 'NO_PROCESS_FOUND';\n\nexport class ProcessRegistryError extends Error {\n readonly code: ProcessRegistryErrorCode;\n\n constructor(message: string, code: ProcessRegistryErrorCode, options?: ErrorOptions) {\n super(message, options);\n this.name = 'ProcessRegistryError';\n this.code = code;\n }\n}\n","import { randomBytes } from 'node:crypto';\nimport os from 'node:os';\nimport path from 'node:path';\n\nexport const DEFAULT_REGISTRY_PATH = path.join(os.homedir(), '.process-registry', 'processes.json');\nexport const DEFAULT_REGISTRY_LOCK_PATH = `${DEFAULT_REGISTRY_PATH}.lock`;\nexport const LOCK_RETRY_DELAY_MS = 75;\nexport const LOCK_MAX_RETRIES = 80;\nexport const LOCK_STALE_AFTER_MS = 5_000;\n\nexport const normalizeRepositoryPath = (value: string): string => path.resolve(value);\n\nexport const makeTempPath = (filePath: string): string => {\n const random = randomBytes(6).toString('hex');\n return `${filePath}.${random}.tmp`;\n};\n","import { randomBytes } from 'node:crypto';\nimport fsSync from 'node:fs';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport {\n type ListProcessFilters,\n ListProcessFiltersSchema,\n type ProcessRegistryRecord,\n ProcessRegistryError,\n type ProcessRegistryResponse,\n ProcessRegistryResponseSchema,\n type ProcessRegistryState,\n ProcessRegistryStateSchema,\n REGISTRY_VERSION,\n type RegisterProcessRequest,\n RegisterProcessRequestSchema,\n type ReleaseProcessRequest,\n ReleaseProcessRequestSchema,\n} from '../types';\nimport {\n DEFAULT_REGISTRY_PATH,\n LOCK_MAX_RETRIES,\n LOCK_RETRY_DELAY_MS,\n LOCK_STALE_AFTER_MS,\n makeTempPath,\n normalizeRepositoryPath,\n} from '../utils';\nimport { PortRegistryService } from '@agimon-ai/foundation-port-registry';\n\ninterface LockState {\n pid: number;\n token: string;\n createdAt: string;\n}\n\ninterface NormalizedFilters {\n repositoryPath: string;\n serviceName: string;\n serviceType: 'service' | 'tool';\n environment?: string;\n}\n\ntype LockCleanupEvent = 'exit' | 'SIGINT' | 'SIGTERM' | 'uncaughtException' | 'unhandledRejection';\n\ntype PortRegistryCleanup = Pick<PortRegistryService, 'releasePort'>;\n\nexport class ProcessRegistryService {\n private readonly registryPath: string;\n private readonly lockPath: string;\n private activeLockToken?: string;\n private readonly lockCleanupHandlers = new Map<LockCleanupEvent, (...args: unknown[]) => void>();\n\n constructor(\n registryPath: string = DEFAULT_REGISTRY_PATH,\n lockPath?: string,\n private readonly portRegistry: PortRegistryCleanup = new PortRegistryService(process.env.PORT_REGISTRY_PATH),\n ) {\n this.registryPath = ProcessRegistryService.resolveRegistryPath(registryPath);\n this.lockPath = lockPath ?? `${this.registryPath}.lock`;\n }\n\n static resolveRegistryPath(inputPath?: string): string {\n if (!inputPath) {\n return DEFAULT_REGISTRY_PATH;\n }\n\n const resolvedPath = path.isAbsolute(inputPath) ? inputPath : path.join(process.cwd(), inputPath);\n if (path.extname(resolvedPath) === '.json') {\n return resolvedPath;\n }\n\n return path.join(resolvedPath, 'processes.json');\n }\n\n async registerProcess(rawRequest: RegisterProcessRequest): Promise<ProcessRegistryResponse> {\n const request = RegisterProcessRequestSchema.parse(rawRequest);\n\n return this.withLock(async () => {\n const state = await this.loadState();\n const normalizedRepo = normalizeRepositoryPath(request.repositoryPath);\n const serviceType = request.serviceType ?? 'service';\n const environment = request.environment ?? process.env.NODE_ENV ?? 'development';\n const registry = await this.pruneState(state);\n const existing = this.findEntry(registry, {\n repositoryPath: normalizedRepo,\n serviceName: request.serviceName,\n serviceType,\n environment,\n });\n\n if (existing) {\n if (existing.pid === request.pid) {\n existing.updatedAt = new Date().toISOString();\n existing.port = request.port ?? existing.port;\n existing.host = request.host ?? existing.host;\n existing.command = request.command ?? existing.command;\n existing.args = request.args ?? existing.args;\n existing.metadata = request.metadata ?? existing.metadata;\n await this.saveState(registry);\n return this.createSuccessResponse(request.pid, existing);\n }\n\n const isAlive = this.isProcessRunning(existing.pid);\n if (isAlive && !request.force) {\n return this.createFailureResponse(`Process already registered for ${request.serviceName} in requested scope`);\n }\n\n if (isAlive) {\n await this.terminateProcess(existing.pid);\n }\n\n await this.releaseAssociatedPort(existing);\n this.removeMatchingEntries(registry, {\n repositoryPath: normalizedRepo,\n serviceName: request.serviceName,\n serviceType,\n environment,\n });\n }\n\n const now = new Date().toISOString();\n const record: ProcessRegistryRecord = {\n repositoryPath: normalizedRepo,\n serviceName: request.serviceName,\n serviceType,\n environment,\n pid: request.pid,\n port: request.port,\n host: request.host,\n command: request.command,\n args: request.args,\n metadata: request.metadata,\n createdAt: now,\n updatedAt: now,\n };\n\n registry.entries.push(record);\n await this.saveState(registry);\n return this.createSuccessResponse(request.pid, record);\n });\n }\n\n async releaseProcess(rawRequest: ReleaseProcessRequest): Promise<ProcessRegistryResponse> {\n const request = ReleaseProcessRequestSchema.parse(rawRequest);\n\n return this.withLock(async () => {\n const state = await this.loadState();\n const registry = await this.pruneMissingRepositories(state);\n const normalizedRepo = normalizeRepositoryPath(request.repositoryPath);\n const serviceType = request.serviceType ?? 'service';\n const environment = request.environment ?? process.env.NODE_ENV ?? 'development';\n const matches = registry.entries.filter((entry) => {\n if (entry.repositoryPath !== normalizedRepo) return false;\n if (entry.serviceName !== request.serviceName) return false;\n if (entry.serviceType !== serviceType) return false;\n if (request.environment && entry.environment !== environment) return false;\n if (typeof request.pid === 'number' && entry.pid !== request.pid) return false;\n return true;\n });\n\n if (matches.length === 0) {\n return this.createFailureResponse(`No matching process entry for ${request.serviceName}`);\n }\n\n const errors: string[] = [];\n const removable = new Set<string>();\n\n for (const entry of matches) {\n const entryKey = this.entryKey(entry);\n\n try {\n if (request.kill ?? true) {\n if (this.isProcessRunning(entry.pid)) {\n await this.terminateProcess(entry.pid);\n }\n }\n\n if (request.releasePort ?? true) {\n await this.releaseAssociatedPort(entry);\n }\n\n removable.add(entryKey);\n } catch (error) {\n errors.push(\n `${entry.serviceName} (pid ${entry.pid}): ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n if (removable.size > 0) {\n registry.entries = registry.entries.filter((entry) => !removable.has(this.entryKey(entry)));\n await this.saveState(registry);\n }\n\n if (errors.length > 0) {\n return this.createFailureResponse(errors.join('; '));\n }\n\n return this.createSuccessResponse();\n });\n }\n\n async listProcesses(filters: ListProcessFilters = {}): Promise<ProcessRegistryRecord[]> {\n const request = ListProcessFiltersSchema.parse(filters);\n\n return this.withLock(async () => {\n const state = await this.loadState();\n const registry = await this.pruneState(state);\n\n return [...registry.entries].filter((entry) => {\n if (request.repositoryPath && entry.repositoryPath !== normalizeRepositoryPath(request.repositoryPath)) {\n return false;\n }\n if (request.serviceType && entry.serviceType !== request.serviceType) return false;\n if (request.serviceName && entry.serviceName !== request.serviceName) return false;\n if (request.environment && entry.environment !== request.environment) return false;\n return true;\n });\n });\n }\n\n private async withLock<T>(callback: () => Promise<T>): Promise<T> {\n const lockToken = `${process.pid}-${randomBytes(6).toString('hex')}`;\n await this.acquireLock(lockToken);\n\n try {\n return await callback();\n } finally {\n await this.releaseLock(lockToken);\n }\n }\n\n private async loadState(): Promise<ProcessRegistryState> {\n await fs.mkdir(path.dirname(this.registryPath), { recursive: true });\n\n try {\n const content = await fs.readFile(this.registryPath, 'utf-8');\n const parsed = JSON.parse(content);\n return ProcessRegistryStateSchema.parse(parsed);\n } catch (error) {\n const sysError = error as NodeJS.ErrnoException;\n if (sysError.code === 'ENOENT') {\n return {\n version: REGISTRY_VERSION,\n updatedAt: new Date().toISOString(),\n entries: [],\n };\n }\n\n if (sysError instanceof SyntaxError) {\n const backupPath = `${this.registryPath}.corrupt.${Date.now()}`;\n await fs.rename(this.registryPath, backupPath).catch(() => undefined);\n }\n\n throw new ProcessRegistryError(\n `Failed to read registry file: ${error instanceof Error ? error.message : String(error)}`,\n 'REGISTRY_READ_FAILED',\n { cause: error },\n );\n }\n }\n\n private async saveState(state: ProcessRegistryState): Promise<void> {\n await fs.mkdir(path.dirname(this.registryPath), { recursive: true });\n\n const payload: ProcessRegistryState = {\n ...state,\n updatedAt: new Date().toISOString(),\n entries: [...state.entries],\n };\n\n const tempPath = makeTempPath(this.registryPath);\n await fs.writeFile(tempPath, JSON.stringify(payload, null, 2), 'utf-8');\n await fs.rename(tempPath, this.registryPath);\n }\n\n private async pruneState(state: ProcessRegistryState): Promise<ProcessRegistryState> {\n const repositoryPruned = await this.pruneMissingRepositories(state);\n return this.pruneDeadProcesses(repositoryPruned);\n }\n\n private async pruneMissingRepositories(state: ProcessRegistryState): Promise<ProcessRegistryState> {\n const entries = await Promise.all(\n state.entries.map(async (entry) => ({\n entry,\n exists: await this.pathExists(entry.repositoryPath),\n })),\n );\n\n const pruned = entries.filter((value) => value.exists).map((value) => value.entry);\n if (pruned.length !== state.entries.length) {\n state.entries = pruned;\n await this.saveState(state);\n }\n\n return state;\n }\n\n private async pruneDeadProcesses(state: ProcessRegistryState): Promise<ProcessRegistryState> {\n const aliveEntries: ProcessRegistryRecord[] = [];\n let changed = false;\n\n for (const entry of state.entries) {\n if (this.isProcessRunning(entry.pid)) {\n aliveEntries.push(entry);\n continue;\n }\n\n changed = true;\n await this.releaseAssociatedPort(entry);\n }\n\n if (changed) {\n state.entries = aliveEntries;\n await this.saveState(state);\n }\n\n return state;\n }\n\n private async acquireLock(token: string): Promise<void> {\n await fs.mkdir(path.dirname(this.lockPath), { recursive: true });\n\n for (let attempt = 0; attempt < LOCK_MAX_RETRIES; attempt += 1) {\n try {\n const lockState: LockState = {\n pid: process.pid,\n token,\n createdAt: new Date().toISOString(),\n };\n await fs.writeFile(this.lockPath, JSON.stringify(lockState), { flag: 'wx' });\n this.registerLockCleanup(token);\n return;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') {\n throw new ProcessRegistryError(\n `Failed to acquire registry lock: ${error instanceof Error ? error.message : String(error)}`,\n 'REGISTRY_LOCK_FAILED',\n { cause: error },\n );\n }\n\n const stale = await this.isStaleLock();\n if (stale) {\n await fs.unlink(this.lockPath).catch(() => undefined);\n attempt -= 1;\n continue;\n }\n\n await this.delay(LOCK_RETRY_DELAY_MS);\n }\n }\n\n throw new ProcessRegistryError('Unable to acquire registry lock (timeout)', 'REGISTRY_LOCK_FAILED');\n }\n\n private async releaseLock(token: string): Promise<void> {\n try {\n const existing = await fs.readFile(this.lockPath, 'utf-8');\n const parsed = JSON.parse(existing) as { token: string };\n if (parsed.token === token) {\n await fs.unlink(this.lockPath);\n }\n } catch {\n // ignore\n } finally {\n this.unregisterLockCleanup(token);\n }\n }\n\n private async isStaleLock(): Promise<boolean> {\n try {\n const content = await fs.readFile(this.lockPath, 'utf-8');\n const parsed = JSON.parse(content) as LockState;\n\n if (parsed.pid) {\n const pidAlive = this.isProcessRunning(parsed.pid);\n if (pidAlive) {\n const age = Date.now() - new Date(parsed.createdAt).getTime();\n return !(Number.isFinite(age) && age < LOCK_STALE_AFTER_MS);\n }\n }\n\n return true;\n } catch {\n return true;\n }\n }\n\n private isProcessRunning(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n }\n\n private registerLockCleanup(token: string): void {\n this.unregisterLockCleanup(this.activeLockToken);\n this.activeLockToken = token;\n\n const cleanup = (): void => {\n this.releaseLockSync(token);\n };\n\n for (const event of [\n 'exit',\n 'SIGINT',\n 'SIGTERM',\n 'uncaughtException',\n 'unhandledRejection',\n ] satisfies LockCleanupEvent[]) {\n process.once(event, cleanup);\n this.lockCleanupHandlers.set(event, cleanup);\n }\n }\n\n private unregisterLockCleanup(token: string | undefined): void {\n if (!token || this.activeLockToken !== token) {\n return;\n }\n\n for (const [event, handler] of this.lockCleanupHandlers) {\n process.off(event, handler);\n }\n\n this.lockCleanupHandlers.clear();\n this.activeLockToken = undefined;\n }\n\n private releaseLockSync(token: string): void {\n try {\n const existing = fsSync.readFileSync(this.lockPath, 'utf-8');\n const parsed = JSON.parse(existing) as { token?: string };\n if (parsed.token === token) {\n fsSync.unlinkSync(this.lockPath);\n }\n } catch {\n // ignore best-effort cleanup\n }\n }\n\n private async terminateProcess(pid: number): Promise<void> {\n try {\n process.kill(pid, 0);\n } catch {\n return;\n }\n\n try {\n process.kill(pid, 'SIGTERM');\n } catch (error) {\n const sysError = error as NodeJS.ErrnoException;\n if (sysError.code === 'ESRCH') {\n return;\n }\n\n throw new Error(\n `Failed to send SIGTERM to process ${pid}: ${error instanceof Error ? error.message : String(error)}`,\n {\n cause: error,\n },\n );\n }\n\n await this.delay(500);\n\n if (!this.isProcessRunning(pid)) {\n return;\n }\n\n try {\n process.kill(pid, 'SIGKILL');\n } catch (error) {\n const sysError = error as NodeJS.ErrnoException;\n if (sysError.code === 'ESRCH') {\n return;\n }\n\n throw new Error(\n `Failed to send SIGKILL to process ${pid}: ${error instanceof Error ? error.message : String(error)}`,\n {\n cause: error,\n },\n );\n }\n\n await this.delay(250);\n\n if (this.isProcessRunning(pid)) {\n throw new Error(`Process ${pid} did not exit after SIGKILL`);\n }\n }\n\n private async releaseAssociatedPort(entry: ProcessRegistryRecord): Promise<void> {\n if (!this.portRegistry || !entry.port) {\n return;\n }\n\n try {\n const result = await this.portRegistry.releasePort({\n repositoryPath: entry.repositoryPath,\n serviceName: entry.serviceName,\n serviceType: entry.serviceType,\n environment: entry.environment,\n pid: entry.pid,\n force: true,\n });\n\n if (!result.success && result.error && !result.error.includes('No matching registry entry')) {\n throw new Error(result.error);\n }\n } catch {\n // Best-effort cleanup: port release failures should not block process cleanup.\n }\n }\n\n private entryKey(entry: ProcessRegistryRecord): string {\n return [\n entry.repositoryPath,\n entry.serviceName,\n entry.serviceType,\n entry.environment ?? '',\n String(entry.pid),\n ].join('|');\n }\n\n private findEntry(state: ProcessRegistryState, filters: NormalizedFilters): ProcessRegistryRecord | undefined {\n return state.entries.find(\n (entry) =>\n entry.repositoryPath === filters.repositoryPath &&\n entry.serviceName === filters.serviceName &&\n entry.serviceType === filters.serviceType &&\n (filters.environment ? entry.environment === filters.environment : true),\n );\n }\n\n private removeMatchingEntries(state: ProcessRegistryState, filters: NormalizedFilters): void {\n state.entries = state.entries.filter(\n (entry) =>\n !(\n entry.repositoryPath === filters.repositoryPath &&\n entry.serviceName === filters.serviceName &&\n entry.serviceType === filters.serviceType &&\n (!filters.environment || entry.environment === filters.environment)\n ),\n );\n }\n\n private createSuccessResponse(pid?: number, record?: ProcessRegistryRecord): ProcessRegistryResponse {\n return ProcessRegistryResponseSchema.parse({\n success: true,\n ...(typeof pid === 'number' ? { pid } : {}),\n ...(record ? { record } : {}),\n });\n }\n\n private createFailureResponse(error: string): ProcessRegistryResponse {\n return ProcessRegistryResponseSchema.parse({\n success: false,\n error,\n });\n }\n\n private async pathExists(candidate: string): Promise<boolean> {\n try {\n await fs.access(candidate);\n return true;\n } catch {\n return false;\n }\n }\n\n private delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ProcessRegistryService.mjs","names":["portRegistry: PortRegistryCleanup","record: ProcessRegistryRecord","errors: string[]","payload: ProcessRegistryState","aliveEntries: ProcessRegistryRecord[]","lockState: LockState"],"sources":["../src/types/index.ts","../src/utils/index.ts","../src/services/ProcessRegistryService.ts"],"sourcesContent":["import { z } from 'zod';\n\nexport const REGISTRY_VERSION = 1 as const;\n\nexport const ServiceCategorySchema = z.enum(['service', 'tool']);\n\nexport const ProcessMetadataSchema = z.record(z.string(), z.unknown());\n\nexport const ProcessRegistryRecordSchema = z.object({\n repositoryPath: z.string().trim().min(1, 'repositoryPath is required'),\n serviceName: z.string().trim().min(1, 'serviceName is required'),\n serviceType: ServiceCategorySchema,\n environment: z.string().trim().min(1).optional(),\n pid: z.number().int().min(1),\n port: z.number().int().min(1).max(65535).optional(),\n host: z.string().trim().min(1).optional(),\n command: z.string().trim().min(1).optional(),\n args: z.array(z.string()).optional(),\n metadata: ProcessMetadataSchema.optional(),\n createdAt: z.string().trim().min(1),\n updatedAt: z.string().trim().min(1),\n});\n\nexport const ProcessRegistryStateSchema = z.object({\n version: z.literal(REGISTRY_VERSION),\n updatedAt: z.string().trim().min(1),\n entries: z.array(ProcessRegistryRecordSchema),\n});\n\nexport const RegisterProcessRequestSchema = z.object({\n repositoryPath: z.string().trim().min(1),\n serviceName: z.string().trim().min(1),\n serviceType: ServiceCategorySchema.default('service'),\n environment: z.string().trim().min(1).optional(),\n pid: z.number().int().min(1),\n port: z.number().int().min(1).max(65535).optional(),\n host: z.string().trim().min(1).optional(),\n command: z.string().trim().min(1).optional(),\n args: z.array(z.string()).optional(),\n metadata: ProcessMetadataSchema.optional(),\n force: z.boolean().optional(),\n});\n\nexport const ReleaseProcessRequestSchema = z.object({\n repositoryPath: z.string().trim().min(1),\n serviceName: z.string().trim().min(1),\n serviceType: ServiceCategorySchema.optional(),\n pid: z.number().int().min(1).optional(),\n environment: z.string().trim().min(1).optional(),\n force: z.boolean().optional(),\n kill: z.boolean().optional().default(true),\n releasePort: z.boolean().optional().default(true),\n});\n\nexport const ListProcessFiltersSchema = z.object({\n repositoryPath: z.string().trim().min(1).optional(),\n serviceType: ServiceCategorySchema.optional(),\n serviceName: z.string().trim().min(1).optional(),\n environment: z.string().trim().min(1).optional(),\n});\n\nexport const ProcessRegistryResponseSchema = z.object({\n success: z.boolean(),\n pid: z.number().int().min(1).optional(),\n record: ProcessRegistryRecordSchema.optional(),\n error: z.string().optional(),\n});\n\nexport type ServiceCategory = z.infer<typeof ServiceCategorySchema>;\nexport type ProcessMetadata = z.infer<typeof ProcessMetadataSchema>;\nexport type ProcessRegistryRecord = z.infer<typeof ProcessRegistryRecordSchema>;\nexport type ProcessRegistryState = z.infer<typeof ProcessRegistryStateSchema>;\nexport type RegisterProcessRequest = z.infer<typeof RegisterProcessRequestSchema>;\nexport type ReleaseProcessRequest = z.infer<typeof ReleaseProcessRequestSchema>;\nexport type ListProcessFilters = z.infer<typeof ListProcessFiltersSchema>;\nexport type ProcessRegistryResponse = z.infer<typeof ProcessRegistryResponseSchema>;\n\nexport type ProcessRegistryErrorCode =\n | 'INVALID_REQUEST'\n | 'REGISTRY_READ_FAILED'\n | 'REGISTRY_WRITE_FAILED'\n | 'REGISTRY_LOCK_FAILED'\n | 'NO_PROCESS_FOUND';\n\nexport class ProcessRegistryError extends Error {\n readonly code: ProcessRegistryErrorCode;\n\n constructor(message: string, code: ProcessRegistryErrorCode, options?: ErrorOptions) {\n super(message, options);\n this.name = 'ProcessRegistryError';\n this.code = code;\n }\n}\n","import { randomBytes } from 'node:crypto';\nimport os from 'node:os';\nimport path from 'node:path';\n\nexport const DEFAULT_REGISTRY_PATH = path.join(os.homedir(), '.process-registry', 'processes.json');\nexport const DEFAULT_REGISTRY_LOCK_PATH = `${DEFAULT_REGISTRY_PATH}.lock`;\nexport const LOCK_RETRY_DELAY_MS = 75;\nexport const LOCK_MAX_RETRIES = 80;\nexport const LOCK_STALE_AFTER_MS = 5_000;\n\nexport const normalizeRepositoryPath = (value: string): string => path.resolve(value);\n\nexport const makeTempPath = (filePath: string): string => {\n const random = randomBytes(6).toString('hex');\n return `${filePath}.${random}.tmp`;\n};\n\nexport function resolveSiblingRegistryPath(registryPath: string | undefined, fileName: string): string | undefined {\n if (!registryPath) {\n return undefined;\n }\n\n const resolved = path.resolve(registryPath);\n if (path.extname(resolved) === '.json') {\n return path.join(path.dirname(resolved), fileName);\n }\n\n return path.join(resolved, fileName);\n}\n","import { randomBytes } from 'node:crypto';\nimport fsSync from 'node:fs';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { PortRegistryService } from '@agimon-ai/foundation-port-registry';\nimport {\n type ListProcessFilters,\n ListProcessFiltersSchema,\n ProcessRegistryError,\n type ProcessRegistryRecord,\n type ProcessRegistryResponse,\n ProcessRegistryResponseSchema,\n type ProcessRegistryState,\n ProcessRegistryStateSchema,\n REGISTRY_VERSION,\n type RegisterProcessRequest,\n RegisterProcessRequestSchema,\n type ReleaseProcessRequest,\n ReleaseProcessRequestSchema,\n} from '../types';\nimport {\n DEFAULT_REGISTRY_PATH,\n LOCK_MAX_RETRIES,\n LOCK_RETRY_DELAY_MS,\n LOCK_STALE_AFTER_MS,\n makeTempPath,\n normalizeRepositoryPath,\n} from '../utils';\n\ninterface LockState {\n pid: number;\n token: string;\n createdAt: string;\n}\n\ninterface NormalizedFilters {\n repositoryPath: string;\n serviceName: string;\n serviceType: 'service' | 'tool';\n environment?: string;\n}\n\ntype LockCleanupEvent = 'exit' | 'SIGINT' | 'SIGTERM' | 'uncaughtException' | 'unhandledRejection';\n\ntype PortRegistryCleanup = Pick<PortRegistryService, 'releasePort'>;\n\nexport class ProcessRegistryService {\n private readonly registryPath: string;\n private readonly lockPath: string;\n private activeLockToken?: string;\n private readonly lockCleanupHandlers = new Map<LockCleanupEvent, (...args: unknown[]) => void>();\n\n constructor(\n registryPath: string = DEFAULT_REGISTRY_PATH,\n lockPath?: string,\n private readonly portRegistry: PortRegistryCleanup = new PortRegistryService(process.env.PORT_REGISTRY_PATH),\n ) {\n this.registryPath = ProcessRegistryService.resolveRegistryPath(registryPath);\n this.lockPath = lockPath ?? `${this.registryPath}.lock`;\n }\n\n static resolveRegistryPath(inputPath?: string): string {\n if (!inputPath) {\n return DEFAULT_REGISTRY_PATH;\n }\n\n const resolvedPath = path.isAbsolute(inputPath) ? inputPath : path.join(process.cwd(), inputPath);\n if (path.extname(resolvedPath) === '.json') {\n return resolvedPath;\n }\n\n return path.join(resolvedPath, 'processes.json');\n }\n\n async registerProcess(rawRequest: RegisterProcessRequest): Promise<ProcessRegistryResponse> {\n const request = RegisterProcessRequestSchema.parse(rawRequest);\n\n return this.withLock(async () => {\n const state = await this.loadState();\n const normalizedRepo = normalizeRepositoryPath(request.repositoryPath);\n const serviceType = request.serviceType ?? 'service';\n const environment = request.environment ?? process.env.NODE_ENV ?? 'development';\n const registry = await this.pruneState(state);\n const existing = this.findEntry(registry, {\n repositoryPath: normalizedRepo,\n serviceName: request.serviceName,\n serviceType,\n environment,\n });\n\n if (existing) {\n if (existing.pid === request.pid) {\n existing.updatedAt = new Date().toISOString();\n existing.port = request.port ?? existing.port;\n existing.host = request.host ?? existing.host;\n existing.command = request.command ?? existing.command;\n existing.args = request.args ?? existing.args;\n existing.metadata = request.metadata ?? existing.metadata;\n await this.saveState(registry);\n return this.createSuccessResponse(request.pid, existing);\n }\n\n const isAlive = this.isProcessRunning(existing.pid);\n if (isAlive && !(request.force ?? true)) {\n return this.createFailureResponse(`Process already registered for ${request.serviceName} in requested scope`);\n }\n\n if (isAlive) {\n await this.terminateProcess(existing.pid);\n }\n\n await this.releaseAssociatedPort(existing);\n this.removeMatchingEntries(registry, {\n repositoryPath: normalizedRepo,\n serviceName: request.serviceName,\n serviceType,\n environment,\n });\n }\n\n const now = new Date().toISOString();\n const record: ProcessRegistryRecord = {\n repositoryPath: normalizedRepo,\n serviceName: request.serviceName,\n serviceType,\n environment,\n pid: request.pid,\n port: request.port,\n host: request.host,\n command: request.command,\n args: request.args,\n metadata: request.metadata,\n createdAt: now,\n updatedAt: now,\n };\n\n registry.entries.push(record);\n await this.saveState(registry);\n return this.createSuccessResponse(request.pid, record);\n });\n }\n\n async releaseProcess(rawRequest: ReleaseProcessRequest): Promise<ProcessRegistryResponse> {\n const request = ReleaseProcessRequestSchema.parse(rawRequest);\n\n return this.withLock(async () => {\n const state = await this.loadState();\n const registry = await this.pruneMissingRepositories(state);\n const normalizedRepo = normalizeRepositoryPath(request.repositoryPath);\n const serviceType = request.serviceType ?? 'service';\n const environment = request.environment ?? process.env.NODE_ENV ?? 'development';\n const matches = registry.entries.filter((entry) => {\n if (entry.repositoryPath !== normalizedRepo) return false;\n if (entry.serviceName !== request.serviceName) return false;\n if (entry.serviceType !== serviceType) return false;\n if (request.environment && entry.environment !== environment) return false;\n if (typeof request.pid === 'number' && entry.pid !== request.pid) return false;\n return true;\n });\n\n if (matches.length === 0) {\n return this.createFailureResponse(`No matching process entry for ${request.serviceName}`);\n }\n\n const errors: string[] = [];\n const removable = new Set<string>();\n\n for (const entry of matches) {\n const entryKey = this.entryKey(entry);\n\n try {\n if (request.kill ?? true) {\n if (this.isProcessRunning(entry.pid)) {\n await this.terminateProcess(entry.pid);\n }\n }\n\n if (request.releasePort ?? true) {\n await this.releaseAssociatedPort(entry);\n }\n\n removable.add(entryKey);\n } catch (error) {\n errors.push(\n `${entry.serviceName} (pid ${entry.pid}): ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n if (removable.size > 0) {\n registry.entries = registry.entries.filter((entry) => !removable.has(this.entryKey(entry)));\n await this.saveState(registry);\n }\n\n if (errors.length > 0) {\n return this.createFailureResponse(errors.join('; '));\n }\n\n return this.createSuccessResponse();\n });\n }\n\n async listProcesses(filters: ListProcessFilters = {}): Promise<ProcessRegistryRecord[]> {\n const request = ListProcessFiltersSchema.parse(filters);\n\n return this.withLock(async () => {\n const state = await this.loadState();\n const registry = await this.pruneState(state);\n\n return [...registry.entries].filter((entry) => {\n if (request.repositoryPath && entry.repositoryPath !== normalizeRepositoryPath(request.repositoryPath)) {\n return false;\n }\n if (request.serviceType && entry.serviceType !== request.serviceType) return false;\n if (request.serviceName && entry.serviceName !== request.serviceName) return false;\n if (request.environment && entry.environment !== request.environment) return false;\n return true;\n });\n });\n }\n\n private async withLock<T>(callback: () => Promise<T>): Promise<T> {\n const lockToken = `${process.pid}-${randomBytes(6).toString('hex')}`;\n await this.acquireLock(lockToken);\n\n try {\n return await callback();\n } finally {\n await this.releaseLock(lockToken);\n }\n }\n\n private async loadState(): Promise<ProcessRegistryState> {\n await fs.mkdir(path.dirname(this.registryPath), { recursive: true });\n\n try {\n const content = await fs.readFile(this.registryPath, 'utf-8');\n const parsed = JSON.parse(content);\n return ProcessRegistryStateSchema.parse(parsed);\n } catch (error) {\n const sysError = error as NodeJS.ErrnoException;\n if (sysError.code === 'ENOENT') {\n return {\n version: REGISTRY_VERSION,\n updatedAt: new Date().toISOString(),\n entries: [],\n };\n }\n\n if (sysError instanceof SyntaxError) {\n const backupPath = `${this.registryPath}.corrupt.${Date.now()}`;\n await fs.rename(this.registryPath, backupPath).catch(() => undefined);\n }\n\n throw new ProcessRegistryError(\n `Failed to read registry file: ${error instanceof Error ? error.message : String(error)}`,\n 'REGISTRY_READ_FAILED',\n { cause: error },\n );\n }\n }\n\n private async saveState(state: ProcessRegistryState): Promise<void> {\n await fs.mkdir(path.dirname(this.registryPath), { recursive: true });\n\n const payload: ProcessRegistryState = {\n ...state,\n updatedAt: new Date().toISOString(),\n entries: [...state.entries],\n };\n\n const tempPath = makeTempPath(this.registryPath);\n await fs.writeFile(tempPath, JSON.stringify(payload, null, 2), 'utf-8');\n await fs.rename(tempPath, this.registryPath);\n }\n\n private async pruneState(state: ProcessRegistryState): Promise<ProcessRegistryState> {\n const repositoryPruned = await this.pruneMissingRepositories(state);\n return this.pruneDeadProcesses(repositoryPruned);\n }\n\n private async pruneMissingRepositories(state: ProcessRegistryState): Promise<ProcessRegistryState> {\n const entries = await Promise.all(\n state.entries.map(async (entry) => ({\n entry,\n exists: await this.pathExists(entry.repositoryPath),\n })),\n );\n\n const pruned = entries.filter((value) => value.exists).map((value) => value.entry);\n if (pruned.length !== state.entries.length) {\n state.entries = pruned;\n await this.saveState(state);\n }\n\n return state;\n }\n\n private async pruneDeadProcesses(state: ProcessRegistryState): Promise<ProcessRegistryState> {\n const aliveEntries: ProcessRegistryRecord[] = [];\n let changed = false;\n\n for (const entry of state.entries) {\n if (this.isProcessRunning(entry.pid)) {\n aliveEntries.push(entry);\n continue;\n }\n\n changed = true;\n await this.releaseAssociatedPort(entry);\n }\n\n if (changed) {\n state.entries = aliveEntries;\n await this.saveState(state);\n }\n\n return state;\n }\n\n private async acquireLock(token: string): Promise<void> {\n await fs.mkdir(path.dirname(this.lockPath), { recursive: true });\n\n for (let attempt = 0; attempt < LOCK_MAX_RETRIES; attempt += 1) {\n try {\n const lockState: LockState = {\n pid: process.pid,\n token,\n createdAt: new Date().toISOString(),\n };\n await fs.writeFile(this.lockPath, JSON.stringify(lockState), { flag: 'wx' });\n this.registerLockCleanup(token);\n return;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') {\n throw new ProcessRegistryError(\n `Failed to acquire registry lock: ${error instanceof Error ? error.message : String(error)}`,\n 'REGISTRY_LOCK_FAILED',\n { cause: error },\n );\n }\n\n const stale = await this.isStaleLock();\n if (stale) {\n await fs.unlink(this.lockPath).catch(() => undefined);\n attempt -= 1;\n continue;\n }\n\n await this.delay(LOCK_RETRY_DELAY_MS);\n }\n }\n\n throw new ProcessRegistryError('Unable to acquire registry lock (timeout)', 'REGISTRY_LOCK_FAILED');\n }\n\n private async releaseLock(token: string): Promise<void> {\n try {\n const existing = await fs.readFile(this.lockPath, 'utf-8');\n const parsed = JSON.parse(existing) as { token: string };\n if (parsed.token === token) {\n await fs.unlink(this.lockPath);\n }\n } catch {\n // ignore\n } finally {\n this.unregisterLockCleanup(token);\n }\n }\n\n private async isStaleLock(): Promise<boolean> {\n try {\n const content = await fs.readFile(this.lockPath, 'utf-8');\n const parsed = JSON.parse(content) as LockState;\n\n if (parsed.pid) {\n const pidAlive = this.isProcessRunning(parsed.pid);\n if (pidAlive) {\n const age = Date.now() - new Date(parsed.createdAt).getTime();\n return !(Number.isFinite(age) && age < LOCK_STALE_AFTER_MS);\n }\n }\n\n return true;\n } catch {\n return true;\n }\n }\n\n private isProcessRunning(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n }\n\n private registerLockCleanup(token: string): void {\n this.unregisterLockCleanup(this.activeLockToken);\n this.activeLockToken = token;\n\n const cleanup = (): void => {\n this.releaseLockSync(token);\n };\n\n for (const event of [\n 'exit',\n 'SIGINT',\n 'SIGTERM',\n 'uncaughtException',\n 'unhandledRejection',\n ] satisfies LockCleanupEvent[]) {\n process.once(event, cleanup);\n this.lockCleanupHandlers.set(event, cleanup);\n }\n }\n\n private unregisterLockCleanup(token: string | undefined): void {\n if (!token || this.activeLockToken !== token) {\n return;\n }\n\n for (const [event, handler] of this.lockCleanupHandlers) {\n process.off(event, handler);\n }\n\n this.lockCleanupHandlers.clear();\n this.activeLockToken = undefined;\n }\n\n private releaseLockSync(token: string): void {\n try {\n const existing = fsSync.readFileSync(this.lockPath, 'utf-8');\n const parsed = JSON.parse(existing) as { token?: string };\n if (parsed.token === token) {\n fsSync.unlinkSync(this.lockPath);\n }\n } catch {\n // ignore best-effort cleanup\n }\n }\n\n private async terminateProcess(pid: number): Promise<void> {\n try {\n process.kill(pid, 0);\n } catch {\n return;\n }\n\n try {\n process.kill(pid, 'SIGTERM');\n } catch (error) {\n const sysError = error as NodeJS.ErrnoException;\n if (sysError.code === 'ESRCH') {\n return;\n }\n\n throw new Error(\n `Failed to send SIGTERM to process ${pid}: ${error instanceof Error ? error.message : String(error)}`,\n {\n cause: error,\n },\n );\n }\n\n await this.delay(500);\n\n if (!this.isProcessRunning(pid)) {\n return;\n }\n\n try {\n process.kill(pid, 'SIGKILL');\n } catch (error) {\n const sysError = error as NodeJS.ErrnoException;\n if (sysError.code === 'ESRCH') {\n return;\n }\n\n throw new Error(\n `Failed to send SIGKILL to process ${pid}: ${error instanceof Error ? error.message : String(error)}`,\n {\n cause: error,\n },\n );\n }\n\n await this.delay(250);\n\n if (this.isProcessRunning(pid)) {\n throw new Error(`Process ${pid} did not exit after SIGKILL`);\n }\n }\n\n private async releaseAssociatedPort(entry: ProcessRegistryRecord): Promise<void> {\n if (!this.portRegistry || !entry.port) {\n return;\n }\n\n try {\n const result = await this.portRegistry.releasePort({\n repositoryPath: entry.repositoryPath,\n serviceName: entry.serviceName,\n serviceType: entry.serviceType,\n environment: entry.environment,\n pid: entry.pid,\n force: true,\n });\n\n if (!result.success && result.error && !result.error.includes('No matching registry entry')) {\n throw new Error(result.error);\n }\n } catch {\n // Best-effort cleanup: port release failures should not block process cleanup.\n }\n }\n\n private entryKey(entry: ProcessRegistryRecord): string {\n return [\n entry.repositoryPath,\n entry.serviceName,\n entry.serviceType,\n entry.environment ?? '',\n String(entry.pid),\n ].join('|');\n }\n\n private findEntry(state: ProcessRegistryState, filters: NormalizedFilters): ProcessRegistryRecord | undefined {\n return state.entries.find(\n (entry) =>\n entry.repositoryPath === filters.repositoryPath &&\n entry.serviceName === filters.serviceName &&\n entry.serviceType === filters.serviceType &&\n (filters.environment ? entry.environment === filters.environment : true),\n );\n }\n\n private removeMatchingEntries(state: ProcessRegistryState, filters: NormalizedFilters): void {\n state.entries = state.entries.filter(\n (entry) =>\n !(\n entry.repositoryPath === filters.repositoryPath &&\n entry.serviceName === filters.serviceName &&\n entry.serviceType === filters.serviceType &&\n (!filters.environment || entry.environment === filters.environment)\n ),\n );\n }\n\n private createSuccessResponse(pid?: number, record?: ProcessRegistryRecord): ProcessRegistryResponse {\n return ProcessRegistryResponseSchema.parse({\n success: true,\n ...(typeof pid === 'number' ? { pid } : {}),\n ...(record ? { record } : {}),\n });\n }\n\n private createFailureResponse(error: string): ProcessRegistryResponse {\n return ProcessRegistryResponseSchema.parse({\n success: false,\n error,\n });\n }\n\n private async pathExists(candidate: string): Promise<boolean> {\n try {\n await fs.access(candidate);\n return true;\n } catch {\n return false;\n }\n }\n\n private delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n"],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
const e=require(`./ProcessRegistryService.cjs`);exports.DEFAULT_REGISTRY_LOCK_PATH=e.n,exports.DEFAULT_REGISTRY_PATH=e.r,exports.LOCK_MAX_RETRIES=e.i,exports.LOCK_RETRY_DELAY_MS=e.a,exports.LOCK_STALE_AFTER_MS=e.o,exports.ListProcessFiltersSchema=e.
|
|
1
|
+
const e=require(`./ProcessRegistryService.cjs`);function t(t){return new e.t(t??process.env.PROCESS_REGISTRY_PATH)}async function n(e,n){let r=n??t(),i=e.pid??process.pid,a=e.serviceType??`tool`,o=e.environment??process.env.NODE_ENV??`development`,s={repositoryPath:e.repositoryPath,serviceName:e.serviceName,serviceType:a,environment:o,pid:i,port:e.port,host:e.host,command:e.command,args:e.args,metadata:e.metadata,force:e.force??!0},c=await r.registerProcess(s);if(!c.success||!c.record)throw Error(c.error||`Failed to register process for ${e.serviceName}`);let l=!1;return{release:async t=>{if(l)return;l=!0;let n=await r.releaseProcess({repositoryPath:e.repositoryPath,serviceName:e.serviceName,serviceType:a,pid:i,environment:o,kill:t?.kill??!0,releasePort:t?.releasePort??!0});if(!n.success&&n.error&&!n.error.includes(`No matching process entry`))throw Error(n.error||`Failed to release process for ${e.serviceName}`)}}}exports.DEFAULT_REGISTRY_LOCK_PATH=e.n,exports.DEFAULT_REGISTRY_PATH=e.r,exports.LOCK_MAX_RETRIES=e.i,exports.LOCK_RETRY_DELAY_MS=e.a,exports.LOCK_STALE_AFTER_MS=e.o,exports.ListProcessFiltersSchema=e.u,exports.ProcessMetadataSchema=e.d,exports.ProcessRegistryError=e.f,exports.ProcessRegistryRecordSchema=e.p,exports.ProcessRegistryResponseSchema=e.m,exports.ProcessRegistryService=e.t,exports.ProcessRegistryStateSchema=e.h,exports.REGISTRY_VERSION=e.g,exports.RegisterProcessRequestSchema=e._,exports.ReleaseProcessRequestSchema=e.v,exports.ServiceCategorySchema=e.y,exports.createProcessLease=n,exports.createProcessRegistryService=t,exports.makeTempPath=e.s,exports.normalizeRepositoryPath=e.c,exports.resolveSiblingRegistryPath=e.l;
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["ProcessRegistryService","request: RegisterProcessRequest"],"sources":["../src/services/ProcessLease.ts"],"sourcesContent":["import type { RegisterProcessRequest, ServiceCategory } from '../types';\nimport { ProcessRegistryService } from './ProcessRegistryService';\n\nexport interface ProcessLease {\n release(options?: { kill?: boolean; releasePort?: boolean }): Promise<void>;\n}\n\nexport interface ProcessLeaseOptions {\n repositoryPath: string;\n serviceName: string;\n serviceType?: ServiceCategory;\n environment?: string;\n pid?: number;\n port?: number;\n host?: string;\n command?: string;\n args?: string[];\n metadata?: Record<string, unknown>;\n force?: boolean;\n}\n\nexport function createProcessRegistryService(registryPath?: string): ProcessRegistryService {\n return new ProcessRegistryService(registryPath ?? process.env.PROCESS_REGISTRY_PATH);\n}\n\nexport async function createProcessLease(\n options: ProcessLeaseOptions,\n service?: ProcessRegistryService,\n): Promise<ProcessLease> {\n const registry = service ?? createProcessRegistryService();\n const pid = options.pid ?? process.pid;\n const serviceType = options.serviceType ?? 'tool';\n const environment = options.environment ?? process.env.NODE_ENV ?? 'development';\n\n const request: RegisterProcessRequest = {\n repositoryPath: options.repositoryPath,\n serviceName: options.serviceName,\n serviceType,\n environment,\n pid,\n port: options.port,\n host: options.host,\n command: options.command,\n args: options.args,\n metadata: options.metadata,\n force: options.force ?? true,\n };\n\n const result = await registry.registerProcess(request);\n\n if (!result.success || !result.record) {\n throw new Error(result.error || `Failed to register process for ${options.serviceName}`);\n }\n\n let released = false;\n return {\n release: async (releaseOptions?: { kill?: boolean; releasePort?: boolean }): Promise<void> => {\n if (released) {\n return;\n }\n\n released = true;\n const releaseResult = await registry.releaseProcess({\n repositoryPath: options.repositoryPath,\n serviceName: options.serviceName,\n serviceType,\n pid,\n environment,\n kill: releaseOptions?.kill ?? true,\n releasePort: releaseOptions?.releasePort ?? true,\n });\n\n if (!releaseResult.success && releaseResult.error && !releaseResult.error.includes('No matching process entry')) {\n throw new Error(releaseResult.error || `Failed to release process for ${options.serviceName}`);\n }\n },\n };\n}\n"],"mappings":"gDAqBA,SAAgB,EAA6B,EAA+C,CAC1F,OAAO,IAAIA,EAAAA,EAAuB,GAAgB,QAAQ,IAAI,sBAAsB,CAGtF,eAAsB,EACpB,EACA,EACuB,CACvB,IAAM,EAAW,GAAW,GAA8B,CACpD,EAAM,EAAQ,KAAO,QAAQ,IAC7B,EAAc,EAAQ,aAAe,OACrC,EAAc,EAAQ,aAAe,QAAQ,IAAI,UAAY,cAE7DC,EAAkC,CACtC,eAAgB,EAAQ,eACxB,YAAa,EAAQ,YACrB,cACA,cACA,MACA,KAAM,EAAQ,KACd,KAAM,EAAQ,KACd,QAAS,EAAQ,QACjB,KAAM,EAAQ,KACd,SAAU,EAAQ,SAClB,MAAO,EAAQ,OAAS,GACzB,CAEK,EAAS,MAAM,EAAS,gBAAgB,EAAQ,CAEtD,GAAI,CAAC,EAAO,SAAW,CAAC,EAAO,OAC7B,MAAU,MAAM,EAAO,OAAS,kCAAkC,EAAQ,cAAc,CAG1F,IAAI,EAAW,GACf,MAAO,CACL,QAAS,KAAO,IAA8E,CAC5F,GAAI,EACF,OAGF,EAAW,GACX,IAAM,EAAgB,MAAM,EAAS,eAAe,CAClD,eAAgB,EAAQ,eACxB,YAAa,EAAQ,YACrB,cACA,MACA,cACA,KAAM,GAAgB,MAAQ,GAC9B,YAAa,GAAgB,aAAe,GAC7C,CAAC,CAEF,GAAI,CAAC,EAAc,SAAW,EAAc,OAAS,CAAC,EAAc,MAAM,SAAS,4BAA4B,CAC7G,MAAU,MAAM,EAAc,OAAS,iCAAiC,EAAQ,cAAc,EAGnG"}
|
package/dist/index.d.cts
CHANGED
|
@@ -157,6 +157,29 @@ declare class ProcessRegistryService {
|
|
|
157
157
|
private delay;
|
|
158
158
|
}
|
|
159
159
|
//#endregion
|
|
160
|
+
//#region src/services/ProcessLease.d.ts
|
|
161
|
+
interface ProcessLease {
|
|
162
|
+
release(options?: {
|
|
163
|
+
kill?: boolean;
|
|
164
|
+
releasePort?: boolean;
|
|
165
|
+
}): Promise<void>;
|
|
166
|
+
}
|
|
167
|
+
interface ProcessLeaseOptions {
|
|
168
|
+
repositoryPath: string;
|
|
169
|
+
serviceName: string;
|
|
170
|
+
serviceType?: ServiceCategory;
|
|
171
|
+
environment?: string;
|
|
172
|
+
pid?: number;
|
|
173
|
+
port?: number;
|
|
174
|
+
host?: string;
|
|
175
|
+
command?: string;
|
|
176
|
+
args?: string[];
|
|
177
|
+
metadata?: Record<string, unknown>;
|
|
178
|
+
force?: boolean;
|
|
179
|
+
}
|
|
180
|
+
declare function createProcessRegistryService(registryPath?: string): ProcessRegistryService;
|
|
181
|
+
declare function createProcessLease(options: ProcessLeaseOptions, service?: ProcessRegistryService): Promise<ProcessLease>;
|
|
182
|
+
//#endregion
|
|
160
183
|
//#region src/utils/index.d.ts
|
|
161
184
|
declare const DEFAULT_REGISTRY_PATH: string;
|
|
162
185
|
declare const DEFAULT_REGISTRY_LOCK_PATH: string;
|
|
@@ -165,6 +188,7 @@ declare const LOCK_MAX_RETRIES = 80;
|
|
|
165
188
|
declare const LOCK_STALE_AFTER_MS = 5000;
|
|
166
189
|
declare const normalizeRepositoryPath: (value: string) => string;
|
|
167
190
|
declare const makeTempPath: (filePath: string) => string;
|
|
191
|
+
declare function resolveSiblingRegistryPath(registryPath: string | undefined, fileName: string): string | undefined;
|
|
168
192
|
//#endregion
|
|
169
|
-
export { DEFAULT_REGISTRY_LOCK_PATH, DEFAULT_REGISTRY_PATH, LOCK_MAX_RETRIES, LOCK_RETRY_DELAY_MS, LOCK_STALE_AFTER_MS, ListProcessFilters, ListProcessFiltersSchema, ProcessMetadata, ProcessMetadataSchema, ProcessRegistryError, ProcessRegistryErrorCode, ProcessRegistryRecord, ProcessRegistryRecordSchema, ProcessRegistryResponse, ProcessRegistryResponseSchema, ProcessRegistryService, ProcessRegistryState, ProcessRegistryStateSchema, REGISTRY_VERSION, RegisterProcessRequest, RegisterProcessRequestSchema, ReleaseProcessRequest, ReleaseProcessRequestSchema, ServiceCategory, ServiceCategorySchema, makeTempPath, normalizeRepositoryPath };
|
|
193
|
+
export { DEFAULT_REGISTRY_LOCK_PATH, DEFAULT_REGISTRY_PATH, LOCK_MAX_RETRIES, LOCK_RETRY_DELAY_MS, LOCK_STALE_AFTER_MS, ListProcessFilters, ListProcessFiltersSchema, ProcessLease, ProcessLeaseOptions, ProcessMetadata, ProcessMetadataSchema, ProcessRegistryError, ProcessRegistryErrorCode, ProcessRegistryRecord, ProcessRegistryRecordSchema, ProcessRegistryResponse, ProcessRegistryResponseSchema, ProcessRegistryService, ProcessRegistryState, ProcessRegistryStateSchema, REGISTRY_VERSION, RegisterProcessRequest, RegisterProcessRequestSchema, ReleaseProcessRequest, ReleaseProcessRequestSchema, ServiceCategory, ServiceCategorySchema, createProcessLease, createProcessRegistryService, makeTempPath, normalizeRepositoryPath, resolveSiblingRegistryPath };
|
|
170
194
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
1
|
import { PortRegistryService } from "@agimon-ai/foundation-port-registry";
|
|
2
|
+
import { z } from "zod";
|
|
3
3
|
|
|
4
4
|
//#region src/types/index.d.ts
|
|
5
5
|
declare const REGISTRY_VERSION: 1;
|
|
@@ -157,6 +157,29 @@ declare class ProcessRegistryService {
|
|
|
157
157
|
private delay;
|
|
158
158
|
}
|
|
159
159
|
//#endregion
|
|
160
|
+
//#region src/services/ProcessLease.d.ts
|
|
161
|
+
interface ProcessLease {
|
|
162
|
+
release(options?: {
|
|
163
|
+
kill?: boolean;
|
|
164
|
+
releasePort?: boolean;
|
|
165
|
+
}): Promise<void>;
|
|
166
|
+
}
|
|
167
|
+
interface ProcessLeaseOptions {
|
|
168
|
+
repositoryPath: string;
|
|
169
|
+
serviceName: string;
|
|
170
|
+
serviceType?: ServiceCategory;
|
|
171
|
+
environment?: string;
|
|
172
|
+
pid?: number;
|
|
173
|
+
port?: number;
|
|
174
|
+
host?: string;
|
|
175
|
+
command?: string;
|
|
176
|
+
args?: string[];
|
|
177
|
+
metadata?: Record<string, unknown>;
|
|
178
|
+
force?: boolean;
|
|
179
|
+
}
|
|
180
|
+
declare function createProcessRegistryService(registryPath?: string): ProcessRegistryService;
|
|
181
|
+
declare function createProcessLease(options: ProcessLeaseOptions, service?: ProcessRegistryService): Promise<ProcessLease>;
|
|
182
|
+
//#endregion
|
|
160
183
|
//#region src/utils/index.d.ts
|
|
161
184
|
declare const DEFAULT_REGISTRY_PATH: string;
|
|
162
185
|
declare const DEFAULT_REGISTRY_LOCK_PATH: string;
|
|
@@ -165,6 +188,7 @@ declare const LOCK_MAX_RETRIES = 80;
|
|
|
165
188
|
declare const LOCK_STALE_AFTER_MS = 5000;
|
|
166
189
|
declare const normalizeRepositoryPath: (value: string) => string;
|
|
167
190
|
declare const makeTempPath: (filePath: string) => string;
|
|
191
|
+
declare function resolveSiblingRegistryPath(registryPath: string | undefined, fileName: string): string | undefined;
|
|
168
192
|
//#endregion
|
|
169
|
-
export { DEFAULT_REGISTRY_LOCK_PATH, DEFAULT_REGISTRY_PATH, LOCK_MAX_RETRIES, LOCK_RETRY_DELAY_MS, LOCK_STALE_AFTER_MS, ListProcessFilters, ListProcessFiltersSchema, ProcessMetadata, ProcessMetadataSchema, ProcessRegistryError, ProcessRegistryErrorCode, ProcessRegistryRecord, ProcessRegistryRecordSchema, ProcessRegistryResponse, ProcessRegistryResponseSchema, ProcessRegistryService, ProcessRegistryState, ProcessRegistryStateSchema, REGISTRY_VERSION, RegisterProcessRequest, RegisterProcessRequestSchema, ReleaseProcessRequest, ReleaseProcessRequestSchema, ServiceCategory, ServiceCategorySchema, makeTempPath, normalizeRepositoryPath };
|
|
193
|
+
export { DEFAULT_REGISTRY_LOCK_PATH, DEFAULT_REGISTRY_PATH, LOCK_MAX_RETRIES, LOCK_RETRY_DELAY_MS, LOCK_STALE_AFTER_MS, ListProcessFilters, ListProcessFiltersSchema, ProcessLease, ProcessLeaseOptions, ProcessMetadata, ProcessMetadataSchema, ProcessRegistryError, ProcessRegistryErrorCode, ProcessRegistryRecord, ProcessRegistryRecordSchema, ProcessRegistryResponse, ProcessRegistryResponseSchema, ProcessRegistryService, ProcessRegistryState, ProcessRegistryStateSchema, REGISTRY_VERSION, RegisterProcessRequest, RegisterProcessRequestSchema, ReleaseProcessRequest, ReleaseProcessRequestSchema, ServiceCategory, ServiceCategorySchema, createProcessLease, createProcessRegistryService, makeTempPath, normalizeRepositoryPath, resolveSiblingRegistryPath };
|
|
170
194
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
import{_ as e,a as t,c as n,d as r,f as i,g as a,h as o,i as s,l as c,m as l,n as u,o as d,p as f,r as p,s as m,t as h,u as g,v as _}from"./ProcessRegistryService.mjs";export{u as DEFAULT_REGISTRY_LOCK_PATH,p as DEFAULT_REGISTRY_PATH,s as LOCK_MAX_RETRIES,t as LOCK_RETRY_DELAY_MS,d as LOCK_STALE_AFTER_MS,
|
|
1
|
+
import{_ as e,a as t,c as n,d as r,f as i,g as a,h as o,i as s,l as c,m as l,n as u,o as d,p as f,r as p,s as m,t as h,u as g,v as _,y as v}from"./ProcessRegistryService.mjs";function y(e){return new h(e??process.env.PROCESS_REGISTRY_PATH)}async function b(e,t){let n=t??y(),r=e.pid??process.pid,i=e.serviceType??`tool`,a=e.environment??process.env.NODE_ENV??`development`,o={repositoryPath:e.repositoryPath,serviceName:e.serviceName,serviceType:i,environment:a,pid:r,port:e.port,host:e.host,command:e.command,args:e.args,metadata:e.metadata,force:e.force??!0},s=await n.registerProcess(o);if(!s.success||!s.record)throw Error(s.error||`Failed to register process for ${e.serviceName}`);let c=!1;return{release:async t=>{if(c)return;c=!0;let o=await n.releaseProcess({repositoryPath:e.repositoryPath,serviceName:e.serviceName,serviceType:i,pid:r,environment:a,kill:t?.kill??!0,releasePort:t?.releasePort??!0});if(!o.success&&o.error&&!o.error.includes(`No matching process entry`))throw Error(o.error||`Failed to release process for ${e.serviceName}`)}}}export{u as DEFAULT_REGISTRY_LOCK_PATH,p as DEFAULT_REGISTRY_PATH,s as LOCK_MAX_RETRIES,t as LOCK_RETRY_DELAY_MS,d as LOCK_STALE_AFTER_MS,g as ListProcessFiltersSchema,r as ProcessMetadataSchema,i as ProcessRegistryError,f as ProcessRegistryRecordSchema,l as ProcessRegistryResponseSchema,h as ProcessRegistryService,o as ProcessRegistryStateSchema,a as REGISTRY_VERSION,e as RegisterProcessRequestSchema,_ as ReleaseProcessRequestSchema,v as ServiceCategorySchema,b as createProcessLease,y as createProcessRegistryService,m as makeTempPath,n as normalizeRepositoryPath,c as resolveSiblingRegistryPath};
|
|
2
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["request: RegisterProcessRequest"],"sources":["../src/services/ProcessLease.ts"],"sourcesContent":["import type { RegisterProcessRequest, ServiceCategory } from '../types';\nimport { ProcessRegistryService } from './ProcessRegistryService';\n\nexport interface ProcessLease {\n release(options?: { kill?: boolean; releasePort?: boolean }): Promise<void>;\n}\n\nexport interface ProcessLeaseOptions {\n repositoryPath: string;\n serviceName: string;\n serviceType?: ServiceCategory;\n environment?: string;\n pid?: number;\n port?: number;\n host?: string;\n command?: string;\n args?: string[];\n metadata?: Record<string, unknown>;\n force?: boolean;\n}\n\nexport function createProcessRegistryService(registryPath?: string): ProcessRegistryService {\n return new ProcessRegistryService(registryPath ?? process.env.PROCESS_REGISTRY_PATH);\n}\n\nexport async function createProcessLease(\n options: ProcessLeaseOptions,\n service?: ProcessRegistryService,\n): Promise<ProcessLease> {\n const registry = service ?? createProcessRegistryService();\n const pid = options.pid ?? process.pid;\n const serviceType = options.serviceType ?? 'tool';\n const environment = options.environment ?? process.env.NODE_ENV ?? 'development';\n\n const request: RegisterProcessRequest = {\n repositoryPath: options.repositoryPath,\n serviceName: options.serviceName,\n serviceType,\n environment,\n pid,\n port: options.port,\n host: options.host,\n command: options.command,\n args: options.args,\n metadata: options.metadata,\n force: options.force ?? true,\n };\n\n const result = await registry.registerProcess(request);\n\n if (!result.success || !result.record) {\n throw new Error(result.error || `Failed to register process for ${options.serviceName}`);\n }\n\n let released = false;\n return {\n release: async (releaseOptions?: { kill?: boolean; releasePort?: boolean }): Promise<void> => {\n if (released) {\n return;\n }\n\n released = true;\n const releaseResult = await registry.releaseProcess({\n repositoryPath: options.repositoryPath,\n serviceName: options.serviceName,\n serviceType,\n pid,\n environment,\n kill: releaseOptions?.kill ?? true,\n releasePort: releaseOptions?.releasePort ?? true,\n });\n\n if (!releaseResult.success && releaseResult.error && !releaseResult.error.includes('No matching process entry')) {\n throw new Error(releaseResult.error || `Failed to release process for ${options.serviceName}`);\n }\n },\n };\n}\n"],"mappings":"+KAqBA,SAAgB,EAA6B,EAA+C,CAC1F,OAAO,IAAI,EAAuB,GAAgB,QAAQ,IAAI,sBAAsB,CAGtF,eAAsB,EACpB,EACA,EACuB,CACvB,IAAM,EAAW,GAAW,GAA8B,CACpD,EAAM,EAAQ,KAAO,QAAQ,IAC7B,EAAc,EAAQ,aAAe,OACrC,EAAc,EAAQ,aAAe,QAAQ,IAAI,UAAY,cAE7DA,EAAkC,CACtC,eAAgB,EAAQ,eACxB,YAAa,EAAQ,YACrB,cACA,cACA,MACA,KAAM,EAAQ,KACd,KAAM,EAAQ,KACd,QAAS,EAAQ,QACjB,KAAM,EAAQ,KACd,SAAU,EAAQ,SAClB,MAAO,EAAQ,OAAS,GACzB,CAEK,EAAS,MAAM,EAAS,gBAAgB,EAAQ,CAEtD,GAAI,CAAC,EAAO,SAAW,CAAC,EAAO,OAC7B,MAAU,MAAM,EAAO,OAAS,kCAAkC,EAAQ,cAAc,CAG1F,IAAI,EAAW,GACf,MAAO,CACL,QAAS,KAAO,IAA8E,CAC5F,GAAI,EACF,OAGF,EAAW,GACX,IAAM,EAAgB,MAAM,EAAS,eAAe,CAClD,eAAgB,EAAQ,eACxB,YAAa,EAAQ,YACrB,cACA,MACA,cACA,KAAM,GAAgB,MAAQ,GAC9B,YAAa,GAAgB,aAAe,GAC7C,CAAC,CAEF,GAAI,CAAC,EAAc,SAAW,EAAc,OAAS,CAAC,EAAc,MAAM,SAAS,4BAA4B,CAC7G,MAAU,MAAM,EAAc,OAAS,iCAAiC,EAAQ,cAAc,EAGnG"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agimon-ai/foundation-process-registry",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Long-running process registry and cleanup coordination across worktrees",
|
|
5
5
|
"bin": {
|
|
6
6
|
"process-registry": "./dist/cli.cjs"
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"zod": "4.3.6",
|
|
17
|
-
"@agimon-ai/foundation-port-registry": "0.
|
|
17
|
+
"@agimon-ai/foundation-port-registry": "0.3.1"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"@types/node": "^22.0.0",
|