@lunnoa/client 2.4.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 +24 -1
- package/dist/cli.js +1242 -107
- package/dist/index.cjs +473 -56
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1294 -195
- package/dist/index.d.ts +1294 -195
- package/dist/index.js +471 -57
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +253 -26
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +1051 -143
- package/dist/react.d.ts +1051 -143
- package/dist/react.js +253 -26
- 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 +178 -9
- package/skills/lunnoa-solution-design/SKILL.md +12 -5
package/dist/index.js
CHANGED
|
@@ -26,16 +26,21 @@ var LunnoaApiError = class extends Error {
|
|
|
26
26
|
method;
|
|
27
27
|
/** Path of the failed request (without the base URL). */
|
|
28
28
|
path;
|
|
29
|
+
/** Present on definition-sync 409s when the server sends the current revision. */
|
|
30
|
+
currentRevision;
|
|
29
31
|
constructor(args) {
|
|
30
32
|
const detail = extractMessage(args.body);
|
|
33
|
+
const currentRevision = extractCurrentRevision(args.body);
|
|
34
|
+
const shown = [detail, currentRevision ? `currentRevision: ${currentRevision}` : void 0].filter(Boolean).join(" ");
|
|
31
35
|
super(
|
|
32
|
-
`Lunnoa API request failed: ${args.method} ${args.path} \u2192 ${args.status}${
|
|
36
|
+
`Lunnoa API request failed: ${args.method} ${args.path} \u2192 ${args.status}${shown ? ` (${shown})` : ""}`
|
|
33
37
|
);
|
|
34
38
|
this.name = "LunnoaApiError";
|
|
35
39
|
this.status = args.status;
|
|
36
40
|
this.body = args.body;
|
|
37
41
|
this.method = args.method;
|
|
38
42
|
this.path = args.path;
|
|
43
|
+
this.currentRevision = currentRevision;
|
|
39
44
|
}
|
|
40
45
|
/** True for 401 responses (missing, revoked, or expired credential). */
|
|
41
46
|
get isUnauthorized() {
|
|
@@ -54,6 +59,11 @@ var LunnoaApiError = class extends Error {
|
|
|
54
59
|
return this.status === 429;
|
|
55
60
|
}
|
|
56
61
|
};
|
|
62
|
+
function extractCurrentRevision(body) {
|
|
63
|
+
if (!body || typeof body !== "object") return void 0;
|
|
64
|
+
const revision = body.currentRevision;
|
|
65
|
+
return typeof revision === "string" ? revision : void 0;
|
|
66
|
+
}
|
|
57
67
|
function extractMessage(body) {
|
|
58
68
|
if (body && typeof body === "object") {
|
|
59
69
|
const record = body;
|
|
@@ -530,6 +540,139 @@ var ActionsResource = class {
|
|
|
530
540
|
}
|
|
531
541
|
};
|
|
532
542
|
|
|
543
|
+
// src/define/files.ts
|
|
544
|
+
var STEP_KEY_PATTERN = /^[A-Za-z][A-Za-z0-9_-]*$/;
|
|
545
|
+
function hasFileAgentSignals(record) {
|
|
546
|
+
if (record.instructions && typeof record.instructions === "object") return true;
|
|
547
|
+
if (record.settings && typeof record.settings === "object") return true;
|
|
548
|
+
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) {
|
|
549
|
+
return true;
|
|
550
|
+
}
|
|
551
|
+
return Array.isArray(record.tools) && record.tools.some(
|
|
552
|
+
(tool) => tool && typeof tool === "object" && !("kind" in tool) && !("use" in tool)
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
function isAgentFileInput(config) {
|
|
556
|
+
if (!config || typeof config !== "object") return false;
|
|
557
|
+
return hasFileAgentSignals(config);
|
|
558
|
+
}
|
|
559
|
+
function isAgentFileShape(config) {
|
|
560
|
+
if (!config || typeof config !== "object") return false;
|
|
561
|
+
return hasFileAgentSignals(config);
|
|
562
|
+
}
|
|
563
|
+
function acceptAgentFile(config) {
|
|
564
|
+
const slug = config.slug?.trim();
|
|
565
|
+
if (!slug) throw new Error("defineAgent requires slug");
|
|
566
|
+
const instructions = config.instructions;
|
|
567
|
+
const fileRef = instructions && typeof instructions === "object" && typeof instructions.file === "string" && instructions.file.trim().length > 0;
|
|
568
|
+
if (typeof instructions !== "string" && !fileRef) {
|
|
569
|
+
throw new Error("defineAgent requires instructions");
|
|
570
|
+
}
|
|
571
|
+
const copy = { ...config };
|
|
572
|
+
delete copy.$schema;
|
|
573
|
+
delete copy.kind;
|
|
574
|
+
return { ...copy, kind: "agent", slug, instructions };
|
|
575
|
+
}
|
|
576
|
+
function assertStepKeys(steps, seen) {
|
|
577
|
+
for (const step of steps ?? []) {
|
|
578
|
+
if (!step?.use?.id?.trim()) {
|
|
579
|
+
throw new Error("workflow step requires use.id");
|
|
580
|
+
}
|
|
581
|
+
if (step.key !== void 0) {
|
|
582
|
+
if (!STEP_KEY_PATTERN.test(step.key) || step.key === "trigger") {
|
|
583
|
+
throw new Error(
|
|
584
|
+
`Step key "${step.key}" must start with a letter, use letters, digits, - or _, and not be "trigger".`
|
|
585
|
+
);
|
|
586
|
+
}
|
|
587
|
+
if (seen.has(step.key)) {
|
|
588
|
+
throw new Error(`Step key "${step.key}" is used twice.`);
|
|
589
|
+
}
|
|
590
|
+
seen.add(step.key);
|
|
591
|
+
}
|
|
592
|
+
for (const path of step.paths ?? []) {
|
|
593
|
+
assertStepKeys(path.steps ?? [], seen);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
function acceptWorkflowStepsFile(config) {
|
|
598
|
+
const slug = config.slug?.trim();
|
|
599
|
+
if (!slug) throw new Error("defineWorkflow requires slug");
|
|
600
|
+
if (!config.steps?.length) {
|
|
601
|
+
throw new Error("defineWorkflow requires at least one step");
|
|
602
|
+
}
|
|
603
|
+
if (config.trigger && !config.trigger.id?.trim()) {
|
|
604
|
+
throw new Error("defineWorkflow trigger requires id");
|
|
605
|
+
}
|
|
606
|
+
assertStepKeys(config.steps, /* @__PURE__ */ new Set());
|
|
607
|
+
const copy = { ...config };
|
|
608
|
+
delete copy.$schema;
|
|
609
|
+
delete copy.kind;
|
|
610
|
+
return {
|
|
611
|
+
...copy,
|
|
612
|
+
kind: "workflow",
|
|
613
|
+
format: "steps",
|
|
614
|
+
slug
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
function acceptWorkflowGraphFile(config) {
|
|
618
|
+
const slug = config.slug?.trim();
|
|
619
|
+
if (!slug) throw new Error("defineWorkflow requires slug");
|
|
620
|
+
if (!Array.isArray(config.nodes) || config.nodes.length === 0) {
|
|
621
|
+
throw new Error("defineWorkflow graph requires nodes");
|
|
622
|
+
}
|
|
623
|
+
const keys = /* @__PURE__ */ new Set();
|
|
624
|
+
let triggers = 0;
|
|
625
|
+
for (const node of config.nodes) {
|
|
626
|
+
if (!node?.key || typeof node.key !== "string") {
|
|
627
|
+
throw new Error("graph node requires key");
|
|
628
|
+
}
|
|
629
|
+
if (!STEP_KEY_PATTERN.test(node.key)) {
|
|
630
|
+
throw new Error(
|
|
631
|
+
`Node key "${node.key}" must start with a letter, use letters, digits, - or _, and not be empty.`
|
|
632
|
+
);
|
|
633
|
+
}
|
|
634
|
+
if (keys.has(node.key)) {
|
|
635
|
+
throw new Error(`Node key "${node.key}" is used twice.`);
|
|
636
|
+
}
|
|
637
|
+
keys.add(node.key);
|
|
638
|
+
if (node.nodeType === "trigger") triggers += 1;
|
|
639
|
+
}
|
|
640
|
+
if (triggers !== 1) {
|
|
641
|
+
throw new Error("defineWorkflow graph requires exactly one trigger");
|
|
642
|
+
}
|
|
643
|
+
for (const edge of config.edges ?? []) {
|
|
644
|
+
if (!keys.has(edge?.source) || !keys.has(edge?.target)) {
|
|
645
|
+
throw new Error(
|
|
646
|
+
`Edge from "${edge?.source ?? ""}" to "${edge?.target ?? ""}" references an unknown key.`
|
|
647
|
+
);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
const copy = { ...config };
|
|
651
|
+
delete copy.$schema;
|
|
652
|
+
delete copy.kind;
|
|
653
|
+
return {
|
|
654
|
+
...copy,
|
|
655
|
+
kind: "workflow",
|
|
656
|
+
format: "graph",
|
|
657
|
+
slug,
|
|
658
|
+
edges: config.edges ?? []
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
function isWorkflowGraphInput(config) {
|
|
662
|
+
return !!config && typeof config === "object" && config.format === "graph";
|
|
663
|
+
}
|
|
664
|
+
function isWorkflowStepsFile(config) {
|
|
665
|
+
if (!config || typeof config !== "object") return false;
|
|
666
|
+
const record = config;
|
|
667
|
+
if (record.format === "steps") return true;
|
|
668
|
+
if (record.trigger && typeof record.trigger === "object" && record.trigger.kind !== "trigger" && typeof record.trigger.id === "string") {
|
|
669
|
+
return true;
|
|
670
|
+
}
|
|
671
|
+
return Array.isArray(record.steps) && record.steps.some(
|
|
672
|
+
(step) => step && typeof step === "object" && step.use && step.use?.kind !== "action"
|
|
673
|
+
);
|
|
674
|
+
}
|
|
675
|
+
|
|
533
676
|
// src/define/index.ts
|
|
534
677
|
var DEFINE_KINDS = {
|
|
535
678
|
connection: "connection",
|
|
@@ -540,6 +683,32 @@ var DEFINE_KINDS = {
|
|
|
540
683
|
agent: "agent",
|
|
541
684
|
entityType: "entityType"
|
|
542
685
|
};
|
|
686
|
+
var PATH_CONDITIONS = [
|
|
687
|
+
"contains",
|
|
688
|
+
"does_not_contain",
|
|
689
|
+
"equals",
|
|
690
|
+
"does_not_equal",
|
|
691
|
+
"exists",
|
|
692
|
+
"does_not_exist",
|
|
693
|
+
"starts_with",
|
|
694
|
+
"ends_with",
|
|
695
|
+
"is_empty",
|
|
696
|
+
"is_not_empty",
|
|
697
|
+
"comes_before",
|
|
698
|
+
"comes_after",
|
|
699
|
+
"is_in_the_past",
|
|
700
|
+
"is_in_the_future",
|
|
701
|
+
"is_true",
|
|
702
|
+
"is_false",
|
|
703
|
+
"is_truthy",
|
|
704
|
+
"is_falsey",
|
|
705
|
+
"is_greater_than",
|
|
706
|
+
"is_less_than",
|
|
707
|
+
"is_greater_than_or_equal_to",
|
|
708
|
+
"is_less_than_or_equal_to",
|
|
709
|
+
"matches_regex",
|
|
710
|
+
"does_not_match_regex"
|
|
711
|
+
];
|
|
543
712
|
function defineConnection(config = {}) {
|
|
544
713
|
return { kind: DEFINE_KINDS.connection, ...config };
|
|
545
714
|
}
|
|
@@ -573,7 +742,7 @@ function defineTrigger(config) {
|
|
|
573
742
|
}
|
|
574
743
|
var MANUAL_TRIGGER_ID = "flow-control_trigger_manual";
|
|
575
744
|
var TRIGGER_KEY = "trigger";
|
|
576
|
-
var
|
|
745
|
+
var STEP_KEY_PATTERN2 = /^[A-Za-z][A-Za-z0-9_-]*$/;
|
|
577
746
|
var STEP_REF_PATTERN = /={{step:([^}]*)}}/g;
|
|
578
747
|
function refString(key, path) {
|
|
579
748
|
const parts = [key, ...path.map(String)];
|
|
@@ -601,32 +770,44 @@ function collectRefs(value, out) {
|
|
|
601
770
|
Object.values(value).forEach((item) => collectRefs(item, out));
|
|
602
771
|
}
|
|
603
772
|
}
|
|
773
|
+
var CONDITIONAL_PATHS_ID = "flow-control_action_conditional-paths";
|
|
774
|
+
var APPROVAL_ID = "flow-control_action_request-approval";
|
|
775
|
+
var BRANCHING_ACTION_IDS = /* @__PURE__ */ new Set([
|
|
776
|
+
CONDITIONAL_PATHS_ID,
|
|
777
|
+
"flow-control_action_manually-decide-paths",
|
|
778
|
+
APPROVAL_ID
|
|
779
|
+
]);
|
|
780
|
+
var PATH_CONDITION_SET = new Set(PATH_CONDITIONS);
|
|
781
|
+
var SINGLE_REF_PATTERN = /^={{step:[^}]*}}$/;
|
|
782
|
+
function collectKeys(steps, seen) {
|
|
783
|
+
for (const step of steps) {
|
|
784
|
+
if (step.key !== void 0) {
|
|
785
|
+
if (!STEP_KEY_PATTERN2.test(step.key) || step.key === TRIGGER_KEY) {
|
|
786
|
+
throw new Error(
|
|
787
|
+
`Step key "${step.key}" must start with a letter, use letters, digits, - or _, and not be "${TRIGGER_KEY}".`
|
|
788
|
+
);
|
|
789
|
+
}
|
|
790
|
+
if (seen.has(step.key)) {
|
|
791
|
+
throw new Error(`Step key "${step.key}" is used twice.`);
|
|
792
|
+
}
|
|
793
|
+
seen.add(step.key);
|
|
794
|
+
}
|
|
795
|
+
for (const path of step.paths ?? []) collectKeys(path.steps ?? [], seen);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
604
798
|
function assertStepRefs(config) {
|
|
605
799
|
const triggerId = config.trigger?.id.trim() ?? MANUAL_TRIGGER_ID;
|
|
606
800
|
const triggerFields = triggerId === MANUAL_TRIGGER_ID ? new Set((config.trigger?.inputs ?? []).map((f) => f.id.trim())) : void 0;
|
|
607
801
|
const allKeys = /* @__PURE__ */ new Set();
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
if (!STEP_KEY_PATTERN.test(step.key) || step.key === TRIGGER_KEY) {
|
|
611
|
-
throw new Error(
|
|
612
|
-
`Step key "${step.key}" must start with a letter, use letters, digits, - or _, and not be "${TRIGGER_KEY}".`
|
|
613
|
-
);
|
|
614
|
-
}
|
|
615
|
-
if (allKeys.has(step.key)) {
|
|
616
|
-
throw new Error(`Step key "${step.key}" is used twice.`);
|
|
617
|
-
}
|
|
618
|
-
allKeys.add(step.key);
|
|
619
|
-
}
|
|
620
|
-
const earlier = /* @__PURE__ */ new Set([TRIGGER_KEY]);
|
|
621
|
-
config.steps.forEach((step, index) => {
|
|
802
|
+
collectKeys(config.steps, allKeys);
|
|
803
|
+
const checkRefs = (value, earlier, where) => {
|
|
622
804
|
const refs = [];
|
|
623
|
-
collectRefs(
|
|
624
|
-
const where = `Step "${step.key ?? step.name ?? index + 1}"`;
|
|
805
|
+
collectRefs(value, refs);
|
|
625
806
|
for (const [key = "", ...path] of refs) {
|
|
626
807
|
const shown = `={{step:${[key, ...path].join(",")}}}`;
|
|
627
808
|
if (!earlier.has(key)) {
|
|
628
809
|
throw new Error(
|
|
629
|
-
allKeys.has(key) ? `${where}: ${shown} refers to itself or a
|
|
810
|
+
allKeys.has(key) ? `${where}: ${shown} refers to itself, a later step, or a step on another path. Use the trigger or an earlier step on the same path.` : `${where}: ${shown} names unknown key "${key}". Use "trigger" or an earlier step's key.`
|
|
630
811
|
);
|
|
631
812
|
}
|
|
632
813
|
if (key === TRIGGER_KEY && triggerFields && !triggerFields.has(path[0] ?? "")) {
|
|
@@ -635,33 +816,93 @@ function assertStepRefs(config) {
|
|
|
635
816
|
);
|
|
636
817
|
}
|
|
637
818
|
}
|
|
638
|
-
|
|
639
|
-
|
|
819
|
+
};
|
|
820
|
+
const checkChain = (steps, visible) => {
|
|
821
|
+
const earlier = new Set(visible);
|
|
822
|
+
steps.forEach((step, index) => {
|
|
823
|
+
const where = `Step "${step.key ?? step.name ?? index + 1}"`;
|
|
824
|
+
checkRefs([step.use?.input, step.input], earlier, where);
|
|
825
|
+
if (step.key) earlier.add(step.key);
|
|
826
|
+
if (!step.paths?.length) return;
|
|
827
|
+
const actionId = step.use ? resolveActionId(step.use) : "";
|
|
828
|
+
if (!BRANCHING_ACTION_IDS.has(actionId)) {
|
|
829
|
+
throw new Error(`${where}: paths only work on ${[...BRANCHING_ACTION_IDS].join(", ")}.`);
|
|
830
|
+
}
|
|
831
|
+
if (index !== steps.length - 1) {
|
|
832
|
+
throw new Error(
|
|
833
|
+
`${where}: a step with paths must be the last step in its list. Put later steps inside each path.`
|
|
834
|
+
);
|
|
835
|
+
}
|
|
836
|
+
if (actionId === APPROVAL_ID && step.paths.length > 2) {
|
|
837
|
+
throw new Error(`${where}: Request Approval takes at most two paths (approved, then rejected).`);
|
|
838
|
+
}
|
|
839
|
+
const labels = /* @__PURE__ */ new Set();
|
|
840
|
+
for (const path of step.paths) {
|
|
841
|
+
const label = path?.label?.trim();
|
|
842
|
+
if (!label) throw new Error(`${where}: every path needs a label.`);
|
|
843
|
+
if (labels.has(label)) throw new Error(`${where}: path label "${label}" is used twice.`);
|
|
844
|
+
labels.add(label);
|
|
845
|
+
if (path.when !== void 0 && actionId !== CONDITIONAL_PATHS_ID) {
|
|
846
|
+
throw new Error(`${where}: "when" only works on Conditional Paths.`);
|
|
847
|
+
}
|
|
848
|
+
for (const condition of (path.when ?? []).flat()) {
|
|
849
|
+
if (!SINGLE_REF_PATTERN.test(condition?.field?.trim() ?? "")) {
|
|
850
|
+
throw new Error(
|
|
851
|
+
`${where}, path "${label}": a condition's field must be one ref.trigger(...) or ref.step(...).`
|
|
852
|
+
);
|
|
853
|
+
}
|
|
854
|
+
if (!PATH_CONDITION_SET.has(condition.condition)) {
|
|
855
|
+
throw new Error(
|
|
856
|
+
`${where}, path "${label}": condition "${condition.condition}" is not supported. Use one of: ${PATH_CONDITIONS.join(", ")}.`
|
|
857
|
+
);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
checkRefs(path.when, earlier, `${where}, path "${label}"`);
|
|
861
|
+
if (!path.steps?.length) throw new Error(`${where}: path "${label}" needs at least one step.`);
|
|
862
|
+
checkChain(path.steps, earlier);
|
|
863
|
+
}
|
|
864
|
+
});
|
|
865
|
+
};
|
|
866
|
+
checkChain(config.steps, /* @__PURE__ */ new Set([TRIGGER_KEY]));
|
|
640
867
|
}
|
|
641
868
|
function defineWorkflow(config) {
|
|
642
|
-
|
|
869
|
+
const record = config;
|
|
870
|
+
if (isWorkflowGraphInput(config) || Array.isArray(record.nodes) && !Array.isArray(record.steps)) {
|
|
871
|
+
return acceptWorkflowGraphFile(config);
|
|
872
|
+
}
|
|
873
|
+
if (isWorkflowStepsFile(config)) {
|
|
874
|
+
return acceptWorkflowStepsFile(config);
|
|
875
|
+
}
|
|
876
|
+
const authored = { ...config };
|
|
877
|
+
delete authored.$schema;
|
|
878
|
+
if (!authored.slug?.trim()) {
|
|
643
879
|
throw new Error("defineWorkflow requires slug");
|
|
644
880
|
}
|
|
645
|
-
if (!
|
|
881
|
+
if (!authored.steps?.length) {
|
|
646
882
|
throw new Error("defineWorkflow requires at least one step");
|
|
647
883
|
}
|
|
648
|
-
if (
|
|
649
|
-
assertTriggerConfig(
|
|
884
|
+
if (authored.trigger) {
|
|
885
|
+
assertTriggerConfig(authored.trigger);
|
|
650
886
|
}
|
|
651
|
-
assertStepRefs(
|
|
652
|
-
return { kind: DEFINE_KINDS.workflow, ...
|
|
887
|
+
assertStepRefs(authored);
|
|
888
|
+
return { kind: DEFINE_KINDS.workflow, ...authored };
|
|
653
889
|
}
|
|
654
890
|
function defineAgent(config) {
|
|
655
|
-
if (
|
|
891
|
+
if (isAgentFileInput(config)) {
|
|
892
|
+
return acceptAgentFile(config);
|
|
893
|
+
}
|
|
894
|
+
const authored = { ...config };
|
|
895
|
+
delete authored.$schema;
|
|
896
|
+
if (!authored.slug?.trim()) {
|
|
656
897
|
throw new Error("defineAgent requires slug");
|
|
657
898
|
}
|
|
658
|
-
if (!
|
|
899
|
+
if (!authored.model?.trim()) {
|
|
659
900
|
throw new Error("defineAgent requires model");
|
|
660
901
|
}
|
|
661
|
-
if (
|
|
902
|
+
if (authored.instructions == null) {
|
|
662
903
|
throw new Error("defineAgent requires instructions");
|
|
663
904
|
}
|
|
664
|
-
return { kind: DEFINE_KINDS.agent, ...
|
|
905
|
+
return { kind: DEFINE_KINDS.agent, ...authored };
|
|
665
906
|
}
|
|
666
907
|
function defineEntityType(config) {
|
|
667
908
|
if (!config.slug?.trim()) {
|
|
@@ -776,10 +1017,24 @@ function unwrapTool(tool) {
|
|
|
776
1017
|
}
|
|
777
1018
|
return tool;
|
|
778
1019
|
}
|
|
1020
|
+
function syncFields(options) {
|
|
1021
|
+
return {
|
|
1022
|
+
...options?.baseRevision ? { baseRevision: options.baseRevision } : {},
|
|
1023
|
+
...options?.force ? { force: true } : {}
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
function idsOf(links) {
|
|
1027
|
+
if (!links) return void 0;
|
|
1028
|
+
return links.map((link) => link.id);
|
|
1029
|
+
}
|
|
779
1030
|
function agentDefinitionToUpsert(def, options) {
|
|
1031
|
+
if (isAgentFileShape(def)) {
|
|
1032
|
+
return fileAgentToUpsert(def, options);
|
|
1033
|
+
}
|
|
780
1034
|
const tools = [];
|
|
781
1035
|
const workflowIds = [...options?.workflowIds ?? []];
|
|
782
|
-
|
|
1036
|
+
const authored = def;
|
|
1037
|
+
for (const tool of authored.tools ?? []) {
|
|
783
1038
|
const unwrapped = unwrapTool(tool);
|
|
784
1039
|
if (unwrapped.kind === DEFINE_KINDS.workflow) {
|
|
785
1040
|
continue;
|
|
@@ -795,17 +1050,65 @@ function agentDefinitionToUpsert(def, options) {
|
|
|
795
1050
|
});
|
|
796
1051
|
}
|
|
797
1052
|
}
|
|
1053
|
+
return {
|
|
1054
|
+
slug: authored.slug,
|
|
1055
|
+
name: authored.name,
|
|
1056
|
+
description: authored.description,
|
|
1057
|
+
instructions: authored.instructions,
|
|
1058
|
+
model: authored.model,
|
|
1059
|
+
aiConnectionId: authored.aiConnection?.id,
|
|
1060
|
+
connectionIds: authored.connections?.map((c) => c.id).filter((id) => Boolean(id)),
|
|
1061
|
+
workflowIds: workflowIds.length ? workflowIds : void 0,
|
|
1062
|
+
tools,
|
|
1063
|
+
codeSource: options?.codeSource ?? "generated",
|
|
1064
|
+
...syncFields(options)
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
1067
|
+
function fileAgentToUpsert(def, options) {
|
|
1068
|
+
if (typeof def.instructions !== "string") {
|
|
1069
|
+
throw new Error(
|
|
1070
|
+
"Agent instructions are still a file reference. The CLI resolves these before push."
|
|
1071
|
+
);
|
|
1072
|
+
}
|
|
1073
|
+
const tools = [];
|
|
1074
|
+
for (const tool of def.tools ?? []) {
|
|
1075
|
+
const action = {
|
|
1076
|
+
kind: DEFINE_KINDS.action,
|
|
1077
|
+
id: tool.id,
|
|
1078
|
+
appId: tool.appId,
|
|
1079
|
+
connection: tool.connection?.id ? { kind: DEFINE_KINDS.connection, id: tool.connection.id } : void 0
|
|
1080
|
+
};
|
|
1081
|
+
tools.push({
|
|
1082
|
+
key: tool.key,
|
|
1083
|
+
appId: resolveActionAppId(action),
|
|
1084
|
+
actionId: resolveActionId(action),
|
|
1085
|
+
connectionId: tool.connection?.id,
|
|
1086
|
+
name: tool.name,
|
|
1087
|
+
description: tool.description,
|
|
1088
|
+
input: tool.input
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
798
1091
|
return {
|
|
799
1092
|
slug: def.slug,
|
|
800
1093
|
name: def.name,
|
|
801
|
-
description
|
|
1094
|
+
// A file is the whole definition. Omitted description and model are empty.
|
|
1095
|
+
description: def.description ?? null,
|
|
802
1096
|
instructions: def.instructions,
|
|
803
|
-
model: def.model,
|
|
1097
|
+
model: def.model ?? null,
|
|
804
1098
|
aiConnectionId: def.aiConnection?.id,
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
1099
|
+
...def.settings ? { settings: def.settings } : {},
|
|
1100
|
+
...def.tools ? { tools } : {},
|
|
1101
|
+
...def.workflows ? { workflows: def.workflows } : {},
|
|
1102
|
+
...def.agents ? { agents: def.agents } : {},
|
|
1103
|
+
...def.connections ? { connectionIds: idsOf(def.connections) } : {},
|
|
1104
|
+
...def.knowledge ? { knowledgeIds: idsOf(def.knowledge) } : {},
|
|
1105
|
+
...def.variables ? { variableIds: idsOf(def.variables) } : {},
|
|
1106
|
+
...def.objects ? { objectIds: idsOf(def.objects) } : {},
|
|
1107
|
+
...def.objectTypes ? { objectTypeIds: idsOf(def.objectTypes) } : {},
|
|
1108
|
+
...def.mcpServers ? { mcpServers: def.mcpServers } : {},
|
|
1109
|
+
...def.a2aAgents ? { a2aAgentIds: idsOf(def.a2aAgents) } : {},
|
|
1110
|
+
codeSource: "synced",
|
|
1111
|
+
...syncFields(options)
|
|
809
1112
|
};
|
|
810
1113
|
}
|
|
811
1114
|
var AgentsResource = class {
|
|
@@ -825,15 +1128,22 @@ var AgentsResource = class {
|
|
|
825
1128
|
query: serializeListQuery(options)
|
|
826
1129
|
});
|
|
827
1130
|
}
|
|
1131
|
+
/** Exported definition for `lunnoa/agents/<slug>.json`. */
|
|
1132
|
+
getDefinition(slug) {
|
|
1133
|
+
return this.http.get(
|
|
1134
|
+
`/api/agents/slug/${encodeURIComponent(slug)}/definition`
|
|
1135
|
+
);
|
|
1136
|
+
}
|
|
828
1137
|
/**
|
|
829
1138
|
* Upsert an agent by slug inside the workspace on the API key.
|
|
830
1139
|
* Prefer the CLI (`npx @lunnoa/client agents deploy`) for happy-path deploy.
|
|
831
1140
|
*/
|
|
832
1141
|
upsertBySlug(body, options) {
|
|
833
1142
|
const payload = "kind" in body && body.kind === "agent" ? agentDefinitionToUpsert(body, options) : body;
|
|
834
|
-
return this.http.post(
|
|
835
|
-
|
|
836
|
-
|
|
1143
|
+
return this.http.post(
|
|
1144
|
+
"/api/agents/upsert",
|
|
1145
|
+
{ body: payload }
|
|
1146
|
+
);
|
|
837
1147
|
}
|
|
838
1148
|
};
|
|
839
1149
|
|
|
@@ -1935,6 +2245,11 @@ function stepFromDefinition(step) {
|
|
|
1935
2245
|
...step.name ? { name: step.name } : {}
|
|
1936
2246
|
};
|
|
1937
2247
|
}
|
|
2248
|
+
if (step.paths?.length) {
|
|
2249
|
+
throw new Error(
|
|
2250
|
+
"runs.start runs straight lines only. Deploy workflows with paths, then execute them."
|
|
2251
|
+
);
|
|
2252
|
+
}
|
|
1938
2253
|
const use = step.use;
|
|
1939
2254
|
const input = {
|
|
1940
2255
|
...use.input ?? {},
|
|
@@ -1952,6 +2267,11 @@ function stepFromDefinition(step) {
|
|
|
1952
2267
|
}
|
|
1953
2268
|
function normalizeStartRunInput(input) {
|
|
1954
2269
|
if (isWorkflowDefinition(input)) {
|
|
2270
|
+
if ("format" in input && input.format === "graph") {
|
|
2271
|
+
throw new Error(
|
|
2272
|
+
"runs.start runs straight lines only. Push the graph workflow, then execute it."
|
|
2273
|
+
);
|
|
2274
|
+
}
|
|
1955
2275
|
return {
|
|
1956
2276
|
steps: input.steps.map(stepFromDefinition),
|
|
1957
2277
|
name: input.name ?? input.slug
|
|
@@ -2143,6 +2463,18 @@ var WorkflowAppsResource = class {
|
|
|
2143
2463
|
}
|
|
2144
2464
|
};
|
|
2145
2465
|
|
|
2466
|
+
// src/resources/code-sync.ts
|
|
2467
|
+
var CodeSyncResource = class {
|
|
2468
|
+
constructor(http) {
|
|
2469
|
+
this.http = http;
|
|
2470
|
+
}
|
|
2471
|
+
http;
|
|
2472
|
+
/** Workspace id and whether synced resources are locked in the UI. */
|
|
2473
|
+
context() {
|
|
2474
|
+
return this.http.get("/api/code-sync/context");
|
|
2475
|
+
}
|
|
2476
|
+
};
|
|
2477
|
+
|
|
2146
2478
|
// src/resources/workflows.ts
|
|
2147
2479
|
function triggerToUpsertInput(trigger) {
|
|
2148
2480
|
return {
|
|
@@ -2155,8 +2487,38 @@ function triggerToUpsertInput(trigger) {
|
|
|
2155
2487
|
...trigger.description ? { description: trigger.description } : {}
|
|
2156
2488
|
};
|
|
2157
2489
|
}
|
|
2490
|
+
function asAction(use) {
|
|
2491
|
+
if (use && typeof use === "object" && "kind" in use && use.kind === DEFINE_KINDS.action) {
|
|
2492
|
+
return use;
|
|
2493
|
+
}
|
|
2494
|
+
const fileUse = use;
|
|
2495
|
+
return {
|
|
2496
|
+
kind: DEFINE_KINDS.action,
|
|
2497
|
+
id: fileUse.id,
|
|
2498
|
+
appId: fileUse.appId,
|
|
2499
|
+
connection: fileUse.connection?.id ? { kind: DEFINE_KINDS.connection, id: fileUse.connection.id } : void 0
|
|
2500
|
+
};
|
|
2501
|
+
}
|
|
2502
|
+
function asTrigger(trigger) {
|
|
2503
|
+
if ("kind" in trigger && trigger.kind === DEFINE_KINDS.trigger) {
|
|
2504
|
+
return trigger;
|
|
2505
|
+
}
|
|
2506
|
+
const fileTrigger = trigger;
|
|
2507
|
+
return {
|
|
2508
|
+
kind: DEFINE_KINDS.trigger,
|
|
2509
|
+
id: fileTrigger.id,
|
|
2510
|
+
appId: fileTrigger.appId,
|
|
2511
|
+
connection: fileTrigger.connection?.id ? { kind: DEFINE_KINDS.connection, id: fileTrigger.connection.id } : void 0,
|
|
2512
|
+
input: fileTrigger.input,
|
|
2513
|
+
inputs: fileTrigger.inputs,
|
|
2514
|
+
name: fileTrigger.name,
|
|
2515
|
+
description: fileTrigger.description
|
|
2516
|
+
};
|
|
2517
|
+
}
|
|
2158
2518
|
function stepToUpsertInput(step) {
|
|
2159
|
-
const use = step.use;
|
|
2519
|
+
const use = asAction(step.use);
|
|
2520
|
+
const retry = "retrySettings" in step ? step.retrySettings : void 0;
|
|
2521
|
+
const continueOnFailure = "continueOnFailure" in step ? step.continueOnFailure : void 0;
|
|
2160
2522
|
return {
|
|
2161
2523
|
...step.key ? { key: step.key } : {},
|
|
2162
2524
|
appId: resolveActionAppId(use),
|
|
@@ -2167,18 +2529,60 @@ function stepToUpsertInput(step) {
|
|
|
2167
2529
|
...step.input ?? {}
|
|
2168
2530
|
},
|
|
2169
2531
|
name: step.name ?? use.name,
|
|
2170
|
-
description: use.description
|
|
2532
|
+
description: step.description ?? use.description,
|
|
2533
|
+
...retry ? { retrySettings: retry } : {},
|
|
2534
|
+
...continueOnFailure ? { continueOnFailure } : {},
|
|
2535
|
+
...step.paths?.length ? {
|
|
2536
|
+
paths: step.paths.map((path) => ({
|
|
2537
|
+
label: path.label,
|
|
2538
|
+
...path.when ? { when: path.when } : {},
|
|
2539
|
+
steps: path.steps.map(
|
|
2540
|
+
(nested) => stepToUpsertInput(nested)
|
|
2541
|
+
)
|
|
2542
|
+
}))
|
|
2543
|
+
} : {}
|
|
2171
2544
|
};
|
|
2172
2545
|
}
|
|
2173
|
-
function
|
|
2546
|
+
function syncFields2(options) {
|
|
2174
2547
|
return {
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2548
|
+
...options?.baseRevision ? { baseRevision: options.baseRevision } : {},
|
|
2549
|
+
...options?.force ? { force: true } : {}
|
|
2550
|
+
};
|
|
2551
|
+
}
|
|
2552
|
+
function workflowDefinitionToUpsert(def, options) {
|
|
2553
|
+
if ("format" in def && def.format === "graph") {
|
|
2554
|
+
return {
|
|
2555
|
+
slug: def.slug,
|
|
2556
|
+
name: def.name,
|
|
2557
|
+
// A file is the whole definition: an omitted description is empty, not "leave it".
|
|
2558
|
+
description: def.description ?? null,
|
|
2559
|
+
codeSource: "synced",
|
|
2560
|
+
graph: { nodes: def.nodes, edges: def.edges ?? [] },
|
|
2561
|
+
...syncFields2(options)
|
|
2562
|
+
};
|
|
2563
|
+
}
|
|
2564
|
+
if ("format" in def && def.format === "steps") {
|
|
2565
|
+
const file = def;
|
|
2566
|
+
return {
|
|
2567
|
+
slug: file.slug,
|
|
2568
|
+
name: file.name,
|
|
2569
|
+
description: file.description ?? null,
|
|
2570
|
+
...file.trigger ? { trigger: triggerToUpsertInput(asTrigger(file.trigger)) } : {},
|
|
2571
|
+
codeSource: "synced",
|
|
2572
|
+
steps: file.steps.map((step) => stepToUpsertInput(step)),
|
|
2573
|
+
...syncFields2(options)
|
|
2574
|
+
};
|
|
2575
|
+
}
|
|
2576
|
+
const authored = def;
|
|
2577
|
+
return {
|
|
2578
|
+
slug: authored.slug,
|
|
2579
|
+
name: authored.name,
|
|
2580
|
+
description: authored.description,
|
|
2581
|
+
isActive: authored.isActive,
|
|
2582
|
+
...authored.trigger ? { trigger: triggerToUpsertInput(authored.trigger) } : {},
|
|
2583
|
+
codeSource: options?.codeSource ?? "generated",
|
|
2584
|
+
...syncFields2(options),
|
|
2585
|
+
steps: authored.steps.map((step) => stepToUpsertInput(step))
|
|
2182
2586
|
};
|
|
2183
2587
|
}
|
|
2184
2588
|
var WorkflowsResource = class {
|
|
@@ -2203,15 +2607,22 @@ var WorkflowsResource = class {
|
|
|
2203
2607
|
query: serializeListQuery(options)
|
|
2204
2608
|
});
|
|
2205
2609
|
}
|
|
2610
|
+
/** Exported definition for `lunnoa/workflows/<slug>.json`. */
|
|
2611
|
+
getDefinition(slug) {
|
|
2612
|
+
return this.http.get(
|
|
2613
|
+
`/api/workflows/slug/${encodeURIComponent(slug)}/definition`
|
|
2614
|
+
);
|
|
2615
|
+
}
|
|
2206
2616
|
/**
|
|
2207
2617
|
* Upsert a workflow by slug inside the workspace on the API key.
|
|
2208
2618
|
* Prefer the CLI (`npx @lunnoa/client workflows deploy`) for happy-path deploy.
|
|
2209
2619
|
*/
|
|
2210
|
-
upsertBySlug(body) {
|
|
2211
|
-
const payload = "kind" in body && body.kind === "workflow" ? workflowDefinitionToUpsert(body) : body;
|
|
2212
|
-
return this.http.post(
|
|
2213
|
-
|
|
2214
|
-
|
|
2620
|
+
upsertBySlug(body, options) {
|
|
2621
|
+
const payload = "kind" in body && body.kind === "workflow" ? workflowDefinitionToUpsert(body, options) : body;
|
|
2622
|
+
return this.http.post(
|
|
2623
|
+
"/api/workflows/upsert",
|
|
2624
|
+
{ body: payload }
|
|
2625
|
+
);
|
|
2215
2626
|
}
|
|
2216
2627
|
/**
|
|
2217
2628
|
* Starts a new execution of the workflow with the given trigger inputs.
|
|
@@ -2277,6 +2688,8 @@ var LunnoaClient = class {
|
|
|
2277
2688
|
aiConnections;
|
|
2278
2689
|
workflowApps;
|
|
2279
2690
|
discovery;
|
|
2691
|
+
/** Workspace id for the definitions lock, and the code-sync lock flag. */
|
|
2692
|
+
codeSync;
|
|
2280
2693
|
constructor(options) {
|
|
2281
2694
|
this.http = new HttpClient(options);
|
|
2282
2695
|
this.auth = new AuthResource(this.http, this.http.tokenStore);
|
|
@@ -2300,6 +2713,7 @@ var LunnoaClient = class {
|
|
|
2300
2713
|
this.connections = new ConnectionsResource(this.http);
|
|
2301
2714
|
this.aiConnections = new AiConnectionsResource(this.http);
|
|
2302
2715
|
this.discovery = new DiscoveryResource(this.http);
|
|
2716
|
+
this.codeSync = new CodeSyncResource(this.http);
|
|
2303
2717
|
}
|
|
2304
2718
|
};
|
|
2305
2719
|
|
|
@@ -2375,6 +2789,6 @@ function createServerClient(options) {
|
|
|
2375
2789
|
});
|
|
2376
2790
|
}
|
|
2377
2791
|
|
|
2378
|
-
export { API_KEY_PREFIX, ActionsResource, AgentChatClient, AgentChatStream, AgentsResource, AiConnectionsResource, ApprovalsResource, AuthResource, ConnectionsResource, DEFINE_KINDS, DiscoveryResource, EntitiesResource, EntityTypesResource, ExecutionProgressStream, ExecutionTimeoutError, ExecutionsResource, HttpClient, KnowledgeResource, LunnoaApiError, LunnoaClient, QueueItemsResource, QueuesResource, RunsResource, ScorecardsResource, SsoAuthResource, TablesResource, TasksResource, VariablesResource, WorkflowAppsResource, WorkflowsResource, assertApiKeyAllowedInEnvironment, createBrowserClient, createLocalStorageTokenStore, createMemoryTokenStore, createServerClient, defineAction, defineAgent, defineAiConnection, defineConnection, defineEntityType, defineTrigger, defineWorkflow, entityTypeDefinitionToUpsert, isBrowserEnvironment, isLoginRequires2FA, iterateOffset, iteratePages, parseSseEvents, parseSseStream, ref, resolveActionAppId, resolveActionId, resolveTriggerAppId, resolveTriggerId };
|
|
2792
|
+
export { API_KEY_PREFIX, ActionsResource, AgentChatClient, AgentChatStream, AgentsResource, AiConnectionsResource, ApprovalsResource, AuthResource, CodeSyncResource, ConnectionsResource, DEFINE_KINDS, DiscoveryResource, EntitiesResource, EntityTypesResource, ExecutionProgressStream, ExecutionTimeoutError, ExecutionsResource, HttpClient, KnowledgeResource, LunnoaApiError, LunnoaClient, PATH_CONDITIONS, QueueItemsResource, QueuesResource, RunsResource, ScorecardsResource, SsoAuthResource, TablesResource, TasksResource, VariablesResource, WorkflowAppsResource, WorkflowsResource, assertApiKeyAllowedInEnvironment, createBrowserClient, createLocalStorageTokenStore, createMemoryTokenStore, createServerClient, defineAction, defineAgent, defineAiConnection, defineConnection, defineEntityType, defineTrigger, defineWorkflow, entityTypeDefinitionToUpsert, isAgentFileShape, isBrowserEnvironment, isLoginRequires2FA, iterateOffset, iteratePages, parseSseEvents, parseSseStream, ref, resolveActionAppId, resolveActionId, resolveTriggerAppId, resolveTriggerId };
|
|
2379
2793
|
//# sourceMappingURL=index.js.map
|
|
2380
2794
|
//# sourceMappingURL=index.js.map
|