@xenosystem/acp 0.1.1 → 0.2.0
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/entry-guard.d.ts +47 -0
- package/dist/entry-guard.d.ts.map +1 -0
- package/dist/entry-guard.js +61 -0
- package/dist/entry-guard.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -4
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Is this module the program the user actually ran?
|
|
3
|
+
*
|
|
4
|
+
* ## Why this exists
|
|
5
|
+
*
|
|
6
|
+
* This file's entry used to decide with:
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* import.meta.url === pathToFileURL(process.argv[1]).href
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* Neither `pathToFileURL` nor `path.resolve` follows symlinks. `npm install -g`
|
|
13
|
+
* installs each bin as a SYMLINK on Linux and macOS (`<prefix>/bin/xeno-acp` →
|
|
14
|
+
* the package's real entry), and Node sets `process.argv[1]` to the path the user
|
|
15
|
+
* actually typed. So the comparison put the symlink against the real file, never
|
|
16
|
+
* matched, and the gateway exited 0 without starting — silently.
|
|
17
|
+
*
|
|
18
|
+
* It works on Windows, because npm writes a `.cmd` shim that calls
|
|
19
|
+
* `node "<real path>"`, so `argv[1]` IS the module path. **A Windows-only
|
|
20
|
+
* development machine cannot reproduce it.** The identical defect shipped in
|
|
21
|
+
* `xeno-agent-cli` 0.5.17 and disabled that CLI entirely on Linux and macOS;
|
|
22
|
+
* this is the same idiom in a second published product, found by sweeping for it
|
|
23
|
+
* rather than by another user report.
|
|
24
|
+
*
|
|
25
|
+
* ## The two rules
|
|
26
|
+
*
|
|
27
|
+
* 1. **Compare REAL paths** — `realpath` on both sides.
|
|
28
|
+
* 2. **A failure to resolve falls back to the LEXICAL comparison, never to
|
|
29
|
+
* `false`.** Answering false on error reproduces the same silent no-op through
|
|
30
|
+
* a different door.
|
|
31
|
+
*
|
|
32
|
+
* `realpath` is injected so the decision is testable on every platform: creating
|
|
33
|
+
* a symlink on Windows needs elevation or developer mode, so a test that built a
|
|
34
|
+
* real one would be SKIPPED on the platform where this bug is invisible.
|
|
35
|
+
*/
|
|
36
|
+
export interface EntryGuardOptions {
|
|
37
|
+
/** Injected for tests. Throws or returns its input on failure — never lies. */
|
|
38
|
+
realpath?: (path: string) => string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* True when `moduleUrl` is the entry Node was asked to run.
|
|
42
|
+
*
|
|
43
|
+
* @param moduleUrl the caller's `import.meta.url`
|
|
44
|
+
* @param argv1 `process.argv[1]`, or undefined when Node was given no script
|
|
45
|
+
*/
|
|
46
|
+
export declare function isProgramEntry(moduleUrl: string, argv1: string | undefined, options?: EntryGuardOptions): boolean;
|
|
47
|
+
//# sourceMappingURL=entry-guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entry-guard.d.ts","sourceRoot":"","sources":["../src/entry-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAMH,MAAM,WAAW,iBAAiB;IAChC,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACrC;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAeT"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Is this module the program the user actually ran?
|
|
3
|
+
*
|
|
4
|
+
* ## Why this exists
|
|
5
|
+
*
|
|
6
|
+
* This file's entry used to decide with:
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* import.meta.url === pathToFileURL(process.argv[1]).href
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* Neither `pathToFileURL` nor `path.resolve` follows symlinks. `npm install -g`
|
|
13
|
+
* installs each bin as a SYMLINK on Linux and macOS (`<prefix>/bin/xeno-acp` →
|
|
14
|
+
* the package's real entry), and Node sets `process.argv[1]` to the path the user
|
|
15
|
+
* actually typed. So the comparison put the symlink against the real file, never
|
|
16
|
+
* matched, and the gateway exited 0 without starting — silently.
|
|
17
|
+
*
|
|
18
|
+
* It works on Windows, because npm writes a `.cmd` shim that calls
|
|
19
|
+
* `node "<real path>"`, so `argv[1]` IS the module path. **A Windows-only
|
|
20
|
+
* development machine cannot reproduce it.** The identical defect shipped in
|
|
21
|
+
* `xeno-agent-cli` 0.5.17 and disabled that CLI entirely on Linux and macOS;
|
|
22
|
+
* this is the same idiom in a second published product, found by sweeping for it
|
|
23
|
+
* rather than by another user report.
|
|
24
|
+
*
|
|
25
|
+
* ## The two rules
|
|
26
|
+
*
|
|
27
|
+
* 1. **Compare REAL paths** — `realpath` on both sides.
|
|
28
|
+
* 2. **A failure to resolve falls back to the LEXICAL comparison, never to
|
|
29
|
+
* `false`.** Answering false on error reproduces the same silent no-op through
|
|
30
|
+
* a different door.
|
|
31
|
+
*
|
|
32
|
+
* `realpath` is injected so the decision is testable on every platform: creating
|
|
33
|
+
* a symlink on Windows needs elevation or developer mode, so a test that built a
|
|
34
|
+
* real one would be SKIPPED on the platform where this bug is invisible.
|
|
35
|
+
*/
|
|
36
|
+
import { realpathSync } from "node:fs";
|
|
37
|
+
import { resolve } from "node:path";
|
|
38
|
+
import { fileURLToPath } from "node:url";
|
|
39
|
+
/**
|
|
40
|
+
* True when `moduleUrl` is the entry Node was asked to run.
|
|
41
|
+
*
|
|
42
|
+
* @param moduleUrl the caller's `import.meta.url`
|
|
43
|
+
* @param argv1 `process.argv[1]`, or undefined when Node was given no script
|
|
44
|
+
*/
|
|
45
|
+
export function isProgramEntry(moduleUrl, argv1, options = {}) {
|
|
46
|
+
if (!argv1)
|
|
47
|
+
return false;
|
|
48
|
+
const modulePath = resolve(fileURLToPath(moduleUrl));
|
|
49
|
+
const invokedPath = resolve(argv1);
|
|
50
|
+
// Computed first, so an unresolvable path degrades to the previous behaviour
|
|
51
|
+
// rather than to silence.
|
|
52
|
+
const lexicallyEqual = invokedPath === modulePath;
|
|
53
|
+
const realpath = options.realpath ?? realpathSync;
|
|
54
|
+
try {
|
|
55
|
+
return realpath(invokedPath) === realpath(modulePath);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return lexicallyEqual;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=entry-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entry-guard.js","sourceRoot":"","sources":["../src/entry-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAOzC;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAC5B,SAAiB,EACjB,KAAyB,EACzB,UAA6B,EAAE;IAE/B,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IAEzB,MAAM,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC;IACrD,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IACnC,6EAA6E;IAC7E,0BAA0B;IAC1B,MAAM,cAAc,GAAG,WAAW,KAAK,UAAU,CAAC;IAElD,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,YAAY,CAAC;IAClD,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,WAAW,CAAC,KAAK,QAAQ,CAAC,UAAU,CAAC,CAAC;IACxD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,cAAc,CAAC;IACxB,CAAC;AACH,CAAC"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;GAQG;AAYH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG1C,eAAO,MAAM,OAAO,UAAU,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -9,15 +9,16 @@
|
|
|
9
9
|
* Env overrides: XENO_ACP_CONFIG, HOST, PORT, XENO_ACP_LOG.
|
|
10
10
|
*/
|
|
11
11
|
import { copyFileSync, existsSync } from "node:fs";
|
|
12
|
+
import { isProgramEntry } from "./entry-guard.js";
|
|
12
13
|
import { resolve } from "node:path";
|
|
13
|
-
import { fileURLToPath
|
|
14
|
+
import { fileURLToPath } from "node:url";
|
|
14
15
|
import { loadConfig, createLogger } from "@xenosystem/acp-core";
|
|
15
16
|
import { buildServer } from "./server.js";
|
|
16
17
|
// Re-export the pure app factory so tests/embedders can build a server without
|
|
17
18
|
// starting one (importing this module must have no side effects).
|
|
18
19
|
export { buildServer } from "./server.js";
|
|
19
20
|
const log = createLogger("server");
|
|
20
|
-
export const VERSION = "0.
|
|
21
|
+
export const VERSION = "0.2.0";
|
|
21
22
|
function usage() {
|
|
22
23
|
return [
|
|
23
24
|
`xeno-acp ${VERSION} — OpenAI-compatible ACP gateway`,
|
|
@@ -89,8 +90,12 @@ async function main() {
|
|
|
89
90
|
}
|
|
90
91
|
// Only auto-start when invoked as the entry point (e.g. `node dist/index.js` or the bin).
|
|
91
92
|
// Importing this module (tests, embedders) must not start a server.
|
|
92
|
-
|
|
93
|
-
|
|
93
|
+
//
|
|
94
|
+
// Symlink-aware — see `entry-guard.ts`. This compared `import.meta.url` against
|
|
95
|
+
// `pathToFileURL(argv[1])`, which does not resolve symlinks, so the globally
|
|
96
|
+
// installed `xeno-acp` bin never matched on Linux or macOS and the gateway
|
|
97
|
+
// exited 0 without starting.
|
|
98
|
+
if (isProgramEntry(import.meta.url, process.argv[1])) {
|
|
94
99
|
main().catch((err) => {
|
|
95
100
|
log.error("fatal:", err);
|
|
96
101
|
process.exit(1);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;GAQG;AAEH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xenosystem/acp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "OpenAI-compatible HTTP gateway for xeno-acp: drive any ACP agent via /v1/chat/completions.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"fastify": "^5.1.0",
|
|
43
|
-
"@xenosystem/acp-core": "^0.
|
|
43
|
+
"@xenosystem/acp-core": "^0.2.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@types/node": "^22.9.0",
|