@lunnoa/client 2.5.0 → 3.0.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/README.md +23 -0
- package/dist/cli.js +1204 -108
- package/dist/index.cjs +359 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1233 -185
- package/dist/index.d.ts +1233 -185
- package/dist/index.js +358 -40
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +215 -27
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +1010 -153
- package/dist/react.d.ts +1010 -153
- package/dist/react.js +215 -27
- package/dist/react.js.map +1 -1
- package/package.json +4 -2
- package/schemas/agent.schema.json +128 -0
- package/schemas/workflow.schema.json +156 -0
- package/skills/lunnoa-client/SKILL.md +142 -9
- package/skills/lunnoa-solution-design/SKILL.md +12 -5
package/dist/react.cjs
CHANGED
|
@@ -78,16 +78,21 @@ var LunnoaApiError = class extends Error {
|
|
|
78
78
|
method;
|
|
79
79
|
/** Path of the failed request (without the base URL). */
|
|
80
80
|
path;
|
|
81
|
+
/** Present on definition-sync 409s when the server sends the current revision. */
|
|
82
|
+
currentRevision;
|
|
81
83
|
constructor(args) {
|
|
82
84
|
const detail = extractMessage(args.body);
|
|
85
|
+
const currentRevision = extractCurrentRevision(args.body);
|
|
86
|
+
const shown = [detail, currentRevision ? `currentRevision: ${currentRevision}` : void 0].filter(Boolean).join(" ");
|
|
83
87
|
super(
|
|
84
|
-
`Lunnoa API request failed: ${args.method} ${args.path} \u2192 ${args.status}${
|
|
88
|
+
`Lunnoa API request failed: ${args.method} ${args.path} \u2192 ${args.status}${shown ? ` (${shown})` : ""}`
|
|
85
89
|
);
|
|
86
90
|
this.name = "LunnoaApiError";
|
|
87
91
|
this.status = args.status;
|
|
88
92
|
this.body = args.body;
|
|
89
93
|
this.method = args.method;
|
|
90
94
|
this.path = args.path;
|
|
95
|
+
this.currentRevision = currentRevision;
|
|
91
96
|
}
|
|
92
97
|
/** True for 401 responses (missing, revoked, or expired credential). */
|
|
93
98
|
get isUnauthorized() {
|
|
@@ -106,6 +111,11 @@ var LunnoaApiError = class extends Error {
|
|
|
106
111
|
return this.status === 429;
|
|
107
112
|
}
|
|
108
113
|
};
|
|
114
|
+
function extractCurrentRevision(body) {
|
|
115
|
+
if (!body || typeof body !== "object") return void 0;
|
|
116
|
+
const revision = body.currentRevision;
|
|
117
|
+
return typeof revision === "string" ? revision : void 0;
|
|
118
|
+
}
|
|
109
119
|
function extractMessage(body) {
|
|
110
120
|
if (body && typeof body === "object") {
|
|
111
121
|
const record = body;
|
|
@@ -582,9 +592,27 @@ var ActionsResource = class {
|
|
|
582
592
|
}
|
|
583
593
|
};
|
|
584
594
|
|
|
595
|
+
// src/define/files.ts
|
|
596
|
+
function hasFileAgentSignals(record) {
|
|
597
|
+
if (record.instructions && typeof record.instructions === "object") return true;
|
|
598
|
+
if (record.settings && typeof record.settings === "object") return true;
|
|
599
|
+
if ("workflows" in record || "agents" in record || "knowledge" in record || "variables" in record || "objects" in record || "objectTypes" in record || "mcpServers" in record || "a2aAgents" in record) {
|
|
600
|
+
return true;
|
|
601
|
+
}
|
|
602
|
+
return Array.isArray(record.tools) && record.tools.some(
|
|
603
|
+
(tool) => tool && typeof tool === "object" && !("kind" in tool) && !("use" in tool)
|
|
604
|
+
);
|
|
605
|
+
}
|
|
606
|
+
function isAgentFileShape(config) {
|
|
607
|
+
if (!config || typeof config !== "object") return false;
|
|
608
|
+
return hasFileAgentSignals(config);
|
|
609
|
+
}
|
|
610
|
+
|
|
585
611
|
// src/define/index.ts
|
|
586
612
|
var DEFINE_KINDS = {
|
|
613
|
+
connection: "connection",
|
|
587
614
|
action: "action",
|
|
615
|
+
trigger: "trigger",
|
|
588
616
|
workflow: "workflow"};
|
|
589
617
|
var PATH_CONDITIONS = [
|
|
590
618
|
"contains",
|
|
@@ -713,10 +741,24 @@ function unwrapTool(tool) {
|
|
|
713
741
|
}
|
|
714
742
|
return tool;
|
|
715
743
|
}
|
|
744
|
+
function syncFields(options) {
|
|
745
|
+
return {
|
|
746
|
+
...options?.baseRevision ? { baseRevision: options.baseRevision } : {},
|
|
747
|
+
...options?.force ? { force: true } : {}
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
function idsOf(links) {
|
|
751
|
+
if (!links) return void 0;
|
|
752
|
+
return links.map((link) => link.id);
|
|
753
|
+
}
|
|
716
754
|
function agentDefinitionToUpsert(def, options) {
|
|
755
|
+
if (isAgentFileShape(def)) {
|
|
756
|
+
return fileAgentToUpsert(def, options);
|
|
757
|
+
}
|
|
717
758
|
const tools = [];
|
|
718
759
|
const workflowIds = [...options?.workflowIds ?? []];
|
|
719
|
-
|
|
760
|
+
const authored = def;
|
|
761
|
+
for (const tool of authored.tools ?? []) {
|
|
720
762
|
const unwrapped = unwrapTool(tool);
|
|
721
763
|
if (unwrapped.kind === DEFINE_KINDS.workflow) {
|
|
722
764
|
continue;
|
|
@@ -732,17 +774,64 @@ function agentDefinitionToUpsert(def, options) {
|
|
|
732
774
|
});
|
|
733
775
|
}
|
|
734
776
|
}
|
|
777
|
+
return {
|
|
778
|
+
slug: authored.slug,
|
|
779
|
+
name: authored.name,
|
|
780
|
+
description: authored.description,
|
|
781
|
+
instructions: authored.instructions,
|
|
782
|
+
model: authored.model,
|
|
783
|
+
aiConnectionId: authored.aiConnection?.id,
|
|
784
|
+
connectionIds: authored.connections?.map((c) => c.id).filter((id) => Boolean(id)),
|
|
785
|
+
workflowIds: workflowIds.length ? workflowIds : void 0,
|
|
786
|
+
tools,
|
|
787
|
+
codeSource: options?.codeSource ?? "generated",
|
|
788
|
+
...syncFields(options)
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
function fileAgentToUpsert(def, options) {
|
|
792
|
+
if (typeof def.instructions !== "string") {
|
|
793
|
+
throw new Error(
|
|
794
|
+
"Agent instructions are still a file reference. The CLI resolves these before push."
|
|
795
|
+
);
|
|
796
|
+
}
|
|
797
|
+
const tools = [];
|
|
798
|
+
for (const tool of def.tools ?? []) {
|
|
799
|
+
const action = {
|
|
800
|
+
id: tool.id,
|
|
801
|
+
appId: tool.appId,
|
|
802
|
+
connection: tool.connection?.id ? { id: tool.connection.id } : void 0
|
|
803
|
+
};
|
|
804
|
+
tools.push({
|
|
805
|
+
key: tool.key,
|
|
806
|
+
appId: resolveActionAppId(action),
|
|
807
|
+
actionId: resolveActionId(action),
|
|
808
|
+
connectionId: tool.connection?.id,
|
|
809
|
+
name: tool.name,
|
|
810
|
+
description: tool.description,
|
|
811
|
+
input: tool.input
|
|
812
|
+
});
|
|
813
|
+
}
|
|
735
814
|
return {
|
|
736
815
|
slug: def.slug,
|
|
737
816
|
name: def.name,
|
|
738
|
-
description
|
|
817
|
+
// A file is the whole definition. Omitted description and model are empty.
|
|
818
|
+
description: def.description ?? null,
|
|
739
819
|
instructions: def.instructions,
|
|
740
|
-
model: def.model,
|
|
820
|
+
model: def.model ?? null,
|
|
741
821
|
aiConnectionId: def.aiConnection?.id,
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
822
|
+
...def.settings ? { settings: def.settings } : {},
|
|
823
|
+
...def.tools ? { tools } : {},
|
|
824
|
+
...def.workflows ? { workflows: def.workflows } : {},
|
|
825
|
+
...def.agents ? { agents: def.agents } : {},
|
|
826
|
+
...def.connections ? { connectionIds: idsOf(def.connections) } : {},
|
|
827
|
+
...def.knowledge ? { knowledgeIds: idsOf(def.knowledge) } : {},
|
|
828
|
+
...def.variables ? { variableIds: idsOf(def.variables) } : {},
|
|
829
|
+
...def.objects ? { objectIds: idsOf(def.objects) } : {},
|
|
830
|
+
...def.objectTypes ? { objectTypeIds: idsOf(def.objectTypes) } : {},
|
|
831
|
+
...def.mcpServers ? { mcpServers: def.mcpServers } : {},
|
|
832
|
+
...def.a2aAgents ? { a2aAgentIds: idsOf(def.a2aAgents) } : {},
|
|
833
|
+
codeSource: "synced",
|
|
834
|
+
...syncFields(options)
|
|
746
835
|
};
|
|
747
836
|
}
|
|
748
837
|
var AgentsResource = class {
|
|
@@ -762,15 +851,22 @@ var AgentsResource = class {
|
|
|
762
851
|
query: serializeListQuery(options)
|
|
763
852
|
});
|
|
764
853
|
}
|
|
854
|
+
/** Exported definition for `lunnoa/agents/<slug>.json`. */
|
|
855
|
+
getDefinition(slug) {
|
|
856
|
+
return this.http.get(
|
|
857
|
+
`/api/agents/slug/${encodeURIComponent(slug)}/definition`
|
|
858
|
+
);
|
|
859
|
+
}
|
|
765
860
|
/**
|
|
766
861
|
* Upsert an agent by slug inside the workspace on the API key.
|
|
767
862
|
* Prefer the CLI (`npx @lunnoa/client agents deploy`) for happy-path deploy.
|
|
768
863
|
*/
|
|
769
864
|
upsertBySlug(body, options) {
|
|
770
865
|
const payload = "kind" in body && body.kind === "agent" ? agentDefinitionToUpsert(body, options) : body;
|
|
771
|
-
return this.http.post(
|
|
772
|
-
|
|
773
|
-
|
|
866
|
+
return this.http.post(
|
|
867
|
+
"/api/agents/upsert",
|
|
868
|
+
{ body: payload }
|
|
869
|
+
);
|
|
774
870
|
}
|
|
775
871
|
};
|
|
776
872
|
|
|
@@ -1894,6 +1990,11 @@ function stepFromDefinition(step) {
|
|
|
1894
1990
|
}
|
|
1895
1991
|
function normalizeStartRunInput(input) {
|
|
1896
1992
|
if (isWorkflowDefinition(input)) {
|
|
1993
|
+
if ("format" in input && input.format === "graph") {
|
|
1994
|
+
throw new Error(
|
|
1995
|
+
"runs.start runs straight lines only. Push the graph workflow, then execute it."
|
|
1996
|
+
);
|
|
1997
|
+
}
|
|
1897
1998
|
return {
|
|
1898
1999
|
steps: input.steps.map(stepFromDefinition),
|
|
1899
2000
|
name: input.name ?? input.slug
|
|
@@ -2085,6 +2186,18 @@ var WorkflowAppsResource = class {
|
|
|
2085
2186
|
}
|
|
2086
2187
|
};
|
|
2087
2188
|
|
|
2189
|
+
// src/resources/code-sync.ts
|
|
2190
|
+
var CodeSyncResource = class {
|
|
2191
|
+
constructor(http) {
|
|
2192
|
+
this.http = http;
|
|
2193
|
+
}
|
|
2194
|
+
http;
|
|
2195
|
+
/** Workspace id and whether synced resources are locked in the UI. */
|
|
2196
|
+
context() {
|
|
2197
|
+
return this.http.get("/api/code-sync/context");
|
|
2198
|
+
}
|
|
2199
|
+
};
|
|
2200
|
+
|
|
2088
2201
|
// src/resources/workflows.ts
|
|
2089
2202
|
function triggerToUpsertInput(trigger) {
|
|
2090
2203
|
return {
|
|
@@ -2097,8 +2210,38 @@ function triggerToUpsertInput(trigger) {
|
|
|
2097
2210
|
...trigger.description ? { description: trigger.description } : {}
|
|
2098
2211
|
};
|
|
2099
2212
|
}
|
|
2213
|
+
function asAction(use) {
|
|
2214
|
+
if (use && typeof use === "object" && "kind" in use && use.kind === DEFINE_KINDS.action) {
|
|
2215
|
+
return use;
|
|
2216
|
+
}
|
|
2217
|
+
const fileUse = use;
|
|
2218
|
+
return {
|
|
2219
|
+
kind: DEFINE_KINDS.action,
|
|
2220
|
+
id: fileUse.id,
|
|
2221
|
+
appId: fileUse.appId,
|
|
2222
|
+
connection: fileUse.connection?.id ? { kind: DEFINE_KINDS.connection, id: fileUse.connection.id } : void 0
|
|
2223
|
+
};
|
|
2224
|
+
}
|
|
2225
|
+
function asTrigger(trigger) {
|
|
2226
|
+
if ("kind" in trigger && trigger.kind === DEFINE_KINDS.trigger) {
|
|
2227
|
+
return trigger;
|
|
2228
|
+
}
|
|
2229
|
+
const fileTrigger = trigger;
|
|
2230
|
+
return {
|
|
2231
|
+
kind: DEFINE_KINDS.trigger,
|
|
2232
|
+
id: fileTrigger.id,
|
|
2233
|
+
appId: fileTrigger.appId,
|
|
2234
|
+
connection: fileTrigger.connection?.id ? { kind: DEFINE_KINDS.connection, id: fileTrigger.connection.id } : void 0,
|
|
2235
|
+
input: fileTrigger.input,
|
|
2236
|
+
inputs: fileTrigger.inputs,
|
|
2237
|
+
name: fileTrigger.name,
|
|
2238
|
+
description: fileTrigger.description
|
|
2239
|
+
};
|
|
2240
|
+
}
|
|
2100
2241
|
function stepToUpsertInput(step) {
|
|
2101
|
-
const use = step.use;
|
|
2242
|
+
const use = asAction(step.use);
|
|
2243
|
+
const retry = "retrySettings" in step ? step.retrySettings : void 0;
|
|
2244
|
+
const continueOnFailure = "continueOnFailure" in step ? step.continueOnFailure : void 0;
|
|
2102
2245
|
return {
|
|
2103
2246
|
...step.key ? { key: step.key } : {},
|
|
2104
2247
|
appId: resolveActionAppId(use),
|
|
@@ -2109,25 +2252,60 @@ function stepToUpsertInput(step) {
|
|
|
2109
2252
|
...step.input ?? {}
|
|
2110
2253
|
},
|
|
2111
2254
|
name: step.name ?? use.name,
|
|
2112
|
-
description: use.description,
|
|
2255
|
+
description: step.description ?? use.description,
|
|
2256
|
+
...retry ? { retrySettings: retry } : {},
|
|
2257
|
+
...continueOnFailure ? { continueOnFailure } : {},
|
|
2113
2258
|
...step.paths?.length ? {
|
|
2114
2259
|
paths: step.paths.map((path) => ({
|
|
2115
2260
|
label: path.label,
|
|
2116
2261
|
...path.when ? { when: path.when } : {},
|
|
2117
|
-
steps: path.steps.map(
|
|
2262
|
+
steps: path.steps.map(
|
|
2263
|
+
(nested) => stepToUpsertInput(nested)
|
|
2264
|
+
)
|
|
2118
2265
|
}))
|
|
2119
2266
|
} : {}
|
|
2120
2267
|
};
|
|
2121
2268
|
}
|
|
2122
|
-
function
|
|
2269
|
+
function syncFields2(options) {
|
|
2123
2270
|
return {
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2271
|
+
...options?.baseRevision ? { baseRevision: options.baseRevision } : {},
|
|
2272
|
+
...options?.force ? { force: true } : {}
|
|
2273
|
+
};
|
|
2274
|
+
}
|
|
2275
|
+
function workflowDefinitionToUpsert(def, options) {
|
|
2276
|
+
if ("format" in def && def.format === "graph") {
|
|
2277
|
+
return {
|
|
2278
|
+
slug: def.slug,
|
|
2279
|
+
name: def.name,
|
|
2280
|
+
// A file is the whole definition: an omitted description is empty, not "leave it".
|
|
2281
|
+
description: def.description ?? null,
|
|
2282
|
+
codeSource: "synced",
|
|
2283
|
+
graph: { nodes: def.nodes, edges: def.edges ?? [] },
|
|
2284
|
+
...syncFields2(options)
|
|
2285
|
+
};
|
|
2286
|
+
}
|
|
2287
|
+
if ("format" in def && def.format === "steps") {
|
|
2288
|
+
const file = def;
|
|
2289
|
+
return {
|
|
2290
|
+
slug: file.slug,
|
|
2291
|
+
name: file.name,
|
|
2292
|
+
description: file.description ?? null,
|
|
2293
|
+
...file.trigger ? { trigger: triggerToUpsertInput(asTrigger(file.trigger)) } : {},
|
|
2294
|
+
codeSource: "synced",
|
|
2295
|
+
steps: file.steps.map((step) => stepToUpsertInput(step)),
|
|
2296
|
+
...syncFields2(options)
|
|
2297
|
+
};
|
|
2298
|
+
}
|
|
2299
|
+
const authored = def;
|
|
2300
|
+
return {
|
|
2301
|
+
slug: authored.slug,
|
|
2302
|
+
name: authored.name,
|
|
2303
|
+
description: authored.description,
|
|
2304
|
+
isActive: authored.isActive,
|
|
2305
|
+
...authored.trigger ? { trigger: triggerToUpsertInput(authored.trigger) } : {},
|
|
2306
|
+
codeSource: options?.codeSource ?? "generated",
|
|
2307
|
+
...syncFields2(options),
|
|
2308
|
+
steps: authored.steps.map((step) => stepToUpsertInput(step))
|
|
2131
2309
|
};
|
|
2132
2310
|
}
|
|
2133
2311
|
var WorkflowsResource = class {
|
|
@@ -2152,15 +2330,22 @@ var WorkflowsResource = class {
|
|
|
2152
2330
|
query: serializeListQuery(options)
|
|
2153
2331
|
});
|
|
2154
2332
|
}
|
|
2333
|
+
/** Exported definition for `lunnoa/workflows/<slug>.json`. */
|
|
2334
|
+
getDefinition(slug) {
|
|
2335
|
+
return this.http.get(
|
|
2336
|
+
`/api/workflows/slug/${encodeURIComponent(slug)}/definition`
|
|
2337
|
+
);
|
|
2338
|
+
}
|
|
2155
2339
|
/**
|
|
2156
2340
|
* Upsert a workflow by slug inside the workspace on the API key.
|
|
2157
2341
|
* Prefer the CLI (`npx @lunnoa/client workflows deploy`) for happy-path deploy.
|
|
2158
2342
|
*/
|
|
2159
|
-
upsertBySlug(body) {
|
|
2160
|
-
const payload = "kind" in body && body.kind === "workflow" ? workflowDefinitionToUpsert(body) : body;
|
|
2161
|
-
return this.http.post(
|
|
2162
|
-
|
|
2163
|
-
|
|
2343
|
+
upsertBySlug(body, options) {
|
|
2344
|
+
const payload = "kind" in body && body.kind === "workflow" ? workflowDefinitionToUpsert(body, options) : body;
|
|
2345
|
+
return this.http.post(
|
|
2346
|
+
"/api/workflows/upsert",
|
|
2347
|
+
{ body: payload }
|
|
2348
|
+
);
|
|
2164
2349
|
}
|
|
2165
2350
|
/**
|
|
2166
2351
|
* Starts a new execution of the workflow with the given trigger inputs.
|
|
@@ -2226,6 +2411,8 @@ var LunnoaClient = class {
|
|
|
2226
2411
|
aiConnections;
|
|
2227
2412
|
workflowApps;
|
|
2228
2413
|
discovery;
|
|
2414
|
+
/** Workspace id for the definitions lock, and the code-sync lock flag. */
|
|
2415
|
+
codeSync;
|
|
2229
2416
|
constructor(options) {
|
|
2230
2417
|
this.http = new HttpClient(options);
|
|
2231
2418
|
this.auth = new AuthResource(this.http, this.http.tokenStore);
|
|
@@ -2249,6 +2436,7 @@ var LunnoaClient = class {
|
|
|
2249
2436
|
this.connections = new ConnectionsResource(this.http);
|
|
2250
2437
|
this.aiConnections = new AiConnectionsResource(this.http);
|
|
2251
2438
|
this.discovery = new DiscoveryResource(this.http);
|
|
2439
|
+
this.codeSync = new CodeSyncResource(this.http);
|
|
2252
2440
|
}
|
|
2253
2441
|
};
|
|
2254
2442
|
|