fix(where)!: Thread OS info
MODIFY
packages/cli/src/endo.js
packages/cli/src/endo.js
@@ -10,6 +10,7 @@ import path from 'path';
import url from 'url';
import crypto from 'crypto';
import { spawn } from 'child_process';
+import os from 'os';
import { Command } from 'commander';
import { makePromiseKit } from '@endo/promise-kit';
@@ -42,10 +43,18 @@ const packageDescriptorPath = url.fileURLToPath(
new URL('../package.json', import.meta.url),
);
-const statePath = whereEndoState(process.platform, process.env);
-const sockPath = whereEndoSock(process.platform, process.env);
-const cachePath = whereEndoCache(process.platform, process.env);
-const logPath = path.join(cachePath, 'endo.log');
+const { username, homedir } = os.userInfo();
+const temp = os.tmpdir();
+const info = {
+ user: username,
+ home: homedir,
+ temp,
+};
+
+const statePath = whereEndoState(process.platform, process.env, info);
+const sockPath = whereEndoSock(process.platform, process.env, info);
+const cachePath = whereEndoCache(process.platform, process.env, info);
+const logPath = path.join(statePath, 'endo.log');
export const main = async rawArgs => {
const { promise: cancelled, reject: cancel } = makePromiseKit();

MODIFY
packages/daemon/index.js
packages/daemon/index.js
@@ -5,6 +5,7 @@ import url from 'url';
import popen from 'child_process';
import fs from 'fs';
import path from 'path';
+import os from 'os';
import { E } from '@endo/eventual-send';
import { makePromiseKit } from '@endo/promise-kit';
@@ -14,10 +15,18 @@ import { makeEndoClient } from './src/client.js';
// Reexports:
export { makeEndoClient } from './src/client.js';
+const { username, homedir } = os.userInfo();
+const temp = os.tmpdir();
+const info = {
+ user: username,
+ home: homedir,
+ temp,
+};
+
const defaultLocator = {
- statePath: whereEndoState(process.platform, process.env),
- sockPath: whereEndoSock(process.platform, process.env),
- cachePath: whereEndoCache(process.platform, process.env),
+ statePath: whereEndoState(process.platform, process.env, info),
+ sockPath: whereEndoSock(process.platform, process.env, info),
+ cachePath: whereEndoCache(process.platform, process.env, info),
};
const endoDaemonPath = url.fileURLToPath(

MODIFY
packages/where/index.js
packages/where/index.js
/**
* Returns a path for local Endo application user data on Windows.
*
- * @param {{[name: string]: string}} env
+ * @param {{[name: string]: string | undefined}} env
+ * @param {import('./types.js').Info} info
*/
-const whereEndoHomeWindows = env => {
+const whereHomeWindows = (env, info) => {
// Favoring local app data over roaming app data since I don't expect to be
// able to listen on one host and connect on another.
// TODO support roaming data for shared content addressable state and
// find a suitable mechanism for merging state that may change independently
// on separate roaming hosts.
if (env.LOCALAPPDATA !== undefined) {
- return `${env.LOCALAPPDATA}\\Endo`;
+ return `${env.LOCALAPPDATA}`;
}
if (env.APPDATA !== undefined) {
- return `${env.APPDATA}\\Local\\Endo`;
+ return `${env.APPDATA}\\Local`;
}
if (env.USERPROFILE !== undefined) {
- return `${env.USERPROFILE}\\AppData\\Local\\Endo`;
+ return `${env.USERPROFILE}\\AppData\\Local`;
}
if (env.HOMEDRIVE !== undefined && env.HOMEPATH !== undefined) {
- return `${env.HOMEDRIVE}${env.HOMEPATH}\\AppData\\Local\\Endo`;
+ return `${env.HOMEDRIVE}${env.HOMEPATH}\\AppData\\Local`;
}
- return 'Endo';
+ return `${info.home}\\AppData\\Local`;
};
/**
@@ -36,47 +37,36 @@ const whereEndoHomeWindows = env => {
*
* @type {typeof import('./types.js').whereEndoState}
*/
-export const whereEndoState = (platform, env) => {
+export const whereEndoState = (platform, env, info) => {
if (env.XDG_STATE_HOME !== undefined) {
return `${env.XDG_STATE_HOME}/endo`;
} else if (platform === 'win32') {
- return whereEndoHomeWindows(env);
- } else if (platform === 'darwin') {
- if (env.HOME !== undefined) {
- return `${env.HOME}/Library/Application Support/Endo`;
+ return `${whereHomeWindows(env, info)}\\Endo`;
+ }
+ const home = env.HOME !== undefined ? env.HOME : info.home;
+ if (platform === 'darwin') {
+ if (home !== undefined) {
+ return `${home}/Library/Application Support/Endo`;
}
- } else if (env.HOME !== undefined) {
- return `${env.HOME}/.local/state/endo`;
}
- return 'endo/state';
+ return `${home}/.local/state/endo`;
};
/**
* Returns the most suitable location for storing state that ideally does not
* persist between restarts or reboots, specifically PID files.
- * This should be coincident with the directory containing UNIX domain sockets,
- * but is not suitable for Windows named pipes.
*
* @type {typeof import('./types.js').whereEndoEphemeralState}
*/
-export const whereEndoEphemeralState = (platform, env) => {
+export const whereEndoEphemeralState = (platform, env, info) => {
if (env.XDG_RUNTIME_DIR !== undefined) {
return `${env.XDG_RUNTIME_DIR}/endo`;
} else if (platform === 'win32') {
- return whereEndoHomeWindows(env);
- } else if (platform === 'darwin') {
- if (env.HOME !== undefined) {
- return `${env.HOME}/Library/Application Support/Endo`;
- }
- }
- if (env.USER !== undefined) {
- // The XDG specification says that in this case, we should fall through to
- // system specific behavior, but we are not in a position to assume which
- // system we're using, other than it is probably a variety of Linux,
- // in which case /tmp and /run might be viable base paths.
- return `/tmp/endo-${env.USER}`;
+ return `${whereHomeWindows(env, info)}\\Temp\\Endo`;
}
- return '/tmp/endo';
+ const temp = env.TMPDIR !== undefined ? env.TMPDIR : info.temp;
+ const user = env.USER !== undefined ? env.USER : info.user;
+ return `${temp}/endo-${user}`;
};
/**
@@ -85,24 +75,25 @@ export const whereEndoEphemeralState = (platform, env) => {
*
* @type {typeof import('./types.js').whereEndoSock}
*/
-export const whereEndoSock = (platform, env, protocol = 'captp0') => {
- if (platform === 'win32') {
+export const whereEndoSock = (platform, env, info, protocol = 'captp0') => {
+ // It must be possible to override the socket or named pipe location, but we
+ // cannot use XDG_RUNTIME_DIR for Windows named pipes, so for this case, we
+ // invent our own environment variable.
+ if (env.ENDO_SOCK !== undefined) {
+ return env.ENDO_SOCK;
+ } else if (platform === 'win32') {
// Named pipes have a special place in Windows (and in our ashen hearts).
- if (env.USERNAME !== undefined) {
- return `\\\\?\\pipe\\${env.USERNAME}-Endo\\${protocol}.pipe`;
- } else {
- return `\\\\?\\pipe\\Endo\\${protocol}.pipe`;
- }
+ const user = env.USERNAME !== undefined ? env.USERNAME : info.user;
+ return `\\\\?\\pipe\\${user}-Endo\\${protocol}.pipe`;
} else if (env.XDG_RUNTIME_DIR !== undefined) {
return `${env.XDG_RUNTIME_DIR}/endo/${protocol}.sock`;
} else if (platform === 'darwin') {
- if (env.HOME !== undefined) {
- return `${env.HOME}/Library/Application Support/Endo/${protocol}.sock`;
+ const home = env.HOME !== undefined ? env.HOME : info.home;
+ return `${home}/Library/Application Support/Endo/${protocol}.sock`;
}
- } else if (env.USER !== undefined) {
- return `/tmp/endo-${env.USER}/${protocol}.sock`;
- }
- return `endo/${protocol}.sock`;
+ const user = env.USER !== undefined ? env.USER : info.user;
+ const temp = env.TMPDIR !== undefined ? env.TMPDIR : info.temp;
+ return `${temp}/endo-${user}/${protocol}.sock`;
};
/**
@@ -110,17 +101,15 @@ export const whereEndoSock = (platform, env, protocol = 'captp0') => {
*
* @type {typeof import('./types.js').whereEndoCache}
*/
-export const whereEndoCache = (platform, env) => {
+export const whereEndoCache = (platform, env, info) => {
if (env.XDG_CACHE_HOME !== undefined) {
return `${env.XDG_CACHE_HOME}/endo`;
} else if (platform === 'win32') {
- return `${whereEndoHomeWindows(env)}`;
+ return `${whereHomeWindows(env, info)}\\Endo`;
} else if (platform === 'darwin') {
- if (env.HOME !== undefined) {
- return `${env.HOME}/Library/Caches/Endo`;
- }
- } else if (env.HOME !== undefined) {
- return `${env.HOME}/.cache/endo`;
+ const home = env.HOME !== undefined ? env.HOME : info.home;
+ return `${home}/Library/Caches/Endo`;
}
- return 'endo/cache';
+ const home = env.HOME !== undefined ? env.HOME : info.home;
+ return `${home}/.cache/endo`;
};

MODIFY
packages/where/test/test-where-endo-cache.js
packages/where/test/test-where-endo-cache.js
@@ -41,9 +41,15 @@ test('windows', t => {
'Infer LOCALAPPDATA from HOMEDRIVE and HOMEPATH',
);
t.is(
- whereEndoCache('win32', {}),
- 'Endo',
- 'Under duress, fall back to a relative path, just Endo',
+ whereEndoCache(
+ 'win32',
+ {},
+ {
+ home: 'C:\\Users\\Alice',
+ },
+ ),
+ 'C:\\Users\\Alice\\AppData\\Local\\Endo',
+ 'Fall through to system-provided home',
);
});
@@ -64,9 +70,15 @@ test('darwin', t => {
'In absence of XDG environment, use conventional Mac cache location',
);
t.is(
- whereEndoCache('darwin', {}),
- 'endo/cache',
- 'Under duress, fall back to a relative path, just endo/cache',
+ whereEndoCache(
+ 'darwin',
+ {},
+ {
+ home: '/Users/homer',
+ },
+ ),
+ '/Users/homer/Library/Caches/Endo',
+ 'Fall back to system provided home',
);
});
@@ -89,10 +101,16 @@ test('linux', t => {
'Infer the conventional XDG environment from the user HOME',
);
t.is(
- whereEndoCache('linux', {
+ whereEndoCache(
+ 'linux',
+ {
USER: 'IGNOREME',
- }),
- 'endo/cache',
+ },
+ {
+ home: '/home/homer',
+ },
+ ),
+ '/home/homer/.cache/endo',
'Under duress, do not attempt to infer whether /users/ or /home/ is a correct USER prefix for HOME, fall through to a relative path',
);
});

MODIFY
packages/where/test/test-where-endo-ephemeral-state.js
packages/where/test/test-where-endo-ephemeral-state.js
@@ -10,7 +10,7 @@ test('windows', t => {
HOMEDRIVE: 'IGNOREME',
HOMEPATH: 'IGNOREME',
}),
- 'C:\\Users\\Alice\\AppData\\Local\\Endo',
+ 'C:\\Users\\Alice\\AppData\\Local\\Temp\\Endo',
'Use LOCALAPPDATA for Endo state if available',
);
t.is(
@@ -20,7 +20,7 @@ test('windows', t => {
HOMEDRIVE: 'IGNOREME',
HOMEPATH: 'IGNOREME',
}),
- 'C:\\Users\\Alice\\AppData\\Local\\Endo',
+ 'C:\\Users\\Alice\\AppData\\Local\\Temp\\Endo',
'Infer LOCALAPPDATA from APPDATA if necessary and possible',
);
t.is(
@@ -29,7 +29,7 @@ test('windows', t => {
HOMEDRIVE: 'IGNOREME',
HOMEPATH: 'IGNOREME',
}),
- 'C:\\Users\\Alice\\AppData\\Local\\Endo',
+ 'C:\\Users\\Alice\\AppData\\Local\\Temp\\Endo',
'Infer LOCALAPPDATA from USERPROFILE if necessary and possible',
);
t.is(
@@ -37,12 +37,18 @@ test('windows', t => {
HOMEDRIVE: 'C:\\',
HOMEPATH: 'Users\\Alice',
}),
- 'C:\\Users\\Alice\\AppData\\Local\\Endo',
+ 'C:\\Users\\Alice\\AppData\\Local\\Temp\\Endo',
'Infer LOCALAPPDATA from HOMEDRIVE and HOMEPATH if necessary and possible',
);
t.is(
- whereEndoEphemeralState('win32', {}),
- 'Endo',
+ whereEndoEphemeralState(
+ 'win32',
+ {},
+ {
+ home: 'C:\\Users\\Alice',
+ },
+ ),
+ 'C:\\Users\\Alice\\AppData\\Local\\Temp\\Endo',
'Under duress, just use a relative path',
);
});
@@ -59,16 +65,18 @@ test('darwin', t => {
'Favor XDG state home over Darwin conventions if provided by the user',
);
t.is(
- whereEndoEphemeralState('darwin', {
- HOME: '/Users/alice',
- }),
- '/Users/alice/Library/Application Support/Endo',
- 'Use the Mac/Darwin conventional location for Application user data',
- );
- t.is(
- whereEndoEphemeralState('darwin', {}),
- '/tmp/endo',
- 'Under duress, fall back to an Endo tmp directory',
+ whereEndoEphemeralState(
+ 'darwin',
+ {
+ HOME: 'IGNOREME',
+ },
+ {
+ user: 'alice',
+ temp: '/tmp/volumes/0',
+ },
+ ),
+ '/tmp/volumes/0/endo-alice',
+ 'Use the system temporary location and user name',
);
});
@@ -84,16 +92,29 @@ test('linux', t => {
'Use XDG state home if provided by the user',
);
t.is(
- whereEndoEphemeralState('linux', {
+ whereEndoEphemeralState(
+ 'linux',
+ {
USER: 'alice',
HOME: 'IGNOREME',
- }),
- '/tmp/endo-alice',
+ },
+ {
+ temp: '/tmp/volume/0',
+ },
+ ),
+ '/tmp/volume/0/endo-alice',
'In the absence of XDG information, infer a temporary location from the USER if available',
);
t.is(
- whereEndoEphemeralState('linux', {}),
- '/tmp/endo',
+ whereEndoEphemeralState(
+ 'linux',
+ {},
+ {
+ user: 'homer',
+ temp: '/tmp/volume/0',
+ },
+ ),
+ '/tmp/volume/0/endo-homer',
'For lack of any useful environment information, fall back to a shared temporary directory',
);
});

MODIFY
packages/where/test/test-where-endo-sock.js
packages/where/test/test-where-endo-sock.js
@@ -11,8 +11,14 @@ test('windows', t => {
'Use Windows named pipe namespace, scoped to the user when possible',
);
t.is(
- whereEndoSock('win32', {}),
- '\\\\?\\pipe\\Endo\\captp0.pipe',
+ whereEndoSock(
+ 'win32',
+ {},
+ {
+ user: 'Bill',
+ },
+ ),
+ '\\\\?\\pipe\\Bill-Endo\\captp0.pipe',
'Under duress, fall back to a location shared by all users',
);
});
@@ -34,9 +40,15 @@ test('darwin', t => {
'Infer Darwin/Mac conventional socket location from HOME',
);
t.is(
- whereEndoSock('darwin', {}),
- 'endo/captp0.sock',
- 'Under duress, fall through to a relative path',
+ whereEndoSock(
+ 'darwin',
+ {},
+ {
+ home: '/Users/alice',
+ },
+ ),
+ '/Users/alice/Library/Application Support/Endo/captp0.sock',
+ 'Fall through to system-provided home',
);
});
@@ -50,15 +62,16 @@ test('linux', t => {
'XDG takes precedence over USER on Linux',
);
t.is(
- whereEndoSock('linux', {
+ whereEndoSock(
+ 'linux',
+ {
USER: 'alice',
- }),
- '/tmp/endo-alice/captp0.sock',
- 'Under duress, assume the host has a /tmp file system and scope the UNIX domain socket to the user by name',
- );
- t.is(
- whereEndoSock('linux', {}),
- 'endo/captp0.sock',
- 'Under extreme duress, just use a relative path for the socket',
+ },
+ {
+ temp: '/tmp/volume/0',
+ },
+ ),
+ '/tmp/volume/0/endo-alice/captp0.sock',
+ 'Under duress, fall back to host provided temporary directory',
);
});

MODIFY
packages/where/test/test-where-endo-state.js
packages/where/test/test-where-endo-state.js
@@ -41,9 +41,15 @@ test('windows', t => {
'Infer LOCALAPPDATA from HOMEDRIVE and HOMEPATH if necessary and possible',
);
t.is(
- whereEndoState('win32', {}),
- 'Endo',
- 'Under duress, just use a relative path',
+ whereEndoState(
+ 'win32',
+ {},
+ {
+ home: 'C:\\Users\\Bill',
+ },
+ ),
+ 'C:\\Users\\Bill\\AppData\\Local\\Endo',
+ 'Fall back to system-provided home',
);
});
@@ -65,9 +71,15 @@ test('darwin', t => {
'Use the Mac/Darwin conventional location for Application user data',
);
t.is(
- whereEndoState('darwin', {}),
- 'endo/state',
- 'Under duress, fall back to a relative path for state',
+ whereEndoState(
+ 'darwin',
+ {},
+ {
+ home: '/Users/Johnny',
+ },
+ ),
+ '/Users/Johnny/Library/Application Support/Endo',
+ 'Fall back to system-provided home',
);
});
@@ -89,8 +101,14 @@ test('linux', t => {
'Infer XDG state home from HOME on Linux',
);
t.is(
- whereEndoState('linux', {}),
- 'endo/state',
+ whereEndoState(
+ 'linux',
+ {},
+ {
+ home: '/home/homer',
+ },
+ ),
+ '/home/homer/.local/state/endo',
'For lack of any useful environment information, fall back to a relative path',
);
});

MODIFY
packages/where/types.d.ts
packages/where/types.d.ts
+export type Info = {
+ home: string;
+ user: string;
+ temp: string;
+};
+
export function whereEndoState(
platform: string,
env: { [name: string]: string | undefined },
+ info: Info,
): string;
export function whereEndoEphemeralState(
platform: string,
env: { [name: string]: string | undefined },
+ info: Info,
): string;
export function whereEndoSock(
platform: string,
env: { [name: string]: string | undefined },
+ info: Info,
+ protocol?: string,
): string;
export function whereEndoCache(
platform: string,
env: { [name: string]: string | undefined },
+ info: Info,
): string;

fix(where)!: Thread OS info