@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/index.cjs
CHANGED
|
@@ -28,16 +28,21 @@ var LunnoaApiError = class extends Error {
|
|
|
28
28
|
method;
|
|
29
29
|
/** Path of the failed request (without the base URL). */
|
|
30
30
|
path;
|
|
31
|
+
/** Present on definition-sync 409s when the server sends the current revision. */
|
|
32
|
+
currentRevision;
|
|
31
33
|
constructor(args) {
|
|
32
34
|
const detail = extractMessage(args.body);
|
|
35
|
+
const currentRevision = extractCurrentRevision(args.body);
|
|
36
|
+
const shown = [detail, currentRevision ? `currentRevision: ${currentRevision}` : void 0].filter(Boolean).join(" ");
|
|
33
37
|
super(
|
|
34
|
-
`Lunnoa API request failed: ${args.method} ${args.path} \u2192 ${args.status}${
|
|
38
|
+
`Lunnoa API request failed: ${args.method} ${args.path} \u2192 ${args.status}${shown ? ` (${shown})` : ""}`
|
|
35
39
|
);
|
|
36
40
|
this.name = "LunnoaApiError";
|
|
37
41
|
this.status = args.status;
|
|
38
42
|
this.body = args.body;
|
|
39
43
|
this.method = args.method;
|
|
40
44
|
this.path = args.path;
|
|
45
|
+
this.currentRevision = currentRevision;
|
|
41
46
|
}
|
|
42
47
|
/** True for 401 responses (missing, revoked, or expired credential). */
|
|
43
48
|
get isUnauthorized() {
|
|
@@ -56,6 +61,11 @@ var LunnoaApiError = class extends Error {
|
|
|
56
61
|
return this.status === 429;
|
|
57
62
|
}
|
|
58
63
|
};
|
|
64
|
+
function extractCurrentRevision(body) {
|
|
65
|
+
if (!body || typeof body !== "object") return void 0;
|
|
66
|
+
const revision = body.currentRevision;
|
|
67
|
+
return typeof revision === "string" ? revision : void 0;
|
|
68
|
+
}
|
|
59
69
|
function extractMessage(body) {
|
|
60
70
|
if (body && typeof body === "object") {
|
|
61
71
|
const record = body;
|
|
@@ -532,6 +542,139 @@ var ActionsResource = class {
|
|
|
532
542
|
}
|
|
533
543
|
};
|
|
534
544
|
|
|
545
|
+
// src/define/files.ts
|
|
546
|
+
var STEP_KEY_PATTERN = /^[A-Za-z][A-Za-z0-9_-]*$/;
|
|
547
|
+
function hasFileAgentSignals(record) {
|
|
548
|
+
if (record.instructions && typeof record.instructions === "object") return true;
|
|
549
|
+
if (record.settings && typeof record.settings === "object") return true;
|
|
550
|
+
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) {
|
|
551
|
+
return true;
|
|
552
|
+
}
|
|
553
|
+
return Array.isArray(record.tools) && record.tools.some(
|
|
554
|
+
(tool) => tool && typeof tool === "object" && !("kind" in tool) && !("use" in tool)
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
function isAgentFileInput(config) {
|
|
558
|
+
if (!config || typeof config !== "object") return false;
|
|
559
|
+
return hasFileAgentSignals(config);
|
|
560
|
+
}
|
|
561
|
+
function isAgentFileShape(config) {
|
|
562
|
+
if (!config || typeof config !== "object") return false;
|
|
563
|
+
return hasFileAgentSignals(config);
|
|
564
|
+
}
|
|
565
|
+
function acceptAgentFile(config) {
|
|
566
|
+
const slug = config.slug?.trim();
|
|
567
|
+
if (!slug) throw new Error("defineAgent requires slug");
|
|
568
|
+
const instructions = config.instructions;
|
|
569
|
+
const fileRef = instructions && typeof instructions === "object" && typeof instructions.file === "string" && instructions.file.trim().length > 0;
|
|
570
|
+
if (typeof instructions !== "string" && !fileRef) {
|
|
571
|
+
throw new Error("defineAgent requires instructions");
|
|
572
|
+
}
|
|
573
|
+
const copy = { ...config };
|
|
574
|
+
delete copy.$schema;
|
|
575
|
+
delete copy.kind;
|
|
576
|
+
return { ...copy, kind: "agent", slug, instructions };
|
|
577
|
+
}
|
|
578
|
+
function assertStepKeys(steps, seen) {
|
|
579
|
+
for (const step of steps ?? []) {
|
|
580
|
+
if (!step?.use?.id?.trim()) {
|
|
581
|
+
throw new Error("workflow step requires use.id");
|
|
582
|
+
}
|
|
583
|
+
if (step.key !== void 0) {
|
|
584
|
+
if (!STEP_KEY_PATTERN.test(step.key) || step.key === "trigger") {
|
|
585
|
+
throw new Error(
|
|
586
|
+
`Step key "${step.key}" must start with a letter, use letters, digits, - or _, and not be "trigger".`
|
|
587
|
+
);
|
|
588
|
+
}
|
|
589
|
+
if (seen.has(step.key)) {
|
|
590
|
+
throw new Error(`Step key "${step.key}" is used twice.`);
|
|
591
|
+
}
|
|
592
|
+
seen.add(step.key);
|
|
593
|
+
}
|
|
594
|
+
for (const path of step.paths ?? []) {
|
|
595
|
+
assertStepKeys(path.steps ?? [], seen);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
function acceptWorkflowStepsFile(config) {
|
|
600
|
+
const slug = config.slug?.trim();
|
|
601
|
+
if (!slug) throw new Error("defineWorkflow requires slug");
|
|
602
|
+
if (!config.steps?.length) {
|
|
603
|
+
throw new Error("defineWorkflow requires at least one step");
|
|
604
|
+
}
|
|
605
|
+
if (config.trigger && !config.trigger.id?.trim()) {
|
|
606
|
+
throw new Error("defineWorkflow trigger requires id");
|
|
607
|
+
}
|
|
608
|
+
assertStepKeys(config.steps, /* @__PURE__ */ new Set());
|
|
609
|
+
const copy = { ...config };
|
|
610
|
+
delete copy.$schema;
|
|
611
|
+
delete copy.kind;
|
|
612
|
+
return {
|
|
613
|
+
...copy,
|
|
614
|
+
kind: "workflow",
|
|
615
|
+
format: "steps",
|
|
616
|
+
slug
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
function acceptWorkflowGraphFile(config) {
|
|
620
|
+
const slug = config.slug?.trim();
|
|
621
|
+
if (!slug) throw new Error("defineWorkflow requires slug");
|
|
622
|
+
if (!Array.isArray(config.nodes) || config.nodes.length === 0) {
|
|
623
|
+
throw new Error("defineWorkflow graph requires nodes");
|
|
624
|
+
}
|
|
625
|
+
const keys = /* @__PURE__ */ new Set();
|
|
626
|
+
let triggers = 0;
|
|
627
|
+
for (const node of config.nodes) {
|
|
628
|
+
if (!node?.key || typeof node.key !== "string") {
|
|
629
|
+
throw new Error("graph node requires key");
|
|
630
|
+
}
|
|
631
|
+
if (!STEP_KEY_PATTERN.test(node.key)) {
|
|
632
|
+
throw new Error(
|
|
633
|
+
`Node key "${node.key}" must start with a letter, use letters, digits, - or _, and not be empty.`
|
|
634
|
+
);
|
|
635
|
+
}
|
|
636
|
+
if (keys.has(node.key)) {
|
|
637
|
+
throw new Error(`Node key "${node.key}" is used twice.`);
|
|
638
|
+
}
|
|
639
|
+
keys.add(node.key);
|
|
640
|
+
if (node.nodeType === "trigger") triggers += 1;
|
|
641
|
+
}
|
|
642
|
+
if (triggers !== 1) {
|
|
643
|
+
throw new Error("defineWorkflow graph requires exactly one trigger");
|
|
644
|
+
}
|
|
645
|
+
for (const edge of config.edges ?? []) {
|
|
646
|
+
if (!keys.has(edge?.source) || !keys.has(edge?.target)) {
|
|
647
|
+
throw new Error(
|
|
648
|
+
`Edge from "${edge?.source ?? ""}" to "${edge?.target ?? ""}" references an unknown key.`
|
|
649
|
+
);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
const copy = { ...config };
|
|
653
|
+
delete copy.$schema;
|
|
654
|
+
delete copy.kind;
|
|
655
|
+
return {
|
|
656
|
+
...copy,
|
|
657
|
+
kind: "workflow",
|
|
658
|
+
format: "graph",
|
|
659
|
+
slug,
|
|
660
|
+
edges: config.edges ?? []
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
function isWorkflowGraphInput(config) {
|
|
664
|
+
return !!config && typeof config === "object" && config.format === "graph";
|
|
665
|
+
}
|
|
666
|
+
function isWorkflowStepsFile(config) {
|
|
667
|
+
if (!config || typeof config !== "object") return false;
|
|
668
|
+
const record = config;
|
|
669
|
+
if (record.format === "steps") return true;
|
|
670
|
+
if (record.trigger && typeof record.trigger === "object" && record.trigger.kind !== "trigger" && typeof record.trigger.id === "string") {
|
|
671
|
+
return true;
|
|
672
|
+
}
|
|
673
|
+
return Array.isArray(record.steps) && record.steps.some(
|
|
674
|
+
(step) => step && typeof step === "object" && step.use && step.use?.kind !== "action"
|
|
675
|
+
);
|
|
676
|
+
}
|
|
677
|
+
|
|
535
678
|
// src/define/index.ts
|
|
536
679
|
var DEFINE_KINDS = {
|
|
537
680
|
connection: "connection",
|
|
@@ -601,7 +744,7 @@ function defineTrigger(config) {
|
|
|
601
744
|
}
|
|
602
745
|
var MANUAL_TRIGGER_ID = "flow-control_trigger_manual";
|
|
603
746
|
var TRIGGER_KEY = "trigger";
|
|
604
|
-
var
|
|
747
|
+
var STEP_KEY_PATTERN2 = /^[A-Za-z][A-Za-z0-9_-]*$/;
|
|
605
748
|
var STEP_REF_PATTERN = /={{step:([^}]*)}}/g;
|
|
606
749
|
function refString(key, path) {
|
|
607
750
|
const parts = [key, ...path.map(String)];
|
|
@@ -641,7 +784,7 @@ var SINGLE_REF_PATTERN = /^={{step:[^}]*}}$/;
|
|
|
641
784
|
function collectKeys(steps, seen) {
|
|
642
785
|
for (const step of steps) {
|
|
643
786
|
if (step.key !== void 0) {
|
|
644
|
-
if (!
|
|
787
|
+
if (!STEP_KEY_PATTERN2.test(step.key) || step.key === TRIGGER_KEY) {
|
|
645
788
|
throw new Error(
|
|
646
789
|
`Step key "${step.key}" must start with a letter, use letters, digits, - or _, and not be "${TRIGGER_KEY}".`
|
|
647
790
|
);
|
|
@@ -725,29 +868,43 @@ function assertStepRefs(config) {
|
|
|
725
868
|
checkChain(config.steps, /* @__PURE__ */ new Set([TRIGGER_KEY]));
|
|
726
869
|
}
|
|
727
870
|
function defineWorkflow(config) {
|
|
728
|
-
|
|
871
|
+
const record = config;
|
|
872
|
+
if (isWorkflowGraphInput(config) || Array.isArray(record.nodes) && !Array.isArray(record.steps)) {
|
|
873
|
+
return acceptWorkflowGraphFile(config);
|
|
874
|
+
}
|
|
875
|
+
if (isWorkflowStepsFile(config)) {
|
|
876
|
+
return acceptWorkflowStepsFile(config);
|
|
877
|
+
}
|
|
878
|
+
const authored = { ...config };
|
|
879
|
+
delete authored.$schema;
|
|
880
|
+
if (!authored.slug?.trim()) {
|
|
729
881
|
throw new Error("defineWorkflow requires slug");
|
|
730
882
|
}
|
|
731
|
-
if (!
|
|
883
|
+
if (!authored.steps?.length) {
|
|
732
884
|
throw new Error("defineWorkflow requires at least one step");
|
|
733
885
|
}
|
|
734
|
-
if (
|
|
735
|
-
assertTriggerConfig(
|
|
886
|
+
if (authored.trigger) {
|
|
887
|
+
assertTriggerConfig(authored.trigger);
|
|
736
888
|
}
|
|
737
|
-
assertStepRefs(
|
|
738
|
-
return { kind: DEFINE_KINDS.workflow, ...
|
|
889
|
+
assertStepRefs(authored);
|
|
890
|
+
return { kind: DEFINE_KINDS.workflow, ...authored };
|
|
739
891
|
}
|
|
740
892
|
function defineAgent(config) {
|
|
741
|
-
if (
|
|
893
|
+
if (isAgentFileInput(config)) {
|
|
894
|
+
return acceptAgentFile(config);
|
|
895
|
+
}
|
|
896
|
+
const authored = { ...config };
|
|
897
|
+
delete authored.$schema;
|
|
898
|
+
if (!authored.slug?.trim()) {
|
|
742
899
|
throw new Error("defineAgent requires slug");
|
|
743
900
|
}
|
|
744
|
-
if (!
|
|
901
|
+
if (!authored.model?.trim()) {
|
|
745
902
|
throw new Error("defineAgent requires model");
|
|
746
903
|
}
|
|
747
|
-
if (
|
|
904
|
+
if (authored.instructions == null) {
|
|
748
905
|
throw new Error("defineAgent requires instructions");
|
|
749
906
|
}
|
|
750
|
-
return { kind: DEFINE_KINDS.agent, ...
|
|
907
|
+
return { kind: DEFINE_KINDS.agent, ...authored };
|
|
751
908
|
}
|
|
752
909
|
function defineEntityType(config) {
|
|
753
910
|
if (!config.slug?.trim()) {
|
|
@@ -862,10 +1019,24 @@ function unwrapTool(tool) {
|
|
|
862
1019
|
}
|
|
863
1020
|
return tool;
|
|
864
1021
|
}
|
|
1022
|
+
function syncFields(options) {
|
|
1023
|
+
return {
|
|
1024
|
+
...options?.baseRevision ? { baseRevision: options.baseRevision } : {},
|
|
1025
|
+
...options?.force ? { force: true } : {}
|
|
1026
|
+
};
|
|
1027
|
+
}
|
|
1028
|
+
function idsOf(links) {
|
|
1029
|
+
if (!links) return void 0;
|
|
1030
|
+
return links.map((link) => link.id);
|
|
1031
|
+
}
|
|
865
1032
|
function agentDefinitionToUpsert(def, options) {
|
|
1033
|
+
if (isAgentFileShape(def)) {
|
|
1034
|
+
return fileAgentToUpsert(def, options);
|
|
1035
|
+
}
|
|
866
1036
|
const tools = [];
|
|
867
1037
|
const workflowIds = [...options?.workflowIds ?? []];
|
|
868
|
-
|
|
1038
|
+
const authored = def;
|
|
1039
|
+
for (const tool of authored.tools ?? []) {
|
|
869
1040
|
const unwrapped = unwrapTool(tool);
|
|
870
1041
|
if (unwrapped.kind === DEFINE_KINDS.workflow) {
|
|
871
1042
|
continue;
|
|
@@ -881,17 +1052,65 @@ function agentDefinitionToUpsert(def, options) {
|
|
|
881
1052
|
});
|
|
882
1053
|
}
|
|
883
1054
|
}
|
|
1055
|
+
return {
|
|
1056
|
+
slug: authored.slug,
|
|
1057
|
+
name: authored.name,
|
|
1058
|
+
description: authored.description,
|
|
1059
|
+
instructions: authored.instructions,
|
|
1060
|
+
model: authored.model,
|
|
1061
|
+
aiConnectionId: authored.aiConnection?.id,
|
|
1062
|
+
connectionIds: authored.connections?.map((c) => c.id).filter((id) => Boolean(id)),
|
|
1063
|
+
workflowIds: workflowIds.length ? workflowIds : void 0,
|
|
1064
|
+
tools,
|
|
1065
|
+
codeSource: options?.codeSource ?? "generated",
|
|
1066
|
+
...syncFields(options)
|
|
1067
|
+
};
|
|
1068
|
+
}
|
|
1069
|
+
function fileAgentToUpsert(def, options) {
|
|
1070
|
+
if (typeof def.instructions !== "string") {
|
|
1071
|
+
throw new Error(
|
|
1072
|
+
"Agent instructions are still a file reference. The CLI resolves these before push."
|
|
1073
|
+
);
|
|
1074
|
+
}
|
|
1075
|
+
const tools = [];
|
|
1076
|
+
for (const tool of def.tools ?? []) {
|
|
1077
|
+
const action = {
|
|
1078
|
+
kind: DEFINE_KINDS.action,
|
|
1079
|
+
id: tool.id,
|
|
1080
|
+
appId: tool.appId,
|
|
1081
|
+
connection: tool.connection?.id ? { kind: DEFINE_KINDS.connection, id: tool.connection.id } : void 0
|
|
1082
|
+
};
|
|
1083
|
+
tools.push({
|
|
1084
|
+
key: tool.key,
|
|
1085
|
+
appId: resolveActionAppId(action),
|
|
1086
|
+
actionId: resolveActionId(action),
|
|
1087
|
+
connectionId: tool.connection?.id,
|
|
1088
|
+
name: tool.name,
|
|
1089
|
+
description: tool.description,
|
|
1090
|
+
input: tool.input
|
|
1091
|
+
});
|
|
1092
|
+
}
|
|
884
1093
|
return {
|
|
885
1094
|
slug: def.slug,
|
|
886
1095
|
name: def.name,
|
|
887
|
-
description
|
|
1096
|
+
// A file is the whole definition. Omitted description and model are empty.
|
|
1097
|
+
description: def.description ?? null,
|
|
888
1098
|
instructions: def.instructions,
|
|
889
|
-
model: def.model,
|
|
1099
|
+
model: def.model ?? null,
|
|
890
1100
|
aiConnectionId: def.aiConnection?.id,
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
1101
|
+
...def.settings ? { settings: def.settings } : {},
|
|
1102
|
+
...def.tools ? { tools } : {},
|
|
1103
|
+
...def.workflows ? { workflows: def.workflows } : {},
|
|
1104
|
+
...def.agents ? { agents: def.agents } : {},
|
|
1105
|
+
...def.connections ? { connectionIds: idsOf(def.connections) } : {},
|
|
1106
|
+
...def.knowledge ? { knowledgeIds: idsOf(def.knowledge) } : {},
|
|
1107
|
+
...def.variables ? { variableIds: idsOf(def.variables) } : {},
|
|
1108
|
+
...def.objects ? { objectIds: idsOf(def.objects) } : {},
|
|
1109
|
+
...def.objectTypes ? { objectTypeIds: idsOf(def.objectTypes) } : {},
|
|
1110
|
+
...def.mcpServers ? { mcpServers: def.mcpServers } : {},
|
|
1111
|
+
...def.a2aAgents ? { a2aAgentIds: idsOf(def.a2aAgents) } : {},
|
|
1112
|
+
codeSource: "synced",
|
|
1113
|
+
...syncFields(options)
|
|
895
1114
|
};
|
|
896
1115
|
}
|
|
897
1116
|
var AgentsResource = class {
|
|
@@ -911,15 +1130,22 @@ var AgentsResource = class {
|
|
|
911
1130
|
query: serializeListQuery(options)
|
|
912
1131
|
});
|
|
913
1132
|
}
|
|
1133
|
+
/** Exported definition for `lunnoa/agents/<slug>.json`. */
|
|
1134
|
+
getDefinition(slug) {
|
|
1135
|
+
return this.http.get(
|
|
1136
|
+
`/api/agents/slug/${encodeURIComponent(slug)}/definition`
|
|
1137
|
+
);
|
|
1138
|
+
}
|
|
914
1139
|
/**
|
|
915
1140
|
* Upsert an agent by slug inside the workspace on the API key.
|
|
916
1141
|
* Prefer the CLI (`npx @lunnoa/client agents deploy`) for happy-path deploy.
|
|
917
1142
|
*/
|
|
918
1143
|
upsertBySlug(body, options) {
|
|
919
1144
|
const payload = "kind" in body && body.kind === "agent" ? agentDefinitionToUpsert(body, options) : body;
|
|
920
|
-
return this.http.post(
|
|
921
|
-
|
|
922
|
-
|
|
1145
|
+
return this.http.post(
|
|
1146
|
+
"/api/agents/upsert",
|
|
1147
|
+
{ body: payload }
|
|
1148
|
+
);
|
|
923
1149
|
}
|
|
924
1150
|
};
|
|
925
1151
|
|
|
@@ -2043,6 +2269,11 @@ function stepFromDefinition(step) {
|
|
|
2043
2269
|
}
|
|
2044
2270
|
function normalizeStartRunInput(input) {
|
|
2045
2271
|
if (isWorkflowDefinition(input)) {
|
|
2272
|
+
if ("format" in input && input.format === "graph") {
|
|
2273
|
+
throw new Error(
|
|
2274
|
+
"runs.start runs straight lines only. Push the graph workflow, then execute it."
|
|
2275
|
+
);
|
|
2276
|
+
}
|
|
2046
2277
|
return {
|
|
2047
2278
|
steps: input.steps.map(stepFromDefinition),
|
|
2048
2279
|
name: input.name ?? input.slug
|
|
@@ -2234,6 +2465,18 @@ var WorkflowAppsResource = class {
|
|
|
2234
2465
|
}
|
|
2235
2466
|
};
|
|
2236
2467
|
|
|
2468
|
+
// src/resources/code-sync.ts
|
|
2469
|
+
var CodeSyncResource = class {
|
|
2470
|
+
constructor(http) {
|
|
2471
|
+
this.http = http;
|
|
2472
|
+
}
|
|
2473
|
+
http;
|
|
2474
|
+
/** Workspace id and whether synced resources are locked in the UI. */
|
|
2475
|
+
context() {
|
|
2476
|
+
return this.http.get("/api/code-sync/context");
|
|
2477
|
+
}
|
|
2478
|
+
};
|
|
2479
|
+
|
|
2237
2480
|
// src/resources/workflows.ts
|
|
2238
2481
|
function triggerToUpsertInput(trigger) {
|
|
2239
2482
|
return {
|
|
@@ -2246,8 +2489,38 @@ function triggerToUpsertInput(trigger) {
|
|
|
2246
2489
|
...trigger.description ? { description: trigger.description } : {}
|
|
2247
2490
|
};
|
|
2248
2491
|
}
|
|
2492
|
+
function asAction(use) {
|
|
2493
|
+
if (use && typeof use === "object" && "kind" in use && use.kind === DEFINE_KINDS.action) {
|
|
2494
|
+
return use;
|
|
2495
|
+
}
|
|
2496
|
+
const fileUse = use;
|
|
2497
|
+
return {
|
|
2498
|
+
kind: DEFINE_KINDS.action,
|
|
2499
|
+
id: fileUse.id,
|
|
2500
|
+
appId: fileUse.appId,
|
|
2501
|
+
connection: fileUse.connection?.id ? { kind: DEFINE_KINDS.connection, id: fileUse.connection.id } : void 0
|
|
2502
|
+
};
|
|
2503
|
+
}
|
|
2504
|
+
function asTrigger(trigger) {
|
|
2505
|
+
if ("kind" in trigger && trigger.kind === DEFINE_KINDS.trigger) {
|
|
2506
|
+
return trigger;
|
|
2507
|
+
}
|
|
2508
|
+
const fileTrigger = trigger;
|
|
2509
|
+
return {
|
|
2510
|
+
kind: DEFINE_KINDS.trigger,
|
|
2511
|
+
id: fileTrigger.id,
|
|
2512
|
+
appId: fileTrigger.appId,
|
|
2513
|
+
connection: fileTrigger.connection?.id ? { kind: DEFINE_KINDS.connection, id: fileTrigger.connection.id } : void 0,
|
|
2514
|
+
input: fileTrigger.input,
|
|
2515
|
+
inputs: fileTrigger.inputs,
|
|
2516
|
+
name: fileTrigger.name,
|
|
2517
|
+
description: fileTrigger.description
|
|
2518
|
+
};
|
|
2519
|
+
}
|
|
2249
2520
|
function stepToUpsertInput(step) {
|
|
2250
|
-
const use = step.use;
|
|
2521
|
+
const use = asAction(step.use);
|
|
2522
|
+
const retry = "retrySettings" in step ? step.retrySettings : void 0;
|
|
2523
|
+
const continueOnFailure = "continueOnFailure" in step ? step.continueOnFailure : void 0;
|
|
2251
2524
|
return {
|
|
2252
2525
|
...step.key ? { key: step.key } : {},
|
|
2253
2526
|
appId: resolveActionAppId(use),
|
|
@@ -2258,25 +2531,60 @@ function stepToUpsertInput(step) {
|
|
|
2258
2531
|
...step.input ?? {}
|
|
2259
2532
|
},
|
|
2260
2533
|
name: step.name ?? use.name,
|
|
2261
|
-
description: use.description,
|
|
2534
|
+
description: step.description ?? use.description,
|
|
2535
|
+
...retry ? { retrySettings: retry } : {},
|
|
2536
|
+
...continueOnFailure ? { continueOnFailure } : {},
|
|
2262
2537
|
...step.paths?.length ? {
|
|
2263
2538
|
paths: step.paths.map((path) => ({
|
|
2264
2539
|
label: path.label,
|
|
2265
2540
|
...path.when ? { when: path.when } : {},
|
|
2266
|
-
steps: path.steps.map(
|
|
2541
|
+
steps: path.steps.map(
|
|
2542
|
+
(nested) => stepToUpsertInput(nested)
|
|
2543
|
+
)
|
|
2267
2544
|
}))
|
|
2268
2545
|
} : {}
|
|
2269
2546
|
};
|
|
2270
2547
|
}
|
|
2271
|
-
function
|
|
2548
|
+
function syncFields2(options) {
|
|
2272
2549
|
return {
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2550
|
+
...options?.baseRevision ? { baseRevision: options.baseRevision } : {},
|
|
2551
|
+
...options?.force ? { force: true } : {}
|
|
2552
|
+
};
|
|
2553
|
+
}
|
|
2554
|
+
function workflowDefinitionToUpsert(def, options) {
|
|
2555
|
+
if ("format" in def && def.format === "graph") {
|
|
2556
|
+
return {
|
|
2557
|
+
slug: def.slug,
|
|
2558
|
+
name: def.name,
|
|
2559
|
+
// A file is the whole definition: an omitted description is empty, not "leave it".
|
|
2560
|
+
description: def.description ?? null,
|
|
2561
|
+
codeSource: "synced",
|
|
2562
|
+
graph: { nodes: def.nodes, edges: def.edges ?? [] },
|
|
2563
|
+
...syncFields2(options)
|
|
2564
|
+
};
|
|
2565
|
+
}
|
|
2566
|
+
if ("format" in def && def.format === "steps") {
|
|
2567
|
+
const file = def;
|
|
2568
|
+
return {
|
|
2569
|
+
slug: file.slug,
|
|
2570
|
+
name: file.name,
|
|
2571
|
+
description: file.description ?? null,
|
|
2572
|
+
...file.trigger ? { trigger: triggerToUpsertInput(asTrigger(file.trigger)) } : {},
|
|
2573
|
+
codeSource: "synced",
|
|
2574
|
+
steps: file.steps.map((step) => stepToUpsertInput(step)),
|
|
2575
|
+
...syncFields2(options)
|
|
2576
|
+
};
|
|
2577
|
+
}
|
|
2578
|
+
const authored = def;
|
|
2579
|
+
return {
|
|
2580
|
+
slug: authored.slug,
|
|
2581
|
+
name: authored.name,
|
|
2582
|
+
description: authored.description,
|
|
2583
|
+
isActive: authored.isActive,
|
|
2584
|
+
...authored.trigger ? { trigger: triggerToUpsertInput(authored.trigger) } : {},
|
|
2585
|
+
codeSource: options?.codeSource ?? "generated",
|
|
2586
|
+
...syncFields2(options),
|
|
2587
|
+
steps: authored.steps.map((step) => stepToUpsertInput(step))
|
|
2280
2588
|
};
|
|
2281
2589
|
}
|
|
2282
2590
|
var WorkflowsResource = class {
|
|
@@ -2301,15 +2609,22 @@ var WorkflowsResource = class {
|
|
|
2301
2609
|
query: serializeListQuery(options)
|
|
2302
2610
|
});
|
|
2303
2611
|
}
|
|
2612
|
+
/** Exported definition for `lunnoa/workflows/<slug>.json`. */
|
|
2613
|
+
getDefinition(slug) {
|
|
2614
|
+
return this.http.get(
|
|
2615
|
+
`/api/workflows/slug/${encodeURIComponent(slug)}/definition`
|
|
2616
|
+
);
|
|
2617
|
+
}
|
|
2304
2618
|
/**
|
|
2305
2619
|
* Upsert a workflow by slug inside the workspace on the API key.
|
|
2306
2620
|
* Prefer the CLI (`npx @lunnoa/client workflows deploy`) for happy-path deploy.
|
|
2307
2621
|
*/
|
|
2308
|
-
upsertBySlug(body) {
|
|
2309
|
-
const payload = "kind" in body && body.kind === "workflow" ? workflowDefinitionToUpsert(body) : body;
|
|
2310
|
-
return this.http.post(
|
|
2311
|
-
|
|
2312
|
-
|
|
2622
|
+
upsertBySlug(body, options) {
|
|
2623
|
+
const payload = "kind" in body && body.kind === "workflow" ? workflowDefinitionToUpsert(body, options) : body;
|
|
2624
|
+
return this.http.post(
|
|
2625
|
+
"/api/workflows/upsert",
|
|
2626
|
+
{ body: payload }
|
|
2627
|
+
);
|
|
2313
2628
|
}
|
|
2314
2629
|
/**
|
|
2315
2630
|
* Starts a new execution of the workflow with the given trigger inputs.
|
|
@@ -2375,6 +2690,8 @@ var LunnoaClient = class {
|
|
|
2375
2690
|
aiConnections;
|
|
2376
2691
|
workflowApps;
|
|
2377
2692
|
discovery;
|
|
2693
|
+
/** Workspace id for the definitions lock, and the code-sync lock flag. */
|
|
2694
|
+
codeSync;
|
|
2378
2695
|
constructor(options) {
|
|
2379
2696
|
this.http = new HttpClient(options);
|
|
2380
2697
|
this.auth = new AuthResource(this.http, this.http.tokenStore);
|
|
@@ -2398,6 +2715,7 @@ var LunnoaClient = class {
|
|
|
2398
2715
|
this.connections = new ConnectionsResource(this.http);
|
|
2399
2716
|
this.aiConnections = new AiConnectionsResource(this.http);
|
|
2400
2717
|
this.discovery = new DiscoveryResource(this.http);
|
|
2718
|
+
this.codeSync = new CodeSyncResource(this.http);
|
|
2401
2719
|
}
|
|
2402
2720
|
};
|
|
2403
2721
|
|
|
@@ -2481,6 +2799,7 @@ exports.AgentsResource = AgentsResource;
|
|
|
2481
2799
|
exports.AiConnectionsResource = AiConnectionsResource;
|
|
2482
2800
|
exports.ApprovalsResource = ApprovalsResource;
|
|
2483
2801
|
exports.AuthResource = AuthResource;
|
|
2802
|
+
exports.CodeSyncResource = CodeSyncResource;
|
|
2484
2803
|
exports.ConnectionsResource = ConnectionsResource;
|
|
2485
2804
|
exports.DEFINE_KINDS = DEFINE_KINDS;
|
|
2486
2805
|
exports.DiscoveryResource = DiscoveryResource;
|
|
@@ -2517,6 +2836,7 @@ exports.defineEntityType = defineEntityType;
|
|
|
2517
2836
|
exports.defineTrigger = defineTrigger;
|
|
2518
2837
|
exports.defineWorkflow = defineWorkflow;
|
|
2519
2838
|
exports.entityTypeDefinitionToUpsert = entityTypeDefinitionToUpsert;
|
|
2839
|
+
exports.isAgentFileShape = isAgentFileShape;
|
|
2520
2840
|
exports.isBrowserEnvironment = isBrowserEnvironment;
|
|
2521
2841
|
exports.isLoginRequires2FA = isLoginRequires2FA;
|
|
2522
2842
|
exports.iterateOffset = iterateOffset;
|