@stackfactor/agent-utils 1.2.16 → 1.2.19
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/README.md +69 -1
- package/dist/cjs/agentProto.d.ts +1 -1
- package/dist/cjs/agentProto.d.ts.map +1 -1
- package/dist/cjs/agentProto.js +6 -0
- package/dist/cjs/client.d.ts +7 -0
- package/dist/cjs/client.d.ts.map +1 -1
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/langChain.d.ts +67 -0
- package/dist/cjs/langChain.d.ts.map +1 -1
- package/dist/cjs/langChain.js +317 -34
- package/dist/cjs/serve.d.ts.map +1 -1
- package/dist/cjs/serve.js +104 -37
- package/dist/esm/agentProto.d.ts +1 -1
- package/dist/esm/agentProto.d.ts.map +1 -1
- package/dist/esm/agentProto.js +6 -0
- package/dist/esm/client.d.ts +7 -0
- package/dist/esm/client.d.ts.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/langChain.d.ts +67 -0
- package/dist/esm/langChain.d.ts.map +1 -1
- package/dist/esm/langChain.js +317 -34
- package/dist/esm/serve.d.ts.map +1 -1
- package/dist/esm/serve.js +104 -37
- package/package.json +2 -2
package/dist/esm/serve.js
CHANGED
|
@@ -14,11 +14,14 @@ const dynamicImport = new Function("specifier", "return import(specifier)");
|
|
|
14
14
|
// own fetch/SDK calls can cooperate, and we fail the check if it overruns.
|
|
15
15
|
const SELF_CHECK_TIMEOUT_MS = 20_000;
|
|
16
16
|
/**
|
|
17
|
-
* Resolves a repo-relative
|
|
17
|
+
* Resolves a repo-relative agent-module path within the app root, rejecting any
|
|
18
18
|
* path that escapes it or is absolute (the path may originate from the
|
|
19
19
|
* integration config, so treat it as untrusted). Returns null when unsafe.
|
|
20
|
+
*
|
|
21
|
+
* Guards both entry points that name a module by configuration: HealthCheck's
|
|
22
|
+
* `check_code` and Execute's `code` (a webhook's handler module).
|
|
20
23
|
*/
|
|
21
|
-
const
|
|
24
|
+
const resolveAgentModulePath = (rel) => {
|
|
22
25
|
const root = process.cwd();
|
|
23
26
|
const dest = resolve(root, rel);
|
|
24
27
|
const back = relative(root, dest);
|
|
@@ -27,6 +30,60 @@ const resolveCheckPath = (rel) => {
|
|
|
27
30
|
}
|
|
28
31
|
return dest;
|
|
29
32
|
};
|
|
33
|
+
/**
|
|
34
|
+
* Loads the function exported by a repo-relative agent module. Shared by the
|
|
35
|
+
* self-check (`check_code`) and the webhook entry point (`code`) so both get the
|
|
36
|
+
* same untrusted-path guard and the same CJS/ESM interop, and neither can drift
|
|
37
|
+
* into resolving a path the other would reject.
|
|
38
|
+
*
|
|
39
|
+
* Never throws: every failure comes back as a `reason` the caller maps to its
|
|
40
|
+
* own error shape (a CheckResult row, or a gRPC error frame).
|
|
41
|
+
*
|
|
42
|
+
* @param rel - Repo-relative module path, e.g. "src/webhooks/inbound-sms.js".
|
|
43
|
+
* @param namedExport - Preferred named export, tried after `default`/bare export.
|
|
44
|
+
*/
|
|
45
|
+
const loadAgentModule = async (rel, namedExport) => {
|
|
46
|
+
const modulePath = resolveAgentModulePath(rel);
|
|
47
|
+
if (!modulePath) {
|
|
48
|
+
return {
|
|
49
|
+
reason: "invalid-path",
|
|
50
|
+
detail: `invalid module path "${rel}"`,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
if (!existsSync(modulePath)) {
|
|
54
|
+
return {
|
|
55
|
+
reason: "not-found",
|
|
56
|
+
detail: `module "${rel}" was not found in the agent image`,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
let exported;
|
|
60
|
+
try {
|
|
61
|
+
// Real dynamic import via an indirection the TS compiler won't rewrite. A
|
|
62
|
+
// literal `import()` is downlevelled to `require()` in the CJS build, and
|
|
63
|
+
// `require()` cannot resolve a file:// URL ("Cannot find module
|
|
64
|
+
// 'file:///app/src/check.js'"). `new Function` keeps a genuine `import()` in
|
|
65
|
+
// both builds; Node resolves the file URL and loads CJS or ESM modules
|
|
66
|
+
// alike (CJS exports surface as the namespace `default`).
|
|
67
|
+
const mod = await dynamicImport(pathToFileURL(modulePath).href);
|
|
68
|
+
exported = mod?.default ?? mod;
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
return {
|
|
72
|
+
reason: "load-failed",
|
|
73
|
+
detail: String(error?.message ?? error),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const fn = typeof exported === "function"
|
|
77
|
+
? exported
|
|
78
|
+
: exported?.[namedExport] ?? exported?.default;
|
|
79
|
+
if (typeof fn !== "function") {
|
|
80
|
+
return {
|
|
81
|
+
reason: "not-a-function",
|
|
82
|
+
detail: `module "${rel}" does not export a function`,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
return { fn: fn };
|
|
86
|
+
};
|
|
30
87
|
/** Coerces whatever the agent's check returned into well-formed CheckResult[]. */
|
|
31
88
|
const normalizeChecks = (raw) => {
|
|
32
89
|
if (!Array.isArray(raw)) {
|
|
@@ -67,60 +124,41 @@ const runSelfCheck = async (checkCode, config, session) => {
|
|
|
67
124
|
},
|
|
68
125
|
];
|
|
69
126
|
}
|
|
70
|
-
const
|
|
71
|
-
if (
|
|
72
|
-
|
|
73
|
-
|
|
127
|
+
const loaded = await loadAgentModule(rel, "check");
|
|
128
|
+
if (loaded.reason) {
|
|
129
|
+
// These rows are the ones this check reported before the loader was factored
|
|
130
|
+
// out, preserved exactly: a missing or unreachable module is a non-blocking
|
|
131
|
+
// `warn` (synthetic testing simply isn't available), while a module that IS
|
|
132
|
+
// there but broken is a blocking `error` — a real defect in a live agent.
|
|
133
|
+
const rows = {
|
|
134
|
+
"invalid-path": {
|
|
74
135
|
name: "synthetic_check",
|
|
75
136
|
ok: false,
|
|
76
137
|
detail: `invalid checkCode path "${rel}"`,
|
|
77
138
|
severity: "warn",
|
|
78
139
|
},
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
if (!existsSync(modulePath)) {
|
|
82
|
-
return [
|
|
83
|
-
{
|
|
140
|
+
"not-found": {
|
|
84
141
|
name: "synthetic_check",
|
|
85
142
|
ok: false,
|
|
86
143
|
detail: `checkCode "${rel}" is configured but the module was not found in the agent image`,
|
|
87
144
|
severity: "warn",
|
|
88
145
|
},
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
let checkFn;
|
|
92
|
-
try {
|
|
93
|
-
// Real dynamic import via an indirection the TS compiler won't rewrite. A
|
|
94
|
-
// literal `import()` is downlevelled to `require()` in the CJS build, and
|
|
95
|
-
// `require()` cannot resolve a file:// URL ("Cannot find module
|
|
96
|
-
// 'file:///app/src/check.js'"). `new Function` keeps a genuine `import()` in
|
|
97
|
-
// both builds; Node resolves the file URL and loads CJS or ESM check
|
|
98
|
-
// modules alike (CJS exports surface as the namespace `default`).
|
|
99
|
-
const mod = await dynamicImport(pathToFileURL(modulePath).href);
|
|
100
|
-
const exported = mod?.default ?? mod;
|
|
101
|
-
checkFn =
|
|
102
|
-
typeof exported === "function" ? exported : exported?.check ?? exported?.default;
|
|
103
|
-
}
|
|
104
|
-
catch (error) {
|
|
105
|
-
return [
|
|
106
|
-
{
|
|
146
|
+
"load-failed": {
|
|
107
147
|
name: "self_check",
|
|
108
148
|
ok: false,
|
|
109
|
-
detail: `failed to load check module: ${
|
|
149
|
+
detail: `failed to load check module: ${loaded.detail}`,
|
|
110
150
|
severity: "error",
|
|
111
151
|
},
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
if (typeof checkFn !== "function") {
|
|
115
|
-
return [
|
|
116
|
-
{
|
|
152
|
+
"not-a-function": {
|
|
117
153
|
name: "self_check",
|
|
118
154
|
ok: false,
|
|
119
155
|
detail: "check module does not export a function",
|
|
120
156
|
severity: "error",
|
|
121
157
|
},
|
|
122
|
-
|
|
158
|
+
};
|
|
159
|
+
return [rows[loaded.reason]];
|
|
123
160
|
}
|
|
161
|
+
const checkFn = loaded.fn;
|
|
124
162
|
const abort = new AbortController();
|
|
125
163
|
const timer = setTimeout(() => abort.abort(), SELF_CHECK_TIMEOUT_MS);
|
|
126
164
|
// Bind the same per-request globals main() sees (config, request.session, …)
|
|
@@ -321,8 +359,37 @@ export const serve = (main, options = {}) => {
|
|
|
321
359
|
signal: abort.signal,
|
|
322
360
|
session,
|
|
323
361
|
};
|
|
362
|
+
// Which function actually runs. Default is the agent's own `main` — the
|
|
363
|
+
// path every caller took before `code` existed, and still the path taken
|
|
364
|
+
// whenever `code` is empty. A webhook instead names its own handler module
|
|
365
|
+
// (integration webHooks[].code), so one agent can serve many endpoints
|
|
366
|
+
// without `main` growing a routing switch. Resolved per request rather
|
|
367
|
+
// than at boot because the value is per-invocation configuration.
|
|
368
|
+
let entry = main;
|
|
369
|
+
const entryPath = String(req.code ?? "").trim();
|
|
370
|
+
if (entryPath) {
|
|
371
|
+
const loaded = await loadAgentModule(entryPath, "handler");
|
|
372
|
+
if (loaded.reason) {
|
|
373
|
+
// A named module that will not load is a deployment/config fault, not
|
|
374
|
+
// a bad request: the caller cannot fix it and retrying will not help.
|
|
375
|
+
// Report it as a result-bearing error frame (not a gRPC status) so the
|
|
376
|
+
// receiver gets the reason verbatim and can log which module failed.
|
|
377
|
+
logger.log(null, logger.levels.error, `agent Execute could not load entry module "${entryPath}" (${loaded.reason}): ${loaded.detail}`);
|
|
378
|
+
if (!abort.signal.aborted) {
|
|
379
|
+
call.write({
|
|
380
|
+
error: {
|
|
381
|
+
code: 500,
|
|
382
|
+
message: `entry module "${entryPath}" could not be loaded: ${loaded.detail}`,
|
|
383
|
+
},
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
call.end();
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
entry = loaded.fn;
|
|
390
|
+
}
|
|
324
391
|
try {
|
|
325
|
-
const result = await runWithContext(context, () =>
|
|
392
|
+
const result = await runWithContext(context, () => entry(abort.signal));
|
|
326
393
|
if (!abort.signal.aborted) {
|
|
327
394
|
call.write({
|
|
328
395
|
result: { result_json: JSON.stringify(result ?? null) },
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.2.
|
|
6
|
+
"version": "1.2.19",
|
|
7
7
|
"description": "",
|
|
8
8
|
"main": "dist/cjs/index.js",
|
|
9
9
|
"module": "dist/esm/index.js",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"work:release": "bash scripts/release.sh",
|
|
20
20
|
"release": "bash scripts/release.sh",
|
|
21
21
|
"release:fanout": "bash scripts/release-fanout.sh",
|
|
22
|
-
"ar-auth": "npm config set @stackfactor:registry https://us-central1-npm.pkg.dev/virtual-development-team/sf-devenv-npm-private/; TOKEN=$(gcloud auth print-access-token 2>/dev/null || curl -sfH 'Metadata-Flavor: Google' http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token | sed -n 's/.*\"access_token\":\"\\([^\"]*\\)\".*/\\1/p'); test -n \"$TOKEN\" || { echo 'ar-auth: no credential — run: gcloud auth login' >&2; exit 1; }; npm config set //us-central1-npm.pkg.dev/virtual-development-team/:_authToken \"$TOKEN\"; echo 'ar-auth: Artifact Registry scope + token configured'",
|
|
22
|
+
"ar-auth": "npm config set @stackfactor:registry https://us-central1-npm.pkg.dev/virtual-development-team/sf-devenv-npm-private/; TOKEN=$(gcloud auth print-access-token 2>/dev/null || curl -sfH 'Metadata-Flavor: Google' http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token | sed -n 's/.*\"access_token\":\"\\([^\"]*\\)\".*/\\1/p'); test -n \"$TOKEN\" || { echo 'ar-auth: no credential — run: gcloud auth login' >&2; exit 1; }; npm config set //us-central1-npm.pkg.dev/virtual-development-team/sf-devenv-npm-private/:_authToken \"$TOKEN\"; npm config set //us-central1-npm.pkg.dev/virtual-development-team/:_authToken \"$TOKEN\"; echo 'ar-auth: Artifact Registry scope + token configured'",
|
|
23
23
|
"reinstall": "npm run ar-auth && npm ci"
|
|
24
24
|
},
|
|
25
25
|
"repository": {
|