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