@stacksjs/rpx 0.11.9 → 0.11.11
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/bin/cli.js +166 -162
- package/dist/chunk-00z9z9an.js +156 -0
- package/dist/chunk-747af2w4.js +1 -0
- package/dist/chunk-end75nnv.js +1 -0
- package/dist/{chunk-jpf41gb9.js → chunk-zs1tyy8z.js} +2 -2
- package/dist/daemon.d.ts +4 -0
- package/dist/dns-state.d.ts +27 -0
- package/dist/dns.d.ts +43 -0
- package/dist/hosts.d.ts +2 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +6 -156
- package/package.json +1 -1
- package/src/daemon.ts +41 -2
- package/src/dns-state.ts +116 -0
- package/src/dns.ts +252 -142
- package/src/hosts.ts +26 -5
- package/src/index.ts +29 -0
- package/src/start.ts +7 -9
- package/src/utils.ts +7 -1
- package/dist/chunk-6z1nzq0x.js +0 -1
- package/dist/chunk-qcdcnadb.js +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{Aa as k,Ba as l,Ca as m,Da as n,qa as a,ra as b,sa as c,ta as d,ua as e,va as f,wa as g,xa as h,ya as i,za as j}from"./chunk-zs1tyy8z.js";export{e as safeStringify,n as safeDeleteFile,m as resolvePathRewrite,d as redactSensitive,g as isValidRootCA,k as isSingleProxyOptions,l as isSingleProxyConfig,j as isMultiProxyOptions,i as isMultiProxyConfig,a as getSudoPassword,h as getPrimaryDomain,f as extractHostname,b as execSudoSync,c as debugLog};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{$ as j,S as a,T as b,U as c,V as d,W as e,X as f,Y as g,Z as h,_ as i,aa as k,ba as l,ca as m,da as n}from"./chunk-00z9z9an.js";import"./chunk-zs1tyy8z.js";export{l as tearDownDevelopmentDns,k as syncDevelopmentDnsFromRegistry,d as stopDnsServer,c as startDnsServer,h as setupResolver,j as setupDevelopmentDns,f as resolverFilePath,m as removeResolver,i as removeLegacyTldResolvers,n as reconcileStaleDevelopmentDns,e as isDnsServerRunning,g as contentLooksLikeRpxResolver,b as RPX_RESOLVER_MARKER,a as DNS_PORT};
|
|
@@ -45,5 +45,5 @@ export type ConfigOf = Config
|
|
|
45
45
|
`),H=Math.max(...J.map((z)=>z.length))+2,q=`┌${"─".repeat(H)}┐`,_=`└${"─".repeat(H)}┘`,A=J.map((z)=>{return this.formatConsoleMessage({timestamp:X,message:K.cyan(z),showTimestamp:!1})});console.error(this.formatConsoleMessage({timestamp:X,message:K.cyan(q),showTimestamp:!1})),A.forEach((z)=>console.error(z)),console.error(this.formatConsoleMessage({timestamp:X,message:K.cyan(_),showTimestamp:!1}))}else if(!O())console.error(`${Z} ${this.environment}.INFO: [BOX] ${Y}`);let G=`${Z} ${this.environment}.INFO: [BOX] ${Y}
|
|
46
46
|
`.replace(this.ANSI_PATTERN,"");if(this.shouldWriteToFile())await this.writeToFile(G)}async prompt(Q){if(O())return Promise.resolve(!0);return new Promise((U)=>{console.error(`${K.cyan("?")} ${Q} (y/n) `);let X=(Z)=>{let $=Z.toString().trim().toLowerCase();L.stdin.removeListener("data",X);try{if(typeof L.stdin.setRawMode==="function")L.stdin.setRawMode(!1)}catch{}L.stdin.pause(),console.error(""),U($==="y"||$==="yes")};try{if(typeof L.stdin.setRawMode==="function")L.stdin.setRawMode(!0)}catch{}L.stdin.resume(),L.stdin.once("data",X)})}setFancy(Q){this.fancy=Q}isFancy(){return this.fancy}pause(){this.enabled=!1}resume(){this.enabled=!0}async start(Q,...U){if(!this.enabled)return;let X=Q;if(U&&U.length>0){let H=/%([sdijfo%])/g,q=0;if(X=Q.replace(H,(_,A)=>{if(A==="%")return"%";if(q>=U.length)return _;let z=U[q++];switch(A){case"s":return String(z);case"d":case"i":return Number(z).toString();case"j":case"o":return JSON.stringify(z,null,2);default:return _}}),q<U.length)X+=` ${U.slice(q).map((_)=>typeof _==="object"?JSON.stringify(_,null,2):String(_)).join(" ")}`}let{consoleText:Z,fileText:$}=this.buildOutputTexts(X);if(this.shouldStyleConsole()){let H=this.options.showTags!==!1&&this.name?K.gray(this.formatTag(this.name)):"",q=this.options.showIcons===!1?"":`${K.blue("◐")} `;console.error(`${q}${H} ${K.cyan(Z)}`)}let J=`[${new Date().toISOString()}] ${this.environment}.INFO: [START] ${$}
|
|
47
47
|
`.replace(this.ANSI_PATTERN,"");if(this.shouldWriteToFile())await this.writeToFile(J)}renderProgressBar(Q,U=!1){if(!this.enabled||!this.shouldStyleConsole()||!L.stdout.isTTY)return;let X=Math.min(100,Math.max(0,Math.round(Q.current/Q.total*100))),Z=Math.round(Q.barLength*X/100),$=Q.barLength-Z,Y=K.green("━".repeat(Z)),G=K.gray("━".repeat($)),J=`[${Y}${G}]`,H=`${X}%`.padStart(4),q=Q.message?` ${Q.message}`:"",_=this.options.showIcons===!1?"":U||X===100?K.green("✓"):K.blue("▶"),A=this.options.showTags!==!1&&this.name?` ${K.gray(this.formatTag(this.name))}`:"",z=`\r${_}${A} ${J} ${H}${q}`,W=L.stdout.columns||80,V=" ".repeat(Math.max(0,W-z.replace(this.ANSI_PATTERN,"").length));if(Q.lastRenderedLine=`${z}${V}`,L.stdout.write(Q.lastRenderedLine),U)L.stdout.write(`
|
|
48
|
-
`)}finishProgressBar(Q,U){if(!this.enabled||!this.fancy||O()||!L.stdout.isTTY){this.activeProgressBar=null;return}if(Q.current<Q.total)Q.current=Q.total;if(U)Q.message=U;this.renderProgressBar(Q,!0),this.activeProgressBar=null}async clear(Q={}){if(O()){console.warn("Log clearing is not supported in browser environments.");return}try{console.warn("Clearing logs...",this.config.logDirectory);let U=await DQ(this.config.logDirectory),X=[];for(let Z of U){if(!(Q.name?new RegExp(Q.name.replace("*",".*")).test(Z):Z.startsWith(this.name))||!Z.endsWith(".log"))continue;let Y=GQ(this.config.logDirectory,Z);if(Q.before)try{if((await YQ(Y)).mtime>=Q.before)continue}catch(G){console.error(`Failed to get stats for file ${Y}:`,G);continue}X.push(Y)}if(X.length===0){console.warn("No log files matched the criteria for clearing.");return}console.warn(`Preparing to delete ${X.length} log file(s)...`);for(let Z of X)try{await NQ(Z),console.warn(`Deleted log file: ${Z}`)}catch($){console.error(`Failed to delete log file ${Z}:`,$)}console.warn("Log clearing process finished.")}catch(U){console.error("Error during log clearing process:",U)}}}var A8=new BQ("stacks");var h0=new BQ("rpx",{showTags:!1});function d0(){return process.env.SUDO_PASSWORD}function R8(Q){let U=d0(),X=Q.replace(/'/g,"'\\''");if(U)return PX(`echo '${U}' | sudo -S sh -c '${X}' 2>/dev/null`,{encoding:"utf-8",stdio:["pipe","pipe","pipe"]});return PX(`sudo sh -c '${X}'`,{encoding:"utf-8"})}function bX(Q,U,X){if(X)h0.debug(`[rpx:${Q}] ${U}`)}var yX="[redacted]",f0=new Set(["certificate","privatekey","key","cert","ca","rootca","password","sudo_password"]),p0=/-----BEGIN [A-Z ]+-----[\s\S]*?-----END [A-Z ]+-----/;function u0(Q){let U=Q.toLowerCase();return f0.has(U)||U.endsWith("password")||U.includes("secret")||U.includes("token")}function EU(Q){if(Array.isArray(Q))return Q.map((X)=>EU(X));if(typeof Q==="string")return p0.test(Q)?yX:Q;if(!Q||typeof Q!=="object")return Q;let U={};for(let[X,Z]of Object.entries(Q)){if(u0(X)){U[X]=yX;continue}U[X]=EU(Z)}return U}function L8(Q,U){return JSON.stringify(EU(Q),null,U)}function w8(Q){if(hX(Q))return Q.proxies.map((U)=>{let X=U.to||"stacks.localhost";return X.startsWith("http")?new URL(X).hostname:X});if(dX(Q)){let U=Q.to||"stacks.localhost";return[U.startsWith("http")?new URL(U).hostname:U]}return["stacks.localhost"]}function K8(Q){return typeof Q==="object"&&Q!==null&&"certificate"in Q&&"privateKey"in Q&&typeof Q.certificate==="string"&&typeof Q.privateKey==="string"}function j8(Q){if(!Q)return"stacks.localhost";if(hX(Q)&&Q.proxies.length>0)return Q.proxies[0].to||"stacks.localhost";if(dX(Q))return Q.to||"stacks.localhost";return"stacks.localhost"}function F8(Q){return!!(Q&&("proxies"in Q)&&Array.isArray(Q.proxies))}function hX(Q){return"proxies"in Q&&Array.isArray(Q.proxies)}function dX(Q){return"to"in Q&&typeof Q.to==="string"}function I8(Q){return!!(Q&&("to"in Q)&&!("proxies"in Q))}function M8(Q,U){if(!U||U.length===0)return null;for(let X of U)if(Q===X.from||Q.startsWith(`${X.from}/`)){let Z=X.to.startsWith("http")?new URL(X.to).host:X.to,$=X.stripPrefix===!0?Q.slice(X.from.length)||"/":Q;return{targetHost:Z,targetPath:$}}return null}async function V8(Q,U){try{await vX.unlink(Q),bX("certificates",`Successfully deleted: ${Q}`,U)}catch(X){if(X.code!=="ENOENT")bX("certificates",`Warning: Could not delete ${Q}: ${X}`,U)}}
|
|
49
|
-
export{r as
|
|
48
|
+
`)}finishProgressBar(Q,U){if(!this.enabled||!this.fancy||O()||!L.stdout.isTTY){this.activeProgressBar=null;return}if(Q.current<Q.total)Q.current=Q.total;if(U)Q.message=U;this.renderProgressBar(Q,!0),this.activeProgressBar=null}async clear(Q={}){if(O()){console.warn("Log clearing is not supported in browser environments.");return}try{console.warn("Clearing logs...",this.config.logDirectory);let U=await DQ(this.config.logDirectory),X=[];for(let Z of U){if(!(Q.name?new RegExp(Q.name.replace("*",".*")).test(Z):Z.startsWith(this.name))||!Z.endsWith(".log"))continue;let Y=GQ(this.config.logDirectory,Z);if(Q.before)try{if((await YQ(Y)).mtime>=Q.before)continue}catch(G){console.error(`Failed to get stats for file ${Y}:`,G);continue}X.push(Y)}if(X.length===0){console.warn("No log files matched the criteria for clearing.");return}console.warn(`Preparing to delete ${X.length} log file(s)...`);for(let Z of X)try{await NQ(Z),console.warn(`Deleted log file: ${Z}`)}catch($){console.error(`Failed to delete log file ${Z}:`,$)}console.warn("Log clearing process finished.")}catch(U){console.error("Error during log clearing process:",U)}}}var A8=new BQ("stacks");var h0=new BQ("rpx",{showTags:!1});function d0(){return process.env.SUDO_PASSWORD}function R8(Q){let U=d0(),X=Q.replace(/'/g,"'\\''");if(U)return PX(`echo '${U}' | sudo -S sh -c '${X}' 2>/dev/null`,{encoding:"utf-8",stdio:["pipe","pipe","pipe"]});try{return PX(`sudo -n sh -c '${X}'`,{encoding:"utf-8",stdio:["pipe","pipe","pipe"]})}catch{throw Error("sudo required but no cached credentials (set SUDO_PASSWORD in .env or run sudo -v)")}}function bX(Q,U,X){if(X)h0.debug(`[rpx:${Q}] ${U}`)}var yX="[redacted]",f0=new Set(["certificate","privatekey","key","cert","ca","rootca","password","sudo_password"]),p0=/-----BEGIN [A-Z ]+-----[\s\S]*?-----END [A-Z ]+-----/;function u0(Q){let U=Q.toLowerCase();return f0.has(U)||U.endsWith("password")||U.includes("secret")||U.includes("token")}function EU(Q){if(Array.isArray(Q))return Q.map((X)=>EU(X));if(typeof Q==="string")return p0.test(Q)?yX:Q;if(!Q||typeof Q!=="object")return Q;let U={};for(let[X,Z]of Object.entries(Q)){if(u0(X)){U[X]=yX;continue}U[X]=EU(Z)}return U}function L8(Q,U){return JSON.stringify(EU(Q),null,U)}function w8(Q){if(hX(Q))return Q.proxies.map((U)=>{let X=U.to||"stacks.localhost";return X.startsWith("http")?new URL(X).hostname:X});if(dX(Q)){let U=Q.to||"stacks.localhost";return[U.startsWith("http")?new URL(U).hostname:U]}return["stacks.localhost"]}function K8(Q){return typeof Q==="object"&&Q!==null&&"certificate"in Q&&"privateKey"in Q&&typeof Q.certificate==="string"&&typeof Q.privateKey==="string"}function j8(Q){if(!Q)return"stacks.localhost";if(hX(Q)&&Q.proxies.length>0)return Q.proxies[0].to||"stacks.localhost";if(dX(Q))return Q.to||"stacks.localhost";return"stacks.localhost"}function F8(Q){return!!(Q&&("proxies"in Q)&&Array.isArray(Q.proxies))}function hX(Q){return"proxies"in Q&&Array.isArray(Q.proxies)}function dX(Q){return"to"in Q&&typeof Q.to==="string"}function I8(Q){return!!(Q&&("to"in Q)&&!("proxies"in Q))}function M8(Q,U){if(!U||U.length===0)return null;for(let X of U)if(Q===X.from||Q.startsWith(`${X.from}/`)){let Z=X.to.startsWith("http")?new URL(X.to).host:X.to,$=X.stripPrefix===!0?Q.slice(X.from.length)||"/":Q;return{targetHost:Z,targetPath:$}}return null}async function V8(Q,U){try{await vX.unlink(Q),bX("certificates",`Successfully deleted: ${Q}`,U)}catch(X){if(X.code!=="ENOENT")bX("certificates",`Warning: Could not delete ${Q}: ${X}`,U)}}
|
|
49
|
+
export{r as pa,d0 as qa,R8 as ra,bX as sa,EU as ta,L8 as ua,w8 as va,K8 as wa,j8 as xa,F8 as ya,hX as za,dX as Aa,I8 as Ba,M8 as Ca,V8 as Da};
|
package/dist/daemon.d.ts
CHANGED
|
@@ -55,6 +55,10 @@ export declare function ensureDaemonRunning(opts?: EnsureDaemonOptions): Promise
|
|
|
55
55
|
* if we had to SIGKILL.
|
|
56
56
|
*/
|
|
57
57
|
export declare function stopDaemon(opts?: StopDaemonOptions): Promise<StopDaemonResult>;
|
|
58
|
+
/**
|
|
59
|
+
* When the daemon is not running, ensure no stale macOS resolver overrides remain.
|
|
60
|
+
*/
|
|
61
|
+
export declare function reconcileDevelopmentDnsOnIdle(opts?: { rpxDir?: string, verbose?: boolean }): Promise<void>;
|
|
58
62
|
export declare interface DaemonOptions {
|
|
59
63
|
verbose?: boolean
|
|
60
64
|
rpxDir?: string
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export declare function defaultRpxDir(): string;
|
|
2
|
+
export declare function getDnsStatePath(rpxDir?: string): string;
|
|
3
|
+
export declare function loadDnsState(rpxDir?: string): Promise<DnsState | null>;
|
|
4
|
+
export declare function saveDnsState(rpxDir: string, state: DnsState): Promise<void>;
|
|
5
|
+
export declare function clearDnsState(rpxDir: string): Promise<void>;
|
|
6
|
+
/**
|
|
7
|
+
* Normalize a dev hostname. Returns null for localhost / IPs — those use /etc/hosts only.
|
|
8
|
+
*/
|
|
9
|
+
export declare function normalizeDevDomain(raw: string): string | null;
|
|
10
|
+
/**
|
|
11
|
+
* macOS resolver basename for a dev domain. Uses the registrable base (last two labels)
|
|
12
|
+
* so `api.postline.test` and `postline.test` share one `/etc/resolver/postline.test` file.
|
|
13
|
+
*/
|
|
14
|
+
export declare function resolverBasenameForDomain(raw: string): string | null;
|
|
15
|
+
export declare function resolverBasenamesForDomains(domains: string[]): string[];
|
|
16
|
+
export declare function devDomainsFromHosts(hosts: string[]): string[];
|
|
17
|
+
export declare const DNS_STATE_VERSION: 1;
|
|
18
|
+
export declare const RPX_DNS_STATE_FILE: 'dns-state.json';
|
|
19
|
+
/** Single-label /etc/resolver files created by older rpx versions (whole-TLD hijack). */
|
|
20
|
+
export declare const LEGACY_TLD_RESOLVER_LABELS: readonly ['com', 'test', 'dev', 'app', 'page', 'local', 'localhost', 'example', 'invalid'];
|
|
21
|
+
export declare interface DnsState {
|
|
22
|
+
version: typeof DNS_STATE_VERSION
|
|
23
|
+
resolvers: string[]
|
|
24
|
+
domains: string[]
|
|
25
|
+
ownerPid: number | null
|
|
26
|
+
updatedAt: string
|
|
27
|
+
}
|
package/dist/dns.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { RegistryEntry } from './registry';
|
|
2
|
+
export declare function startDnsServer(domains: string[], verbose?: boolean): Promise<boolean>;
|
|
3
|
+
export declare function stopDnsServer(verbose?: boolean): void;
|
|
4
|
+
export declare function isDnsServerRunning(): boolean;
|
|
5
|
+
export declare function resolverFilePath(basename: string): string;
|
|
6
|
+
/** True when a resolver file points at the rpx local DNS port. */
|
|
7
|
+
export declare function contentLooksLikeRpxResolver(content: string): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* @deprecated Use {@link setupDevelopmentDns}. Domain-scoped resolver files only.
|
|
10
|
+
*/
|
|
11
|
+
export declare function setupResolver(verbose?: boolean, domains?: string[]): Promise<boolean>;
|
|
12
|
+
/** Remove legacy whole-TLD resolver files (e.g. `/etc/resolver/com`). */
|
|
13
|
+
export declare function removeLegacyTldResolvers(verbose?: boolean): Promise<string[]>;
|
|
14
|
+
/**
|
|
15
|
+
* Start the local DNS server and install domain-scoped macOS resolver files.
|
|
16
|
+
*/
|
|
17
|
+
export declare function setupDevelopmentDns(opts: DevelopmentDnsOptions): Promise<boolean>;
|
|
18
|
+
/**
|
|
19
|
+
* Sync resolver + DNS state to the current set of registry hosts (daemon mode).
|
|
20
|
+
*/
|
|
21
|
+
export declare function syncDevelopmentDnsFromRegistry(entries: RegistryEntry[], opts?: { rpxDir?: string, verbose?: boolean, ownerPid?: number }): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Stop DNS and remove all resolver files recorded in state (plus legacy TLD files).
|
|
24
|
+
*/
|
|
25
|
+
export declare function tearDownDevelopmentDns(opts?: { rpxDir?: string, verbose?: boolean }): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* @deprecated Use {@link tearDownDevelopmentDns}.
|
|
28
|
+
*/
|
|
29
|
+
export declare function removeResolver(verbose?: boolean): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Remove stale DNS overrides left after a crashed dev session or legacy TLD hijacks.
|
|
32
|
+
* Safe to call before starting the daemon or `./buddy dev`.
|
|
33
|
+
*/
|
|
34
|
+
export declare function reconcileStaleDevelopmentDns(opts?: { rpxDir?: string, verbose?: boolean }): Promise<void>;
|
|
35
|
+
/** High port — does not require root. */
|
|
36
|
+
export declare const DNS_PORT: 15353;
|
|
37
|
+
export declare const RPX_RESOLVER_MARKER: '# managed-by: rpx';
|
|
38
|
+
export declare interface DevelopmentDnsOptions {
|
|
39
|
+
domains: string[]
|
|
40
|
+
rpxDir?: string
|
|
41
|
+
verbose?: boolean
|
|
42
|
+
ownerPid?: number
|
|
43
|
+
}
|
package/dist/hosts.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/** `.localhost` names resolve to loopback per RFC 6761 — no /etc/hosts entry needed. */
|
|
2
|
+
export declare function isLoopbackDevelopmentHost(host: string): boolean;
|
|
1
3
|
export declare function addHosts(hosts: string[], verbose?: boolean): Promise<void>;
|
|
2
4
|
export declare function removeHosts(hosts: string[], verbose?: boolean): Promise<void>;
|
|
3
5
|
export declare function checkHosts(hosts: string[], verbose?: boolean): Promise<boolean[]>;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { startProxies as startProxiesFunc } from './start';
|
|
2
2
|
export type { RegistryEntry, WatchHandle, WatchOptions } from './registry';
|
|
3
|
+
export type { DevelopmentDnsOptions } from './dns';
|
|
3
4
|
export type {
|
|
4
5
|
DaemonHandle,
|
|
5
6
|
DaemonOptions,
|
|
@@ -61,6 +62,30 @@ export {
|
|
|
61
62
|
watchRegistry,
|
|
62
63
|
writeEntry,
|
|
63
64
|
} from './registry';
|
|
65
|
+
export {
|
|
66
|
+
DNS_PORT,
|
|
67
|
+
RPX_RESOLVER_MARKER,
|
|
68
|
+
contentLooksLikeRpxResolver,
|
|
69
|
+
isDnsServerRunning,
|
|
70
|
+
reconcileStaleDevelopmentDns,
|
|
71
|
+
removeLegacyTldResolvers,
|
|
72
|
+
removeResolver,
|
|
73
|
+
resolverFilePath,
|
|
74
|
+
setupDevelopmentDns,
|
|
75
|
+
setupResolver,
|
|
76
|
+
startDnsServer,
|
|
77
|
+
stopDnsServer,
|
|
78
|
+
syncDevelopmentDnsFromRegistry,
|
|
79
|
+
tearDownDevelopmentDns,
|
|
80
|
+
} from './dns';
|
|
81
|
+
export {
|
|
82
|
+
DNS_STATE_VERSION,
|
|
83
|
+
LEGACY_TLD_RESOLVER_LABELS,
|
|
84
|
+
devDomainsFromHosts,
|
|
85
|
+
normalizeDevDomain,
|
|
86
|
+
resolverBasenameForDomain,
|
|
87
|
+
resolverBasenamesForDomains,
|
|
88
|
+
} from './dns-state';
|
|
64
89
|
export {
|
|
65
90
|
acquireDaemonLock,
|
|
66
91
|
defaultDaemonSpawnCommand,
|
|
@@ -69,6 +94,7 @@ export {
|
|
|
69
94
|
getDaemonRpxDir,
|
|
70
95
|
isDaemonRunning,
|
|
71
96
|
readDaemonPid,
|
|
97
|
+
reconcileDevelopmentDnsOnIdle,
|
|
72
98
|
releaseDaemonLock,
|
|
73
99
|
runDaemon,
|
|
74
100
|
stopDaemon,
|