n8n-nodes-lifespace 0.1.22 → 0.1.24
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 +24 -15
- package/dist/credentials/LifeSpaceAgentExecutionApi.credentials.d.ts +13 -0
- package/dist/credentials/LifeSpaceAgentExecutionApi.credentials.js +70 -0
- package/dist/credentials/LifeSpaceAgentExecutionApi.credentials.js.map +1 -0
- package/dist/nodes/LifeSpace/LifeSpace.node.js +172 -9
- package/dist/nodes/LifeSpace/LifeSpace.node.js.map +1 -1
- package/dist/nodes/LifeSpaceTool/LifeSpaceTool.node.js +318 -29
- package/dist/nodes/LifeSpaceTool/LifeSpaceTool.node.js.map +1 -1
- package/dist/nodes/agent/lifeSpaceToolFactory.d.ts +1 -1
- package/dist/nodes/agent/lifeSpaceToolFactory.js +41 -0
- package/dist/nodes/agent/lifeSpaceToolFactory.js.map +1 -1
- package/dist/nodes/lifespaceDiscovery.d.ts +10 -5
- package/dist/nodes/lifespaceDiscovery.js +33 -24
- package/dist/nodes/lifespaceDiscovery.js.map +1 -1
- package/dist/nodes/shared/lifeSpaceExecutionAuthority.d.ts +25 -0
- package/dist/nodes/shared/lifeSpaceExecutionAuthority.js +127 -0
- package/dist/nodes/shared/lifeSpaceExecutionAuthority.js.map +1 -0
- package/dist/package.json +2 -2
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -44,25 +44,26 @@ Restart n8n afterwards. In queue mode, install the package anywhere that may exe
|
|
|
44
44
|
|
|
45
45
|
## Quick start
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
LifeSpace has two explicit execution modes in n8n.
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
For **Service Principal(服务主体)automation**, create a **LifeSpace API** credential with the Core **API Base URL** and an opaque `lsp_pat_*` **Service API Token**. This remains the default for scheduled/system workflows where `Principal = Actor = Service`.
|
|
50
50
|
|
|
51
|
-
- **
|
|
51
|
+
For a user-initiated **Delegated Agent(委托智能体)** Tool or Batch, create a separate **LifeSpace Delegated Agent API** credential containing:
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
- **Core API Base URL**;
|
|
54
|
+
- **Identity API Base URL**;
|
|
55
|
+
- the server-only Application credential `lsa_*`;
|
|
56
|
+
- the registered LifeSpace `agt_*` Agent ID.
|
|
56
57
|
|
|
57
|
-
|
|
58
|
+
Delegated mode does **not** require or fall back to a Service PAT. The adapter uses the Application credential only to mint a short-lived `User Principal → Agent Actor` execution token from LifeSpace Identity, then calls Core with that token plus explicit Delegation selectors.
|
|
58
59
|
|
|
59
|
-
|
|
60
|
+
`Principal User ID`, `Delegation ID`, `Read Delegation ID` and per-item Batch Delegation IDs are execution/control context, never LLM Tool arguments. `Read Delegation ID` is intentionally separate: it covers delegated design-time Discovery and only the runtime calls that actually need relation-name lookup or optimistic-concurrency pre-reads, so those helper reads cannot accidentally consume a single-use Delegation reserved for the actual query/mutation/action. A pure runtime mutation with no helper read does not exercise the Read Delegation.
|
|
60
61
|
|
|
61
|
-
|
|
62
|
+
Credentials are secrets. Store them only in n8n credentials; do not put `lsp_pat_*`, `lsa_*`, Agent JWTs or other secrets in workflow fields, URLs, source files or workflow exports.
|
|
62
63
|
|
|
63
64
|
A **LifeSpace Trigger** additionally uses a **LifeSpace Webhook Signing** credential containing the endpoint-scoped HMAC signing secret. This is deliberately separate from the API credential: the Service API Token authenticates outbound n8n → LifeSpace calls, while the signing secret verifies inbound LifeSpace → n8n deliveries and rotates with its Webhook Endpoint. The Trigger still reuses the LifeSpace API credential for Space/Record Type discovery, so API context is not duplicated.
|
|
64
65
|
|
|
65
|
-
Runtime Discovery determines which Spaces, Record Types, fields, queries, Actions and relation lookup capabilities the current
|
|
66
|
+
Runtime Discovery determines which Spaces, Record Types, fields, queries, Actions and relation lookup capabilities the current execution context can use. Service mode uses the Service PAT. Delegated Agent mode uses the short-lived Agent token; progressive semantic detail uses the configured Read Delegation selector. Relation target lookup remains lazy and field-scoped. Execution authorization is always enforced by LifeSpace from verified Application / Principal / Actor, credential scope, Application × Model Access, current Space/Data Grant authority and the explicitly selected Delegation when Principal and Actor differ.
|
|
66
67
|
|
|
67
68
|
## Generated Record UX
|
|
68
69
|
|
|
@@ -89,7 +90,7 @@ Current Calendar models expose the canonical `capabilityBindings.calendar.rangeF
|
|
|
89
90
|
|
|
90
91
|
## LifeSpace contract compatibility
|
|
91
92
|
|
|
92
|
-
This package follows the current LifeSpace Core Kernel `0.
|
|
93
|
+
This package follows the current LifeSpace Core Kernel `0.40.0` contract family. Integration/Eventing is independently versioned at `0.3.0`. Authority v2 requires an explicit Delegation selector whenever `Principal != Actor`, and Model Contracts expose the first-class atomic Generic Runtime Batch surface.
|
|
93
94
|
|
|
94
95
|
The UX depends on these Kernel capabilities:
|
|
95
96
|
|
|
@@ -112,6 +113,7 @@ The UX depends on these Kernel capabilities:
|
|
|
112
113
|
- `0.34.0`: explicit `eq/lt/lte/gt/gte` comparison transports, first-class `createdAt` / `updatedAt` envelope comparisons and Core-owned datetime local-date-window conversion become discoverable.
|
|
113
114
|
- `0.35.0`: grouped `query.capabilityQueries` introduced capability-owned compatibility queries.
|
|
114
115
|
- `0.36.0`: `query.canonical` unifies Search, typed Boolean Filter, multi-Sort and cursor Pagination behind `POST .../records/query`; Runtime Discovery also exposes canonical `instant`, `range<date>`, `range<instant>`, `temporal_range` field types and the Calendar `rangeField` binding. The human Workflow node consumes those current semantics while retaining historical Discovery spellings only for compatibility.
|
|
116
|
+
- `0.40.0`: Authority v2 separates verified Application / Principal / Actor from the explicit opaque Delegation selector; one Delegation has one Space/Model/Record scope; first-class `POST /spaces/{spaceId}/models/batch` atomically commits 1-20 create/update/delete operations and returns one `changeSetId`.
|
|
115
117
|
|
|
116
118
|
The adapter prefers the `0.27+` progressive flow while configuring a node:
|
|
117
119
|
|
|
@@ -181,7 +183,11 @@ Credentials are intentionally static secure configuration and are not workflow-e
|
|
|
181
183
|
|
|
182
184
|
The normal n8n-facing resource is **Record**. LifeSpace still owns **Model** semantics internally; the adapter uses **Record Type** for the workflow-facing selection.
|
|
183
185
|
|
|
184
|
-
Supported operations
|
|
186
|
+
Supported human Workflow operations include ordinary Record CRUD/Query/Action plus **Batch Mutation**. Batch Mutation accepts 1-20 create/update/delete operations, may mix Record Types in one Space, and sends exactly one atomic Core request. Update/Delete items carry their already-known optimistic-concurrency `version`; the adapter does not turn Batch into N pre-read + CRUD calls.
|
|
187
|
+
|
|
188
|
+
The native Agent Tool supports Query/Create/Update/Delete/Action plus **Create Records (Batch)** for 1-20 records of its pinned Record Type. Agent Batch Update/Delete are intentionally not synthesized in this version: automatically resolving N current versions would reintroduce an N-read waterfall. Use the human Batch surface with known versions, or add a future set-based version-resolution contract before exposing those Agent operations.
|
|
189
|
+
|
|
190
|
+
Ordinary Record operations remain:
|
|
185
191
|
|
|
186
192
|
- **Create**;
|
|
187
193
|
- **Get**;
|
|
@@ -333,13 +339,16 @@ Webhook Endpoint / Event Subscription creation is intentionally not performed by
|
|
|
333
339
|
|
|
334
340
|
## What the package provides
|
|
335
341
|
|
|
336
|
-
- **LifeSpace API** credential for
|
|
342
|
+
- **LifeSpace API** credential for Service Principal authentication and Runtime Discovery;
|
|
343
|
+
- **LifeSpace Delegated Agent API** credential for server-only Application authentication, short-lived Agent token minting, and delegated Core execution without a Service PAT;
|
|
337
344
|
- **LifeSpace Webhook Signing** credential for endpoint-scoped inbound HMAC verification;
|
|
338
|
-
- **LifeSpace** human workflow node with Discovery-driven Record operations plus advanced API Request;
|
|
339
|
-
- **LifeSpace Agent Tool** native AiTool with Discovery-driven semantic schemas;
|
|
345
|
+
- **LifeSpace** human workflow node with Discovery-driven Record operations, first-class 1-20 atomic Batch create/update/delete, plus advanced API Request;
|
|
346
|
+
- **LifeSpace Agent Tool** native AiTool with Discovery-driven semantic schemas and bounded same-model Batch Create;
|
|
340
347
|
- **LifeSpace Trigger** with signed multi-Record-Type Domain Event filtering;
|
|
341
348
|
- shared thin adapter projection from LifeSpace Runtime Discovery into human n8n controls and model-facing Tool schemas.
|
|
342
349
|
|
|
350
|
+
The human Batch surface is the generic cross-model atomic transport. The Agent Tool deliberately exposes only same-model Batch Create in this release: Agent Update/Delete continue to resolve optimistic concurrency through a read before mutation, and the adapter does not turn multiple such reads into an N+1 pseudo-Batch. Cross-model or mutation-mixed Agent Batch should be added only when its full input contract can remain deterministic and bounded without reintroducing per-item Runtime round trips.
|
|
351
|
+
|
|
343
352
|
LifeSpace remains authoritative for validation, authorization, defaults, Mutation Authority, Action semantics, query/time semantics, relation semantics and event contracts. Runtime Discovery and Relation Target Lookup are current capability/reference projections, not execution-authorization proofs.
|
|
344
353
|
|
|
345
354
|
## Architecture boundary
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
|
|
2
|
+
export declare class LifeSpaceAgentExecutionApi implements ICredentialType {
|
|
3
|
+
name: string;
|
|
4
|
+
displayName: string;
|
|
5
|
+
icon: {
|
|
6
|
+
readonly light: "file:lifespace.svg";
|
|
7
|
+
readonly dark: "file:lifespace.dark.svg";
|
|
8
|
+
};
|
|
9
|
+
documentationUrl: string;
|
|
10
|
+
properties: INodeProperties[];
|
|
11
|
+
authenticate: IAuthenticateGeneric;
|
|
12
|
+
test: ICredentialTestRequest;
|
|
13
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LifeSpaceAgentExecutionApi = void 0;
|
|
4
|
+
class LifeSpaceAgentExecutionApi {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.name = 'lifeSpaceAgentExecutionApi';
|
|
7
|
+
this.displayName = 'LifeSpace Delegated Agent API';
|
|
8
|
+
this.icon = {
|
|
9
|
+
light: 'file:lifespace.svg',
|
|
10
|
+
dark: 'file:lifespace.dark.svg',
|
|
11
|
+
};
|
|
12
|
+
this.documentationUrl = 'https://github.com/huangshirui/LifeSpace/blob/main/docs/application-authentication.md';
|
|
13
|
+
this.properties = [
|
|
14
|
+
{
|
|
15
|
+
displayName: 'Core API Base URL',
|
|
16
|
+
name: 'coreBaseUrl',
|
|
17
|
+
type: 'string',
|
|
18
|
+
default: '',
|
|
19
|
+
placeholder: 'https://core.example.com/api/v1',
|
|
20
|
+
required: true,
|
|
21
|
+
description: 'LifeSpace Core API root used by delegated Agent business requests',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
displayName: 'Identity API Base URL',
|
|
25
|
+
name: 'identityBaseUrl',
|
|
26
|
+
type: 'string',
|
|
27
|
+
default: '',
|
|
28
|
+
placeholder: 'https://identity.example.com',
|
|
29
|
+
required: true,
|
|
30
|
+
description: 'LifeSpace Identity origin. Do not include /internal/v1/agent-tokens',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
displayName: 'Application Credential',
|
|
34
|
+
name: 'applicationSecret',
|
|
35
|
+
type: 'string',
|
|
36
|
+
typeOptions: { password: true },
|
|
37
|
+
default: '',
|
|
38
|
+
placeholder: 'lsa_...',
|
|
39
|
+
required: true,
|
|
40
|
+
description: 'Server-only trusted LifeSpace Application credential used only to mint short-lived delegated Agent execution tokens',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
displayName: 'Agent ID',
|
|
44
|
+
name: 'agentId',
|
|
45
|
+
type: 'string',
|
|
46
|
+
default: '',
|
|
47
|
+
placeholder: 'agt_...',
|
|
48
|
+
required: true,
|
|
49
|
+
description: 'LifeSpace Agent identity bound to this registered Application',
|
|
50
|
+
},
|
|
51
|
+
];
|
|
52
|
+
this.authenticate = {
|
|
53
|
+
type: 'generic',
|
|
54
|
+
properties: {
|
|
55
|
+
headers: {
|
|
56
|
+
Authorization: '=Bearer {{$credentials.applicationSecret}}',
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
this.test = {
|
|
61
|
+
request: {
|
|
62
|
+
baseURL: '={{$credentials.identityBaseUrl.endsWith("/") ? $credentials.identityBaseUrl.slice(0, -1) : $credentials.identityBaseUrl}}',
|
|
63
|
+
url: '/internal/v1/agents',
|
|
64
|
+
method: 'GET',
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
exports.LifeSpaceAgentExecutionApi = LifeSpaceAgentExecutionApi;
|
|
70
|
+
//# sourceMappingURL=LifeSpaceAgentExecutionApi.credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LifeSpaceAgentExecutionApi.credentials.js","sourceRoot":"","sources":["../../credentials/LifeSpaceAgentExecutionApi.credentials.ts"],"names":[],"mappings":";;;AAOA,MAAa,0BAA0B;IAAvC;QACE,SAAI,GAAG,4BAA4B,CAAC;QAEpC,gBAAW,GAAG,+BAA+B,CAAC;QAE9C,SAAI,GAAG;YACL,KAAK,EAAE,oBAAoB;YAC3B,IAAI,EAAE,yBAAyB;SACvB,CAAC;QAEX,qBAAgB,GAAG,uFAAuF,CAAC;QAE3G,eAAU,GAAsB;YAC9B;gBACE,WAAW,EAAE,mBAAmB;gBAChC,IAAI,EAAE,aAAa;gBACnB,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,EAAE;gBACX,WAAW,EAAE,iCAAiC;gBAC9C,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE,mEAAmE;aACjF;YACD;gBACE,WAAW,EAAE,uBAAuB;gBACpC,IAAI,EAAE,iBAAiB;gBACvB,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,EAAE;gBACX,WAAW,EAAE,8BAA8B;gBAC3C,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE,qEAAqE;aACnF;YACD;gBACE,WAAW,EAAE,wBAAwB;gBACrC,IAAI,EAAE,mBAAmB;gBACzB,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAC/B,OAAO,EAAE,EAAE;gBACX,WAAW,EAAE,SAAS;gBACtB,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE,qHAAqH;aACnI;YACD;gBACE,WAAW,EAAE,UAAU;gBACvB,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,EAAE;gBACX,WAAW,EAAE,SAAS;gBACtB,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE,+DAA+D;aAC7E;SACF,CAAC;QAEF,iBAAY,GAAyB;YACnC,IAAI,EAAE,SAAS;YACf,UAAU,EAAE;gBACV,OAAO,EAAE;oBACP,aAAa,EAAE,4CAA4C;iBAC5D;aACF;SACF,CAAC;QAEF,SAAI,GAA2B;YAC7B,OAAO,EAAE;gBACP,OAAO,EAAE,4HAA4H;gBACrI,GAAG,EAAE,qBAAqB;gBAC1B,MAAM,EAAE,KAAK;aACd;SACF,CAAC;IACJ,CAAC;CAAA;AApED,gEAoEC"}
|
|
@@ -4,6 +4,7 @@ exports.LifeSpace = void 0;
|
|
|
4
4
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
5
|
const lifespaceDiscovery_1 = require("../lifespaceDiscovery");
|
|
6
6
|
const lifeSpaceActionSnapshot_1 = require("../shared/lifeSpaceActionSnapshot");
|
|
7
|
+
const lifeSpaceExecutionAuthority_1 = require("../shared/lifeSpaceExecutionAuthority");
|
|
7
8
|
const LEGACY_MODEL_ROUTE_TO_KEY = Object.freeze({
|
|
8
9
|
tasks: 'task',
|
|
9
10
|
wishes: 'wish',
|
|
@@ -86,6 +87,81 @@ function parseJsonObject(context, itemIndex, value, fieldName) {
|
|
|
86
87
|
}
|
|
87
88
|
return parsed;
|
|
88
89
|
}
|
|
90
|
+
function batchMutationOperations(context, itemIndex, value, delegated) {
|
|
91
|
+
let parsed;
|
|
92
|
+
try {
|
|
93
|
+
parsed = typeof value === 'string' ? JSON.parse(value) : value;
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
throw new n8n_workflow_1.NodeOperationError(context.getNode(), error, { itemIndex });
|
|
97
|
+
}
|
|
98
|
+
if (!Array.isArray(parsed) || parsed.length < 1 || parsed.length > 20) {
|
|
99
|
+
throw new n8n_workflow_1.NodeOperationError(context.getNode(), 'Batch Operations must be a JSON array containing 1 to 20 create/update/delete operations', { itemIndex });
|
|
100
|
+
}
|
|
101
|
+
return parsed.map((raw, index) => {
|
|
102
|
+
var _a, _b, _c, _d;
|
|
103
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
104
|
+
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Batch operation ${index} must be a JSON object`, { itemIndex });
|
|
105
|
+
}
|
|
106
|
+
const input = raw;
|
|
107
|
+
const operation = String((_a = input.operation) !== null && _a !== void 0 ? _a : '');
|
|
108
|
+
const modelKey = String((_b = input.modelKey) !== null && _b !== void 0 ? _b : '').trim();
|
|
109
|
+
if (!['create', 'update', 'delete'].includes(operation)) {
|
|
110
|
+
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Batch operation ${index} must be create, update, or delete`, { itemIndex });
|
|
111
|
+
}
|
|
112
|
+
if (!/^[A-Za-z][A-Za-z0-9_]{0,99}$/u.test(modelKey)) {
|
|
113
|
+
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Batch operation ${index} has an invalid modelKey`, { itemIndex });
|
|
114
|
+
}
|
|
115
|
+
const delegationId = String((_c = input.delegationId) !== null && _c !== void 0 ? _c : '').trim();
|
|
116
|
+
if (delegated && !/^dlg_[A-Za-z0-9_-]+$/u.test(delegationId)) {
|
|
117
|
+
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Batch operation ${index} requires its own valid delegationId in Delegated Agent mode`, { itemIndex });
|
|
118
|
+
}
|
|
119
|
+
if (!delegated && delegationId) {
|
|
120
|
+
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Batch operation ${index} cannot use delegationId in Service Principal mode`, { itemIndex });
|
|
121
|
+
}
|
|
122
|
+
if (operation === 'create') {
|
|
123
|
+
const data = input.data;
|
|
124
|
+
if (!data || typeof data !== 'object' || Array.isArray(data)) {
|
|
125
|
+
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Batch create ${index} requires an object data field`, { itemIndex });
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
operation: 'create',
|
|
129
|
+
modelKey,
|
|
130
|
+
data: data,
|
|
131
|
+
...(delegationId ? { delegationId } : {}),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
const recordId = String((_d = input.recordId) !== null && _d !== void 0 ? _d : '').trim();
|
|
135
|
+
const version = Number(input.version);
|
|
136
|
+
if (!/^rec_[A-Za-z0-9_-]+$/u.test(recordId)) {
|
|
137
|
+
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Batch operation ${index} has an invalid recordId`, { itemIndex });
|
|
138
|
+
}
|
|
139
|
+
if (!Number.isInteger(version) || version < 1) {
|
|
140
|
+
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Batch operation ${index} requires a positive integer version`, { itemIndex });
|
|
141
|
+
}
|
|
142
|
+
if (operation === 'delete') {
|
|
143
|
+
return {
|
|
144
|
+
operation: 'delete',
|
|
145
|
+
modelKey,
|
|
146
|
+
recordId,
|
|
147
|
+
version,
|
|
148
|
+
...(delegationId ? { delegationId } : {}),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
const data = input.data;
|
|
152
|
+
if (!data || typeof data !== 'object' || Array.isArray(data) || Object.keys(data).length === 0) {
|
|
153
|
+
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Batch update ${index} requires non-empty object data`, { itemIndex });
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
operation: 'update',
|
|
157
|
+
modelKey,
|
|
158
|
+
recordId,
|
|
159
|
+
version,
|
|
160
|
+
data: data,
|
|
161
|
+
...(delegationId ? { delegationId } : {}),
|
|
162
|
+
};
|
|
163
|
+
});
|
|
164
|
+
}
|
|
89
165
|
function mappedValue(context, itemIndex, parameterName) {
|
|
90
166
|
const value = context.getNodeParameter(`${parameterName}.value`, itemIndex, {});
|
|
91
167
|
return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
@@ -565,7 +641,7 @@ class LifeSpace {
|
|
|
565
641
|
},
|
|
566
642
|
group: ['transform'],
|
|
567
643
|
version: 1,
|
|
568
|
-
subtitle: '={{$parameter["resource"] === "modelRecord" ? $parameter["operation"] : "API Request"}}',
|
|
644
|
+
subtitle: '={{$parameter["resource"] === "modelRecord" ? $parameter["operation"] : ($parameter["resource"] === "batchMutation" ? "Batch Mutation" : "API Request")}}',
|
|
569
645
|
description: 'Use LifeSpace records and APIs in n8n workflows',
|
|
570
646
|
defaults: {
|
|
571
647
|
name: 'LifeSpace',
|
|
@@ -577,6 +653,22 @@ class LifeSpace {
|
|
|
577
653
|
{
|
|
578
654
|
name: 'lifeSpaceApi',
|
|
579
655
|
required: true,
|
|
656
|
+
displayOptions: {
|
|
657
|
+
hide: {
|
|
658
|
+
resource: ['batchMutation'],
|
|
659
|
+
batchAuthorityMode: ['delegatedAgent'],
|
|
660
|
+
},
|
|
661
|
+
},
|
|
662
|
+
},
|
|
663
|
+
{
|
|
664
|
+
name: 'lifeSpaceAgentExecutionApi',
|
|
665
|
+
required: true,
|
|
666
|
+
displayOptions: {
|
|
667
|
+
show: {
|
|
668
|
+
resource: ['batchMutation'],
|
|
669
|
+
batchAuthorityMode: ['delegatedAgent'],
|
|
670
|
+
},
|
|
671
|
+
},
|
|
580
672
|
},
|
|
581
673
|
],
|
|
582
674
|
properties: [
|
|
@@ -590,6 +682,10 @@ class LifeSpace {
|
|
|
590
682
|
name: 'Record',
|
|
591
683
|
value: 'modelRecord',
|
|
592
684
|
},
|
|
685
|
+
{
|
|
686
|
+
name: 'Batch Mutation',
|
|
687
|
+
value: 'batchMutation',
|
|
688
|
+
},
|
|
593
689
|
{
|
|
594
690
|
name: 'API Request',
|
|
595
691
|
value: 'apiRequest',
|
|
@@ -597,6 +693,50 @@ class LifeSpace {
|
|
|
597
693
|
],
|
|
598
694
|
default: 'modelRecord',
|
|
599
695
|
},
|
|
696
|
+
{
|
|
697
|
+
displayName: 'Batch Authority Mode',
|
|
698
|
+
name: 'batchAuthorityMode',
|
|
699
|
+
type: 'options',
|
|
700
|
+
noDataExpression: true,
|
|
701
|
+
displayOptions: { show: { resource: ['batchMutation'] } },
|
|
702
|
+
options: [
|
|
703
|
+
{
|
|
704
|
+
name: 'Service Principal',
|
|
705
|
+
value: 'service',
|
|
706
|
+
description: 'Use the existing LifeSpace Service API Token for the atomic Batch',
|
|
707
|
+
},
|
|
708
|
+
{
|
|
709
|
+
name: 'Delegated Agent',
|
|
710
|
+
value: 'delegatedAgent',
|
|
711
|
+
description: 'Mint one short-lived User Principal -> Agent Actor token; every Batch item supplies its explicit Delegation selector',
|
|
712
|
+
},
|
|
713
|
+
],
|
|
714
|
+
default: 'service',
|
|
715
|
+
},
|
|
716
|
+
{
|
|
717
|
+
displayName: 'Principal User ID',
|
|
718
|
+
name: 'batchPrincipalUserId',
|
|
719
|
+
type: 'string',
|
|
720
|
+
default: '',
|
|
721
|
+
required: true,
|
|
722
|
+
placeholder: 'usr_...',
|
|
723
|
+
displayOptions: {
|
|
724
|
+
show: {
|
|
725
|
+
resource: ['batchMutation'],
|
|
726
|
+
batchAuthorityMode: ['delegatedAgent'],
|
|
727
|
+
},
|
|
728
|
+
},
|
|
729
|
+
description: 'Execution context for delegated Batch only. This value is not part of any AI Tool input.',
|
|
730
|
+
},
|
|
731
|
+
{
|
|
732
|
+
displayName: 'Batch Operations',
|
|
733
|
+
name: 'batchOperations',
|
|
734
|
+
type: 'json',
|
|
735
|
+
default: '[]',
|
|
736
|
+
required: true,
|
|
737
|
+
displayOptions: { show: { resource: ['batchMutation'] } },
|
|
738
|
+
description: 'JSON array with 1-20 create/update/delete operations. Update/delete require recordId and version. Delegated Agent mode requires delegationId on every item. The adapter sends one atomic Core Batch request and performs no rollback logic itself.',
|
|
739
|
+
},
|
|
600
740
|
{
|
|
601
741
|
displayName: 'Operation',
|
|
602
742
|
name: 'operation',
|
|
@@ -616,7 +756,7 @@ class LifeSpace {
|
|
|
616
756
|
{
|
|
617
757
|
displayName: 'Space Name or ID', name: 'spaceId', type: 'options',
|
|
618
758
|
typeOptions: { loadOptionsMethod: 'getSpaces' }, options: [], default: '', required: true,
|
|
619
|
-
displayOptions: { show: { resource: ['modelRecord'] } },
|
|
759
|
+
displayOptions: { show: { resource: ['modelRecord', 'batchMutation'] } },
|
|
620
760
|
description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
|
|
621
761
|
},
|
|
622
762
|
{
|
|
@@ -1087,21 +1227,43 @@ class LifeSpace {
|
|
|
1087
1227
|
};
|
|
1088
1228
|
}
|
|
1089
1229
|
async execute() {
|
|
1090
|
-
var _a, _b, _c;
|
|
1230
|
+
var _a, _b, _c, _d;
|
|
1091
1231
|
const items = this.getInputData();
|
|
1092
1232
|
const output = [];
|
|
1093
|
-
const credentials = await this.getCredentials('lifeSpaceApi');
|
|
1094
|
-
const baseUrl = (0, lifespaceDiscovery_1.normalizeBaseUrl)(credentials.baseUrl);
|
|
1095
1233
|
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
|
|
1096
1234
|
try {
|
|
1097
1235
|
const resource = this.getNodeParameter('resource', itemIndex);
|
|
1098
1236
|
let response;
|
|
1099
|
-
if (resource === '
|
|
1237
|
+
if (resource === 'batchMutation') {
|
|
1238
|
+
const rawSpaceId = String(this.getNodeParameter('spaceId', itemIndex)).trim();
|
|
1239
|
+
if (!rawSpaceId) {
|
|
1240
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'LifeSpace Batch Mutation requires a Space ID', { itemIndex });
|
|
1241
|
+
}
|
|
1242
|
+
const batchAuthorityMode = String((_a = this.getNodeParameter('batchAuthorityMode', itemIndex, 'service')) !== null && _a !== void 0 ? _a : 'service');
|
|
1243
|
+
const baseUrl = batchAuthorityMode === 'delegatedAgent'
|
|
1244
|
+
? (0, lifeSpaceExecutionAuthority_1.delegatedAgentCoreBaseUrl)(this, await this.getCredentials('lifeSpaceAgentExecutionApi', itemIndex))
|
|
1245
|
+
: (0, lifespaceDiscovery_1.normalizeBaseUrl)((await this.getCredentials('lifeSpaceApi', itemIndex)).baseUrl);
|
|
1246
|
+
const operations = batchMutationOperations(this, itemIndex, this.getNodeParameter('batchOperations', itemIndex, '[]'), batchAuthorityMode === 'delegatedAgent');
|
|
1247
|
+
const authority = await (0, lifeSpaceExecutionAuthority_1.executionAuthority)(this, itemIndex, 'write', {
|
|
1248
|
+
modeParameter: 'batchAuthorityMode',
|
|
1249
|
+
principalParameter: 'batchPrincipalUserId',
|
|
1250
|
+
delegationParameter: 'batchDelegationId',
|
|
1251
|
+
requireDelegation: false,
|
|
1252
|
+
});
|
|
1253
|
+
response = await (0, lifeSpaceExecutionAuthority_1.lifeSpaceRequest)(this, authority, {
|
|
1254
|
+
method: 'POST',
|
|
1255
|
+
url: `${baseUrl}/spaces/${encodeURIComponent(rawSpaceId)}/models/batch`,
|
|
1256
|
+
body: { operations },
|
|
1257
|
+
json: true,
|
|
1258
|
+
});
|
|
1259
|
+
}
|
|
1260
|
+
else if (resource === 'modelRecord') {
|
|
1261
|
+
const baseUrl = (0, lifespaceDiscovery_1.normalizeBaseUrl)((await this.getCredentials('lifeSpaceApi', itemIndex)).baseUrl);
|
|
1100
1262
|
const operation = this.getNodeParameter('operation', itemIndex);
|
|
1101
1263
|
const rawSpaceId = String(this.getNodeParameter('spaceId', itemIndex));
|
|
1102
1264
|
const recordType = (0, lifespaceDiscovery_1.decodeRecordTypeSelector)(this.getNodeParameter('recordType', itemIndex, ''));
|
|
1103
|
-
const legacyModelRoute = String((
|
|
1104
|
-
const rawModelKey = (
|
|
1265
|
+
const legacyModelRoute = String((_b = this.getNodeParameter('modelRoute', itemIndex, '')) !== null && _b !== void 0 ? _b : '').trim();
|
|
1266
|
+
const rawModelKey = (_c = recordType === null || recordType === void 0 ? void 0 : recordType.modelKey) !== null && _c !== void 0 ? _c : LEGACY_MODEL_ROUTE_TO_KEY[legacyModelRoute];
|
|
1105
1267
|
if (!rawModelKey) {
|
|
1106
1268
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'LifeSpace Record Type selector is invalid. Choose a Record Type from Discovery or pass a Trigger recordType value.', { itemIndex });
|
|
1107
1269
|
}
|
|
@@ -1110,7 +1272,7 @@ class LifeSpace {
|
|
|
1110
1272
|
const collectionPath = `/spaces/${spaceId}/models/${modelKey}/records`;
|
|
1111
1273
|
if (operation === 'list') {
|
|
1112
1274
|
const returnAll = this.getNodeParameter('returnAll', itemIndex, false);
|
|
1113
|
-
const canonical = String((
|
|
1275
|
+
const canonical = String((_d = this.getNodeParameter('queryMode', itemIndex, '')) !== null && _d !== void 0 ? _d : '') === 'canonical';
|
|
1114
1276
|
const queryPageRequest = (limit, cursor) => {
|
|
1115
1277
|
if (canonical) {
|
|
1116
1278
|
return {
|
|
@@ -1203,6 +1365,7 @@ class LifeSpace {
|
|
|
1203
1365
|
}
|
|
1204
1366
|
}
|
|
1205
1367
|
else {
|
|
1368
|
+
const baseUrl = (0, lifespaceDiscovery_1.normalizeBaseUrl)((await this.getCredentials('lifeSpaceApi', itemIndex)).baseUrl);
|
|
1206
1369
|
const method = this.getNodeParameter('method', itemIndex);
|
|
1207
1370
|
const path = String(this.getNodeParameter('path', itemIndex));
|
|
1208
1371
|
const options = {
|