ether-to-astro 1.3.0 → 1.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/entrypoint.d.ts +1 -0
- package/dist/entrypoint.js +13 -0
- package/dist/loader.js +2 -2
- package/dist/mcp-alias.js +2 -1
- package/package.json +1 -1
- package/src/entrypoint.ts +17 -0
- package/src/loader.ts +2 -2
- package/src/mcp-alias.ts +2 -1
- package/tests/unit/entrypoint.test.ts +101 -2
package/dist/entrypoint.d.ts
CHANGED
|
@@ -11,3 +11,4 @@ export interface EntrypointResolution {
|
|
|
11
11
|
mcpStartupDefaults: Readonly<McpStartupDefaults>;
|
|
12
12
|
}
|
|
13
13
|
export declare function resolveEntrypoint(argv: string[], invokedPath?: string): EntrypointResolution;
|
|
14
|
+
export declare function isDirectExecution(importMetaUrl: string, invokedPath?: string): boolean;
|
package/dist/entrypoint.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { realpathSync } from 'node:fs';
|
|
1
2
|
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
2
4
|
import { isValidTimezone } from './time-utils.js';
|
|
3
5
|
const VALID_HOUSE_STYLES = new Set(['P', 'W', 'K', 'E']);
|
|
4
6
|
function readOptionValue(argv, index, flag) {
|
|
@@ -76,3 +78,14 @@ export function resolveEntrypoint(argv, invokedPath = process.argv[1] ?? '') {
|
|
|
76
78
|
mcpStartupDefaults: Object.freeze({ ...defaults }),
|
|
77
79
|
};
|
|
78
80
|
}
|
|
81
|
+
export function isDirectExecution(importMetaUrl, invokedPath = process.argv[1] ?? '') {
|
|
82
|
+
if (!invokedPath) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
return realpathSync(fileURLToPath(importMetaUrl)) === realpathSync(invokedPath);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
package/dist/loader.js
CHANGED
|
@@ -3,7 +3,7 @@ import { readFile } from 'node:fs/promises';
|
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
// Set up browser globals for astrochart library BEFORE any imports
|
|
5
5
|
import { JSDOM } from 'jsdom';
|
|
6
|
-
import { resolveEntrypoint } from './entrypoint.js';
|
|
6
|
+
import { isDirectExecution, resolveEntrypoint } from './entrypoint.js';
|
|
7
7
|
function initializeRuntime() {
|
|
8
8
|
const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>');
|
|
9
9
|
globalThis.window = dom.window;
|
|
@@ -61,7 +61,7 @@ export async function runEntrypoint(argv = process.argv.slice(2), invokedPath =
|
|
|
61
61
|
const code = await runCli(resolved.cliArgv);
|
|
62
62
|
process.exit(code);
|
|
63
63
|
}
|
|
64
|
-
if (import.meta.url
|
|
64
|
+
if (isDirectExecution(import.meta.url)) {
|
|
65
65
|
runEntrypoint().catch((error) => {
|
|
66
66
|
console.error('[ERROR] Failed to start program:', error);
|
|
67
67
|
process.exit(1);
|
package/dist/mcp-alias.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { isDirectExecution } from './entrypoint.js';
|
|
2
3
|
import { runEntrypoint } from './loader.js';
|
|
3
|
-
if (import.meta.url
|
|
4
|
+
if (isDirectExecution(import.meta.url)) {
|
|
4
5
|
runEntrypoint(['--mcp', ...process.argv.slice(2)], process.argv[1]).catch((error) => {
|
|
5
6
|
console.error('[ERROR] Failed to start program:', error);
|
|
6
7
|
process.exit(1);
|
package/package.json
CHANGED
package/src/entrypoint.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { realpathSync } from 'node:fs';
|
|
1
2
|
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
2
4
|
import { isValidTimezone } from './time-utils.js';
|
|
3
5
|
import type { HouseSystem } from './types.js';
|
|
4
6
|
|
|
@@ -116,3 +118,18 @@ export function resolveEntrypoint(
|
|
|
116
118
|
mcpStartupDefaults: Object.freeze({ ...defaults }),
|
|
117
119
|
};
|
|
118
120
|
}
|
|
121
|
+
|
|
122
|
+
export function isDirectExecution(
|
|
123
|
+
importMetaUrl: string,
|
|
124
|
+
invokedPath = process.argv[1] ?? ''
|
|
125
|
+
): boolean {
|
|
126
|
+
if (!invokedPath) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
return realpathSync(fileURLToPath(importMetaUrl)) === realpathSync(invokedPath);
|
|
132
|
+
} catch {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
}
|
package/src/loader.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { readFile } from 'node:fs/promises';
|
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
// Set up browser globals for astrochart library BEFORE any imports
|
|
6
6
|
import { JSDOM } from 'jsdom';
|
|
7
|
-
import { resolveEntrypoint } from './entrypoint.js';
|
|
7
|
+
import { isDirectExecution, resolveEntrypoint } from './entrypoint.js';
|
|
8
8
|
|
|
9
9
|
function initializeRuntime() {
|
|
10
10
|
const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>');
|
|
@@ -72,7 +72,7 @@ export async function runEntrypoint(argv = process.argv.slice(2), invokedPath =
|
|
|
72
72
|
process.exit(code);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
if (import.meta.url
|
|
75
|
+
if (isDirectExecution(import.meta.url)) {
|
|
76
76
|
runEntrypoint().catch((error) => {
|
|
77
77
|
console.error('[ERROR] Failed to start program:', error);
|
|
78
78
|
process.exit(1);
|
package/src/mcp-alias.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import { isDirectExecution } from './entrypoint.js';
|
|
3
4
|
import { runEntrypoint } from './loader.js';
|
|
4
5
|
|
|
5
|
-
if (import.meta.url
|
|
6
|
+
if (isDirectExecution(import.meta.url)) {
|
|
6
7
|
runEntrypoint(['--mcp', ...process.argv.slice(2)], process.argv[1]).catch((error) => {
|
|
7
8
|
console.error('[ERROR] Failed to start program:', error);
|
|
8
9
|
process.exit(1);
|
|
@@ -1,5 +1,56 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { existsSync, mkdtempSync, rmSync, symlinkSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { pathToFileURL } from 'node:url';
|
|
6
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
7
|
+
import { isDirectExecution, resolveEntrypoint } from '../../src/entrypoint.js';
|
|
8
|
+
|
|
9
|
+
const tempDirs: string[] = [];
|
|
10
|
+
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
while (tempDirs.length > 0) {
|
|
13
|
+
const tempDir = tempDirs.pop();
|
|
14
|
+
if (tempDir) {
|
|
15
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
function createTempDir(prefix: string): string {
|
|
21
|
+
const tempDir = mkdtempSync(path.join(tmpdir(), prefix));
|
|
22
|
+
tempDirs.push(tempDir);
|
|
23
|
+
return tempDir;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function runCommand(
|
|
27
|
+
command: string,
|
|
28
|
+
args: string[],
|
|
29
|
+
options: {
|
|
30
|
+
cwd?: string;
|
|
31
|
+
env?: NodeJS.ProcessEnv;
|
|
32
|
+
} = {}
|
|
33
|
+
) {
|
|
34
|
+
const result = spawnSync(command, args, {
|
|
35
|
+
cwd: options.cwd,
|
|
36
|
+
env: options.env,
|
|
37
|
+
encoding: 'utf8',
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
if (result.status !== 0) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
[
|
|
43
|
+
`Command failed: ${command} ${args.join(' ')}`,
|
|
44
|
+
result.stdout && `stdout:\n${result.stdout}`,
|
|
45
|
+
result.stderr && `stderr:\n${result.stderr}`,
|
|
46
|
+
]
|
|
47
|
+
.filter(Boolean)
|
|
48
|
+
.join('\n\n')
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return result.stdout;
|
|
53
|
+
}
|
|
3
54
|
|
|
4
55
|
describe('When resolving unified binary entrypoint mode', () => {
|
|
5
56
|
it('Given the e2a binary with no startup flags, then CLI mode receives the original argv', () => {
|
|
@@ -64,4 +115,52 @@ describe('When resolving unified binary entrypoint mode', () => {
|
|
|
64
115
|
resolveEntrypoint(['--mcp', 'get-next-eclipses'], '/usr/local/bin/e2a')
|
|
65
116
|
).toThrow(/Unexpected CLI arguments in MCP mode/);
|
|
66
117
|
});
|
|
118
|
+
|
|
119
|
+
it('Given a symlinked bin path, then direct execution is still detected', () => {
|
|
120
|
+
const tempDir = createTempDir('astro-entrypoint-');
|
|
121
|
+
|
|
122
|
+
const actualPath = path.join(tempDir, 'loader.js');
|
|
123
|
+
const symlinkPath = path.join(tempDir, 'e2a');
|
|
124
|
+
writeFileSync(actualPath, '#!/usr/bin/env node\n');
|
|
125
|
+
symlinkSync(actualPath, symlinkPath);
|
|
126
|
+
|
|
127
|
+
expect(isDirectExecution(pathToFileURL(actualPath).href, symlinkPath)).toBe(true);
|
|
128
|
+
expect(isDirectExecution(pathToFileURL(actualPath).href, path.join(tempDir, 'other-bin'))).toBe(
|
|
129
|
+
false
|
|
130
|
+
);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it(
|
|
134
|
+
'Given symlinked bin shims, then the built entrypoints execute end to end',
|
|
135
|
+
() => {
|
|
136
|
+
const repoRoot = process.cwd();
|
|
137
|
+
const binDir = createTempDir('astro-bin-shims-');
|
|
138
|
+
const env = process.env;
|
|
139
|
+
|
|
140
|
+
runCommand('npm', ['run', 'build'], { cwd: repoRoot, env });
|
|
141
|
+
|
|
142
|
+
const loaderPath = path.join(repoRoot, 'dist', 'loader.js');
|
|
143
|
+
const mcpAliasPath = path.join(repoRoot, 'dist', 'mcp-alias.js');
|
|
144
|
+
expect(existsSync(loaderPath)).toBe(true);
|
|
145
|
+
expect(existsSync(mcpAliasPath)).toBe(true);
|
|
146
|
+
|
|
147
|
+
const e2aBinPath = path.join(binDir, 'e2a');
|
|
148
|
+
const e2aMcpBinPath = path.join(binDir, 'e2a-mcp');
|
|
149
|
+
symlinkSync(loaderPath, e2aBinPath);
|
|
150
|
+
symlinkSync(mcpAliasPath, e2aMcpBinPath);
|
|
151
|
+
|
|
152
|
+
const e2aOutput = runCommand('node', [e2aBinPath, '--help'], {
|
|
153
|
+
cwd: repoRoot,
|
|
154
|
+
env,
|
|
155
|
+
});
|
|
156
|
+
expect(e2aOutput).toContain('Usage: e2a [options] [command]');
|
|
157
|
+
|
|
158
|
+
const e2aMcpOutput = runCommand('node', [e2aMcpBinPath, '--help'], {
|
|
159
|
+
cwd: repoRoot,
|
|
160
|
+
env,
|
|
161
|
+
});
|
|
162
|
+
expect(e2aMcpOutput).toContain('Usage: e2a-mcp [options]');
|
|
163
|
+
},
|
|
164
|
+
60000
|
|
165
|
+
);
|
|
67
166
|
});
|