@codemation/core-nodes 0.9.0 → 0.10.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/CHANGELOG.md +45 -0
- package/dist/index.cjs +140 -48
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +55 -16
- package/dist/index.d.ts +55 -16
- package/dist/index.js +140 -49
- package/dist/index.js.map +1 -1
- package/dist/metadata.json +13 -1
- package/package.json +2 -2
- package/src/chatModels/CodemationChatModelFactory.ts +2 -61
- package/src/chatModels/ManagedHmacSignerFactory.types.ts +88 -0
- package/src/index.ts +1 -0
- package/src/nodes/AIAgentNode.ts +14 -4
- package/src/nodes/codemationDocumentScannerNode.ts +131 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,50 @@
|
|
|
1
1
|
# @codemation/core-nodes
|
|
2
2
|
|
|
3
|
+
## 0.10.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [[`2fcb715`](https://github.com/MadeRelevant/codemation/commit/2fcb7153d9c732b2f846b8a8d1cc5626b4363fa6)]:
|
|
8
|
+
- @codemation/core@0.13.1
|
|
9
|
+
|
|
10
|
+
## 0.10.0
|
|
11
|
+
|
|
12
|
+
### Minor Changes
|
|
13
|
+
|
|
14
|
+
- [#184](https://github.com/MadeRelevant/codemation/pull/184) [`8c5b213`](https://github.com/MadeRelevant/codemation/commit/8c5b213092501bb48aa0c575b0489bbd36b46c79) Thanks [@cblokland90](https://github.com/cblokland90)! - Add `CodemationDocumentScanner` node — managed document/image analysis returning `{ markdown, fields }` via the Codemation doc-scanner service, no Azure credential required.
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- [#187](https://github.com/MadeRelevant/codemation/pull/187) [`cf45e88`](https://github.com/MadeRelevant/codemation/commit/cf45e88c28dcb3ba87bcf14e34c550ae0ac036e8) Thanks [@cblokland90](https://github.com/cblokland90)! - fix(aiagent): managed structured output no longer throws "schema is not a function"
|
|
19
|
+
|
|
20
|
+
AIAgentNode passed a consumer-created Zod `outputSchema` straight to the AI SDK's
|
|
21
|
+
`Output.object` on the managed (non-OpenAI) path. Consumer workflows build that schema
|
|
22
|
+
with their own tsx-loaded Zod — a different runtime copy than the framework's Zod — so the
|
|
23
|
+
SDK threw "schema is not a function". Now the schema is converted to a plain JSON Schema via
|
|
24
|
+
the schema's own instance method (`createJsonSchemaRecord`, dual-zod safe) and wrapped with
|
|
25
|
+
`jsonSchema()` for both paths, so `Output.object` never receives a raw cross-namespace Zod.
|
|
26
|
+
|
|
27
|
+
- [#189](https://github.com/MadeRelevant/codemation/pull/189) [`c1b081f`](https://github.com/MadeRelevant/codemation/commit/c1b081ffc8b66b0c4593c94f1d57a1cdf5c41140) Thanks [@cblokland90](https://github.com/cblokland90)! - feat(core): add `getBytes`, `getText`, and `getJson<T>` to the binary API
|
|
28
|
+
|
|
29
|
+
`ExecutionBinaryService` (and `NodeBinaryAttachmentService`) now expose three read helpers:
|
|
30
|
+
- `getBytes(attachment, maxBytes?)` — bounded read into `Uint8Array` (size-checked before allocation).
|
|
31
|
+
- `getText(attachment, maxBytes?)` — bounded read + UTF-8 decode.
|
|
32
|
+
- `getJson<T>(attachment, maxBytes?)` — bounded read + decode + `JSON.parse` with a clear `SyntaxError` on invalid JSON.
|
|
33
|
+
|
|
34
|
+
The duplicate `readBinaryBody` helpers previously copied across `core-nodes` and `core-nodes-ocr` are removed; all callers now use `ctx.binary.getBytes()`. `openReadStream` remains for true streaming use cases.
|
|
35
|
+
|
|
36
|
+
- [#184](https://github.com/MadeRelevant/codemation/pull/184) [`8c5b213`](https://github.com/MadeRelevant/codemation/commit/8c5b213092501bb48aa0c575b0489bbd36b46c79) Thanks [@cblokland90](https://github.com/cblokland90)! - Extract `managedHmacFetchFactory` from `CodemationChatModelFactory` into a shared
|
|
37
|
+
`ManagedHmacSignerFactory` module (story 05, doc-scanner sprint).
|
|
38
|
+
|
|
39
|
+
The helper is a package-internal refactor: not added to the public barrel export,
|
|
40
|
+
no public API surface change — patch is correct. It adds a `signBody` option
|
|
41
|
+
(default `true`, preserving LLM-chat behaviour) that implements LD11
|
|
42
|
+
empty-body-hash signing for the upcoming `CodemationDocumentScanner` node
|
|
43
|
+
(story 06).
|
|
44
|
+
|
|
45
|
+
- Updated dependencies [[`c1b081f`](https://github.com/MadeRelevant/codemation/commit/c1b081ffc8b66b0c4593c94f1d57a1cdf5c41140), [`be520d2`](https://github.com/MadeRelevant/codemation/commit/be520d2755144a3709ecc109019b84e2c502337e)]:
|
|
46
|
+
- @codemation/core@0.13.0
|
|
47
|
+
|
|
3
48
|
## 0.9.0
|
|
4
49
|
|
|
5
50
|
### Minor Changes
|
package/dist/index.cjs
CHANGED
|
@@ -4389,6 +4389,59 @@ var OpenAiChatModelPresets = class {
|
|
|
4389
4389
|
};
|
|
4390
4390
|
const openAiChatModelPresets = new OpenAiChatModelPresets();
|
|
4391
4391
|
|
|
4392
|
+
//#endregion
|
|
4393
|
+
//#region src/chatModels/ManagedHmacSignerFactory.types.ts
|
|
4394
|
+
/**
|
|
4395
|
+
* Creates an HMAC-signing fetch wrapper that authenticates requests to
|
|
4396
|
+
* Codemation managed services (LLM broker, doc-scanner) with the
|
|
4397
|
+
* Codemation-Hmac v=1 scheme.
|
|
4398
|
+
*
|
|
4399
|
+
* Mirrors HmacRequestSigner from @codemation/host/pairing without importing
|
|
4400
|
+
* that package (which would create a circular dependency since @codemation/host
|
|
4401
|
+
* depends on @codemation/core-nodes).
|
|
4402
|
+
*
|
|
4403
|
+
* @param workspaceId - Workspace identifier injected by the CP provisioner.
|
|
4404
|
+
* @param pairingSecret - Base64-encoded 32-byte HMAC key injected by the provisioner.
|
|
4405
|
+
* @param options - Optional behaviour flags and test seams.
|
|
4406
|
+
*/
|
|
4407
|
+
function managedHmacFetchFactory(workspaceId, pairingSecret, options) {
|
|
4408
|
+
const signBody = options?.signBody ?? true;
|
|
4409
|
+
return async (input, init) => {
|
|
4410
|
+
const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
4411
|
+
const method = init?.method ?? "POST";
|
|
4412
|
+
let bodyForSigning = "";
|
|
4413
|
+
if (signBody && init?.body !== void 0 && init.body !== null) bodyForSigning = typeof init.body === "string" ? init.body : await new Response(init.body).text();
|
|
4414
|
+
const authHeader = buildHmacAuthHeader(workspaceId, pairingSecret, method, url, bodyForSigning, options);
|
|
4415
|
+
const headers = new Headers(init?.headers);
|
|
4416
|
+
headers.set("Authorization", authHeader);
|
|
4417
|
+
const outgoingBody = signBody ? bodyForSigning || init?.body : init?.body;
|
|
4418
|
+
return fetch(input, {
|
|
4419
|
+
...init,
|
|
4420
|
+
body: outgoingBody,
|
|
4421
|
+
headers
|
|
4422
|
+
});
|
|
4423
|
+
};
|
|
4424
|
+
}
|
|
4425
|
+
/**
|
|
4426
|
+
* Produces a Codemation-Hmac v=1 Authorization header value.
|
|
4427
|
+
* Algorithm must match HmacVerifier.computeSignature() in the control-plane.
|
|
4428
|
+
*/
|
|
4429
|
+
function buildHmacAuthHeader(workspaceId, pairingSecret, method, url, body, overrides) {
|
|
4430
|
+
const ts = overrides?.now ? overrides.now() : Math.floor(Date.now() / 1e3);
|
|
4431
|
+
const nonce = overrides?.nonce ? overrides.nonce() : (0, node_crypto.randomBytes)(16).toString("base64");
|
|
4432
|
+
const parsed = new URL(url);
|
|
4433
|
+
const path = (parsed.pathname + parsed.search).toLowerCase();
|
|
4434
|
+
const bodyHash = (0, node_crypto.createHash)("sha256").update(body, "utf8").digest("hex");
|
|
4435
|
+
const baseString = [
|
|
4436
|
+
method.toUpperCase(),
|
|
4437
|
+
path,
|
|
4438
|
+
ts,
|
|
4439
|
+
nonce,
|
|
4440
|
+
bodyHash
|
|
4441
|
+
].join("\n");
|
|
4442
|
+
return `Codemation-Hmac v=1,workspaceId=${workspaceId},ts=${ts},nonce=${nonce},sig=${(0, node_crypto.createHmac)("sha256", Buffer.from(pairingSecret, "base64")).update(baseString, "utf8").digest("base64")}`;
|
|
4443
|
+
}
|
|
4444
|
+
|
|
4392
4445
|
//#endregion
|
|
4393
4446
|
//#region src/chatModels/CodemationChatModelFactory.ts
|
|
4394
4447
|
let CodemationChatModelFactory = class CodemationChatModelFactory$1 {
|
|
@@ -4398,7 +4451,7 @@ let CodemationChatModelFactory = class CodemationChatModelFactory$1 {
|
|
|
4398
4451
|
const workspaceId = process.env["WORKSPACE_ID"];
|
|
4399
4452
|
const pairingSecret = process.env["WORKSPACE_PAIRING_SECRET"];
|
|
4400
4453
|
if (!workspaceId || !pairingSecret) throw new Error("Codemation managed AI not available in this environment (workspace pairing is not configured).");
|
|
4401
|
-
const hmacFetch =
|
|
4454
|
+
const hmacFetch = managedHmacFetchFactory(workspaceId, pairingSecret);
|
|
4402
4455
|
const languageModel = (0, __ai_sdk_openai.createOpenAI)({
|
|
4403
4456
|
baseURL: `${gatewayUrl}/v1`,
|
|
4404
4457
|
apiKey: "codemation-managed",
|
|
@@ -4414,52 +4467,6 @@ let CodemationChatModelFactory = class CodemationChatModelFactory$1 {
|
|
|
4414
4467
|
}
|
|
4415
4468
|
});
|
|
4416
4469
|
}
|
|
4417
|
-
/**
|
|
4418
|
-
* Creates an HMAC-signed fetch wrapper for use with AI SDK's createOpenAI.
|
|
4419
|
-
* Each call signs the request body with the workspace pairing secret so the
|
|
4420
|
-
* LLM broker can authenticate the workspace without a user-managed API key.
|
|
4421
|
-
*
|
|
4422
|
-
* Mirrors HmacRequestSigner from @codemation/host/pairing without importing
|
|
4423
|
-
* that package (which would create a circular dependency since @codemation/host
|
|
4424
|
-
* depends on @codemation/core-nodes).
|
|
4425
|
-
*/
|
|
4426
|
-
buildHmacSignedFetch(workspaceId, pairingSecret) {
|
|
4427
|
-
return async (input, init) => {
|
|
4428
|
-
const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
4429
|
-
const method = init?.method ?? "POST";
|
|
4430
|
-
let bodyString = "";
|
|
4431
|
-
if (init?.body !== void 0 && init.body !== null) if (typeof init.body === "string") bodyString = init.body;
|
|
4432
|
-
else bodyString = await new Response(init.body).text();
|
|
4433
|
-
const authHeader = this.buildHmacAuthHeader(workspaceId, pairingSecret, method, url, bodyString);
|
|
4434
|
-
const headers = new Headers(init?.headers);
|
|
4435
|
-
headers.set("Authorization", authHeader);
|
|
4436
|
-
const effectiveBody = bodyString || init?.body;
|
|
4437
|
-
return fetch(input, {
|
|
4438
|
-
...init,
|
|
4439
|
-
body: effectiveBody,
|
|
4440
|
-
headers
|
|
4441
|
-
});
|
|
4442
|
-
};
|
|
4443
|
-
}
|
|
4444
|
-
/**
|
|
4445
|
-
* Produces a Codemation-Hmac v1 Authorization header value.
|
|
4446
|
-
* The algorithm must match HmacVerifier.computeSignature() in the control-plane.
|
|
4447
|
-
*/
|
|
4448
|
-
buildHmacAuthHeader(workspaceId, pairingSecret, method, url, body) {
|
|
4449
|
-
const ts = Math.floor(Date.now() / 1e3);
|
|
4450
|
-
const nonce = (0, node_crypto.randomBytes)(16).toString("base64");
|
|
4451
|
-
const parsed = new URL(url);
|
|
4452
|
-
const path = (parsed.pathname + parsed.search).toLowerCase();
|
|
4453
|
-
const bodyHash = (0, node_crypto.createHash)("sha256").update(body, "utf8").digest("hex");
|
|
4454
|
-
const baseString = [
|
|
4455
|
-
method.toUpperCase(),
|
|
4456
|
-
path,
|
|
4457
|
-
ts,
|
|
4458
|
-
nonce,
|
|
4459
|
-
bodyHash
|
|
4460
|
-
].join("\n");
|
|
4461
|
-
return `Codemation-Hmac v=1,workspaceId=${workspaceId},ts=${ts},nonce=${nonce},sig=${(0, node_crypto.createHmac)("sha256", Buffer.from(pairingSecret, "base64")).update(baseString, "utf8").digest("base64")}`;
|
|
4462
|
-
}
|
|
4463
4470
|
};
|
|
4464
4471
|
CodemationChatModelFactory = __decorate([(0, __codemation_core.chatModel)({ packageName: "@codemation/core-nodes" })], CodemationChatModelFactory);
|
|
4465
4472
|
|
|
@@ -6815,7 +6822,11 @@ let AIAgentNode = class AIAgentNode$1 {
|
|
|
6815
6822
|
});
|
|
6816
6823
|
try {
|
|
6817
6824
|
const callOptions = this.resolveCallOptions(model, guardrails.modelInvocationOptions);
|
|
6818
|
-
const
|
|
6825
|
+
const schemaRecord = this.isZodSchema(schema) ? this.executionHelpers.createJsonSchemaRecord(schema, {
|
|
6826
|
+
schemaName: structuredOptions?.schemaName ?? "structured_output",
|
|
6827
|
+
requireObjectRoot: true
|
|
6828
|
+
}) : schema;
|
|
6829
|
+
const outputSchema = ai.Output.object({ schema: (0, ai.jsonSchema)(schemaRecord) });
|
|
6819
6830
|
const result = await (0, ai.generateText)({
|
|
6820
6831
|
model: model.languageModel,
|
|
6821
6832
|
messages: [...messages],
|
|
@@ -9051,6 +9062,86 @@ const inboxApproval = (0, __codemation_core.defineHumanApprovalNode)({
|
|
|
9051
9062
|
}
|
|
9052
9063
|
});
|
|
9053
9064
|
|
|
9065
|
+
//#endregion
|
|
9066
|
+
//#region src/nodes/codemationDocumentScannerNode.ts
|
|
9067
|
+
const ANALYZER_TYPES = [
|
|
9068
|
+
"document",
|
|
9069
|
+
"invoice",
|
|
9070
|
+
"image",
|
|
9071
|
+
"auto"
|
|
9072
|
+
];
|
|
9073
|
+
const codemationDocumentScannerNode = (0, __codemation_core.defineNode)({
|
|
9074
|
+
key: "codemation.document-scanner",
|
|
9075
|
+
title: "Codemation Document Scanner",
|
|
9076
|
+
description: "Analyzes a binary attachment (document or image) via the managed Codemation document-scanning service and returns markdown text plus structured fields. No Azure credential required — auth uses the workspace pairing secret. Enable includeConfidence to get per-field confidence scores (0–1).",
|
|
9077
|
+
icon: "lucide:scan-text",
|
|
9078
|
+
input: {
|
|
9079
|
+
binaryField: "data",
|
|
9080
|
+
analyzerType: "auto",
|
|
9081
|
+
contentType: void 0,
|
|
9082
|
+
includeConfidence: false,
|
|
9083
|
+
maxBytes: void 0
|
|
9084
|
+
},
|
|
9085
|
+
configSchema: object({
|
|
9086
|
+
binaryField: string().optional(),
|
|
9087
|
+
analyzerType: _enum(ANALYZER_TYPES).optional(),
|
|
9088
|
+
contentType: string().optional(),
|
|
9089
|
+
includeConfidence: boolean().optional(),
|
|
9090
|
+
maxBytes: number().int().positive().optional()
|
|
9091
|
+
}),
|
|
9092
|
+
inspectorSummary({ config: config$1 }) {
|
|
9093
|
+
const cfg = config$1;
|
|
9094
|
+
const rows = [{
|
|
9095
|
+
label: "Analyzer type",
|
|
9096
|
+
value: cfg.analyzerType ?? "auto"
|
|
9097
|
+
}];
|
|
9098
|
+
const binaryField = cfg.binaryField ?? "data";
|
|
9099
|
+
if (binaryField !== "data") rows.push({
|
|
9100
|
+
label: "Binary field",
|
|
9101
|
+
value: binaryField
|
|
9102
|
+
});
|
|
9103
|
+
if (cfg.includeConfidence) rows.push({
|
|
9104
|
+
label: "Confidence",
|
|
9105
|
+
value: "enabled"
|
|
9106
|
+
});
|
|
9107
|
+
if (cfg.contentType) rows.push({
|
|
9108
|
+
label: "Content type",
|
|
9109
|
+
value: cfg.contentType
|
|
9110
|
+
});
|
|
9111
|
+
return rows;
|
|
9112
|
+
},
|
|
9113
|
+
async execute({ item, ctx }, { config: rawConfig }) {
|
|
9114
|
+
const config$1 = rawConfig;
|
|
9115
|
+
const gatewayUrl = process.env["DOC_SCANNER_GATEWAY_URL"];
|
|
9116
|
+
if (!gatewayUrl) throw new Error("Codemation Document Scanner not available in this environment (DOC_SCANNER_GATEWAY_URL is not set).");
|
|
9117
|
+
const workspaceId = process.env["WORKSPACE_ID"];
|
|
9118
|
+
const pairingSecret = process.env["WORKSPACE_PAIRING_SECRET"];
|
|
9119
|
+
if (!workspaceId || !pairingSecret) throw new Error("Codemation Document Scanner not available (workspace pairing is not configured).");
|
|
9120
|
+
const binaryField = config$1.binaryField ?? "data";
|
|
9121
|
+
const attachment = item.binary?.[binaryField];
|
|
9122
|
+
if (!attachment) throw new Error(`Codemation Document Scanner: no binary attachment at key "${binaryField}".`);
|
|
9123
|
+
const body = await ctx.binary.getBytes(attachment, config$1.maxBytes);
|
|
9124
|
+
const contentType = config$1.contentType ?? attachment.mimeType ?? "application/octet-stream";
|
|
9125
|
+
const analyzerType = config$1.analyzerType ?? "auto";
|
|
9126
|
+
const confidenceSuffix = config$1.includeConfidence ?? false ? "&confidence=true" : "";
|
|
9127
|
+
const url = `${gatewayUrl}/analyze?type=${encodeURIComponent(analyzerType)}${confidenceSuffix}`;
|
|
9128
|
+
const response = await managedHmacFetchFactory(workspaceId, pairingSecret, { signBody: false })(url, {
|
|
9129
|
+
method: "POST",
|
|
9130
|
+
body: body.buffer,
|
|
9131
|
+
headers: {
|
|
9132
|
+
"Content-Type": contentType,
|
|
9133
|
+
"Content-Length": String(body.byteLength),
|
|
9134
|
+
"X-Codemation-Caller": "workflow-node"
|
|
9135
|
+
}
|
|
9136
|
+
});
|
|
9137
|
+
if (!response.ok) {
|
|
9138
|
+
const text = await response.text().catch(() => "(unreadable)");
|
|
9139
|
+
throw new Error(`Codemation Document Scanner: service responded ${response.status} ${response.statusText} — ${text}`);
|
|
9140
|
+
}
|
|
9141
|
+
return await response.json();
|
|
9142
|
+
}
|
|
9143
|
+
});
|
|
9144
|
+
|
|
9054
9145
|
//#endregion
|
|
9055
9146
|
exports.AIAgent = AIAgent;
|
|
9056
9147
|
exports.AIAgentConnectionWorkflowExpander = AIAgentConnectionWorkflowExpander;
|
|
@@ -9279,6 +9370,7 @@ exports.WorkflowChain = WorkflowChain;
|
|
|
9279
9370
|
exports.apiKeyCredentialType = apiKeyCredentialType;
|
|
9280
9371
|
exports.basicAuthCredentialType = basicAuthCredentialType;
|
|
9281
9372
|
exports.bearerTokenCredentialType = bearerTokenCredentialType;
|
|
9373
|
+
exports.codemationDocumentScannerNode = codemationDocumentScannerNode;
|
|
9282
9374
|
exports.collectionDeleteNode = collectionDeleteNode;
|
|
9283
9375
|
exports.collectionFindOneNode = collectionFindOneNode;
|
|
9284
9376
|
exports.collectionGetNode = collectionGetNode;
|