@lunora/codegen 1.0.0-alpha.41 → 1.0.0-alpha.43
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/dist/index.d.mts +142 -9
- package/dist/index.d.ts +142 -9
- package/dist/index.mjs +7 -6
- package/dist/packem_shared/AGENTS_FILENAME-BksnAJgx.mjs +116 -0
- package/dist/packem_shared/GENERATED_HEADER-B-FzJ297.mjs +4 -0
- package/dist/packem_shared/{OPENRPC_VERSION-DxbqafcJ.mjs → OPENRPC_VERSION-DqTYxQ22.mjs} +1 -1
- package/dist/packem_shared/{SCHEMA_SNAPSHOT_FILENAME-C1yHg38Z.mjs → SCHEMA_SNAPSHOT_FILENAME-BbF69nj9.mjs} +68 -9
- package/dist/packem_shared/{buildOpenApiDocument-_mwWOc2N.mjs → buildOpenApiDocument-BrN41JMl.mjs} +1 -1
- package/dist/packem_shared/{discoverCrons-D-jrpm97.mjs → discoverCrons-DJJ3ZE5F.mjs} +35 -20
- package/dist/packem_shared/{emit-CFz-9dcD.mjs → emit-DtherCpQ.mjs} +286 -32
- package/dist/packem_shared/{emitApp-D8rVuM__.mjs → emitApp-D7daY94X.mjs} +25 -2
- package/package.json +5 -4
- package/dist/packem_shared/GENERATED_HEADER-cUyu5VOU.mjs +0 -3
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { agentComponent } from '@lunora/agent/component';
|
|
1
2
|
import { LunoraError } from '@lunora/errors';
|
|
2
3
|
import { s as sanitizeNamespace } from './paths-BRd6JHuF.mjs';
|
|
3
4
|
|
|
@@ -696,19 +697,19 @@ ${members}
|
|
|
696
697
|
};
|
|
697
698
|
return [...namespaces.entries()].toSorted(([a], [b]) => a.localeCompare(b)).map((entry) => renderNamespace(entry)).join("\n");
|
|
698
699
|
};
|
|
699
|
-
const
|
|
700
|
-
if (workflows.length === 0) {
|
|
700
|
+
const renderSchedulerReferences = (workflows, agents) => {
|
|
701
|
+
if (workflows.length === 0 && agents.length === 0) {
|
|
701
702
|
return { block: "", importLine: "" };
|
|
702
703
|
}
|
|
703
|
-
const
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
).join("\n");
|
|
708
|
-
const block = `
|
|
704
|
+
const importLines = [];
|
|
705
|
+
let block = "";
|
|
706
|
+
if (workflows.length > 0) {
|
|
707
|
+
block += `
|
|
709
708
|
/** Params type carried by a \`defineWorkflow\` definition (its phantom \`__params\`). */
|
|
710
709
|
type WorkflowParamsOf<Definition> = Definition extends { __params?: infer Params } ? (unknown extends Params ? Record<string, unknown> : Params) : Record<string, unknown>;
|
|
711
|
-
|
|
710
|
+
`;
|
|
711
|
+
}
|
|
712
|
+
block += `
|
|
712
713
|
/** A typed reference to a durable workflow, addressable for \`cronJobs()\` targets. Mirrors \`@lunora/scheduler\`'s \`WorkflowReference\` structurally. */
|
|
713
714
|
export interface WorkflowReference<Params = Record<string, unknown>> {
|
|
714
715
|
readonly isLunoraWorkflow: true;
|
|
@@ -716,7 +717,14 @@ export interface WorkflowReference<Params = Record<string, unknown>> {
|
|
|
716
717
|
readonly binding: string;
|
|
717
718
|
readonly name: string;
|
|
718
719
|
}
|
|
719
|
-
|
|
720
|
+
`;
|
|
721
|
+
if (workflows.length > 0) {
|
|
722
|
+
const sorted = [...workflows].toSorted((a, b) => a.exportName.localeCompare(b.exportName));
|
|
723
|
+
const refMembers = sorted.map((workflow) => ` ${workflow.exportName}: WorkflowReference<WorkflowParamsOf<typeof lunoraWorkflowDefinitions.${workflow.exportName}>>;`).join("\n");
|
|
724
|
+
const objectMembers = sorted.map(
|
|
725
|
+
(workflow) => ` ${workflow.exportName}: { isLunoraWorkflow: true, binding: ${JSON.stringify(workflow.bindingName)}, name: ${JSON.stringify(workflow.exportName)} },`
|
|
726
|
+
).join("\n");
|
|
727
|
+
block += `
|
|
720
728
|
/** This project's durable workflows, addressable as typed references (e.g. \`crons.daily("digest", …, workflows.digestPipeline, params)\`). */
|
|
721
729
|
export interface WorkflowsRef {
|
|
722
730
|
${refMembers}
|
|
@@ -726,12 +734,141 @@ export const workflows: WorkflowsRef = {
|
|
|
726
734
|
${objectMembers}
|
|
727
735
|
};
|
|
728
736
|
`;
|
|
729
|
-
|
|
730
|
-
`
|
|
737
|
+
importLines.push(`import type * as lunoraWorkflowDefinitions from "../workflows.js";
|
|
738
|
+
`);
|
|
739
|
+
}
|
|
740
|
+
if (agents.length > 0) {
|
|
741
|
+
const sorted = [...agents].toSorted((a, b) => a.exportName.localeCompare(b.exportName));
|
|
742
|
+
const refMembers = sorted.map((agent) => ` ${agent.exportName}: WorkflowReference<AgentRunInput>;`).join("\n");
|
|
743
|
+
const objectMembers = sorted.map(
|
|
744
|
+
(agent) => ` ${agent.exportName}: { isLunoraWorkflow: true, binding: ${JSON.stringify(agent.bindingName)}, name: ${JSON.stringify(agent.name)} },`
|
|
745
|
+
).join("\n");
|
|
746
|
+
block += `
|
|
747
|
+
/** This project's durable agents, addressable as typed references for \`cronJobs()\` — each fire starts a fresh agent run (e.g. \`crons.daily("sweep", …, agents.support, { input, threadKey })\`). */
|
|
748
|
+
export interface AgentsRef {
|
|
749
|
+
${refMembers}
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
export const agents: AgentsRef = {
|
|
753
|
+
${objectMembers}
|
|
754
|
+
};
|
|
755
|
+
`;
|
|
756
|
+
importLines.push(`import type { AgentRunInput } from "@lunora/agent";
|
|
757
|
+
`);
|
|
758
|
+
}
|
|
759
|
+
return { block, importLine: importLines.join("") };
|
|
760
|
+
};
|
|
761
|
+
const agentRuntimeFunctionNames = () => Object.keys(agentComponent().functions).toSorted((a, b) => a.localeCompare(b));
|
|
762
|
+
const takenAgentFunctionNames = (functions) => new Set(functions.filter((definition) => sanitizeNamespace(definition.filePath) === "agents").map((definition) => definition.exportName));
|
|
763
|
+
const renderAgentFunctionRegistry = (agents, functions) => {
|
|
764
|
+
const empty = { importLine: "", lines: "", prelude: "" };
|
|
765
|
+
if (agents.length === 0) {
|
|
766
|
+
return empty;
|
|
767
|
+
}
|
|
768
|
+
const taken = takenAgentFunctionNames(functions);
|
|
769
|
+
const names = agentRuntimeFunctionNames().filter((name) => !taken.has(name));
|
|
770
|
+
if (names.length === 0) {
|
|
771
|
+
return empty;
|
|
772
|
+
}
|
|
773
|
+
return {
|
|
774
|
+
importLine: 'import { agentComponent } from "@lunora/agent/component";\n',
|
|
775
|
+
lines: names.map((name) => ` "agents:${name}": lunoraAgentRuntimeFunctions.${name} as unknown as RegisteredLunoraFunction,`).join("\n"),
|
|
776
|
+
prelude: "\n/**\n * The `@lunora/agent` runtime component — auto-registered because `lunora/agents.ts`\n * declares agents. The durable loop dispatches the internal mutations; clients\n * subscribe to the public queries (`agents:agentMessages` is the live thread view).\n */\nconst lunoraAgentRuntimeFunctions = agentComponent().functions;\n"
|
|
777
|
+
};
|
|
778
|
+
};
|
|
779
|
+
const renderSandboxFunctionRegistry = (usesSandbox, functions) => {
|
|
780
|
+
if (!usesSandbox) {
|
|
781
|
+
return { importLine: "", lines: "", prelude: "" };
|
|
782
|
+
}
|
|
783
|
+
const collision = functions.find((definition) => sanitizeNamespace(definition.filePath) === "sandbox" && definition.exportName === "invoke");
|
|
784
|
+
if (collision) {
|
|
785
|
+
throw new LunoraError(
|
|
786
|
+
"INTERNAL",
|
|
787
|
+
`@lunora/codegen: "sandbox:invoke" is reserved for the batteries-included sandbox tool dispatcher (browserTool/containerTool) — rename the "invoke" export in ${collision.filePath}`
|
|
788
|
+
);
|
|
789
|
+
}
|
|
790
|
+
return {
|
|
791
|
+
importLine: 'import { sandboxComponent } from "@lunora/agent/component";\n',
|
|
792
|
+
lines: ' "sandbox:invoke": lunoraSandbox.invoke as unknown as RegisteredLunoraFunction,',
|
|
793
|
+
prelude: "\n/**\n * The `@lunora/agent` sandbox dispatcher — auto-registered because `lunora/`\n * imports a sandbox tool (`browserTool`/`containerTool`). The batteries-included\n * tools dispatch to this internal action, which runs on an action ctx carrying\n * `ctx.browser` + `ctx.containers` (the durable tool step itself has neither).\n */\nconst lunoraSandbox = sandboxComponent();\n"
|
|
794
|
+
};
|
|
731
795
|
};
|
|
732
|
-
const
|
|
796
|
+
const syntheticAgentApiFunctions = (agents, functions) => {
|
|
797
|
+
if (agents.length === 0) {
|
|
798
|
+
return [];
|
|
799
|
+
}
|
|
800
|
+
const taken = takenAgentFunctionNames(functions);
|
|
801
|
+
const definitions = [
|
|
802
|
+
{
|
|
803
|
+
args: { key: { kind: "string" }, limit: { inner: { kind: "number" }, kind: "optional" } },
|
|
804
|
+
exportName: "agentMessages",
|
|
805
|
+
filePath: "agents",
|
|
806
|
+
kind: "query",
|
|
807
|
+
returnType: "Record<string, unknown>[]"
|
|
808
|
+
},
|
|
809
|
+
{
|
|
810
|
+
args: {
|
|
811
|
+
decision: {
|
|
812
|
+
kind: "union",
|
|
813
|
+
members: [
|
|
814
|
+
{ kind: "literal", literalValue: '"approve"' },
|
|
815
|
+
{ kind: "literal", literalValue: '"reject"' }
|
|
816
|
+
]
|
|
817
|
+
},
|
|
818
|
+
instanceId: { kind: "string" },
|
|
819
|
+
note: { inner: { kind: "string" }, kind: "optional" },
|
|
820
|
+
threadKey: { kind: "string" },
|
|
821
|
+
toolCallId: { kind: "string" }
|
|
822
|
+
},
|
|
823
|
+
exportName: "agentResolveApproval",
|
|
824
|
+
filePath: "agents",
|
|
825
|
+
kind: "mutation",
|
|
826
|
+
returnType: "{ resolved: boolean }"
|
|
827
|
+
},
|
|
828
|
+
{
|
|
829
|
+
args: {
|
|
830
|
+
agent: { kind: "string" },
|
|
831
|
+
input: { kind: "string" },
|
|
832
|
+
threadKey: { kind: "string" },
|
|
833
|
+
title: { inner: { kind: "string" }, kind: "optional" }
|
|
834
|
+
},
|
|
835
|
+
exportName: "agentRun",
|
|
836
|
+
filePath: "agents",
|
|
837
|
+
kind: "mutation",
|
|
838
|
+
returnType: "{ id: string; threadKey: string }"
|
|
839
|
+
},
|
|
840
|
+
{
|
|
841
|
+
args: { key: { kind: "string" } },
|
|
842
|
+
exportName: "agentState",
|
|
843
|
+
filePath: "agents",
|
|
844
|
+
kind: "query",
|
|
845
|
+
returnType: "Record<string, unknown> | undefined"
|
|
846
|
+
},
|
|
847
|
+
{
|
|
848
|
+
args: { key: { kind: "string" } },
|
|
849
|
+
exportName: "agentThread",
|
|
850
|
+
filePath: "agents",
|
|
851
|
+
kind: "query",
|
|
852
|
+
returnType: "Record<string, unknown> | undefined"
|
|
853
|
+
}
|
|
854
|
+
];
|
|
855
|
+
for (const agent of agents) {
|
|
856
|
+
if (agent.voice) {
|
|
857
|
+
definitions.push({
|
|
858
|
+
args: { threadKey: { kind: "string" } },
|
|
859
|
+
exportName: `${agent.exportName}Voice`,
|
|
860
|
+
filePath: "agents",
|
|
861
|
+
kind: "stream",
|
|
862
|
+
returnType: "Record<string, unknown>"
|
|
863
|
+
});
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
return definitions.filter((definition) => !taken.has(definition.exportName));
|
|
867
|
+
};
|
|
868
|
+
const emitApi = (options) => {
|
|
869
|
+
const { agents = [], functions, useUmbrella = false, workflows = [] } = options;
|
|
733
870
|
const base = baseSpecifiers(useUmbrella);
|
|
734
|
-
const publicFunctions = functions.filter((definition) => definition.visibility !== "internal");
|
|
871
|
+
const publicFunctions = [...functions.filter((definition) => definition.visibility !== "internal"), ...syntheticAgentApiFunctions(agents, functions)];
|
|
735
872
|
const internalFunctions = functions.filter((definition) => definition.visibility === "internal");
|
|
736
873
|
const publicBody = renderApiBody(publicFunctions);
|
|
737
874
|
const internalBody = renderApiBody(internalFunctions);
|
|
@@ -747,10 +884,10 @@ ${publicBody}
|
|
|
747
884
|
const internalBlock = internalBody ? `
|
|
748
885
|
${internalBody}
|
|
749
886
|
` : "";
|
|
750
|
-
const
|
|
887
|
+
const schedulerReferences = renderSchedulerReferences(workflows, agents);
|
|
751
888
|
return `${GENERATED_HEADER}import { anyApi } from "${base.serverTypes}";
|
|
752
889
|
import type { FunctionReference } from "${base.client}";
|
|
753
|
-
${
|
|
890
|
+
${schedulerReferences.importLine}${dataModelImportLine}
|
|
754
891
|
export interface ApiTypes {${apiBlock}}
|
|
755
892
|
|
|
756
893
|
export const api = anyApi as unknown as ApiTypes;
|
|
@@ -759,7 +896,7 @@ export const api = anyApi as unknown as ApiTypes;
|
|
|
759
896
|
export interface InternalApiTypes {${internalBlock}}
|
|
760
897
|
|
|
761
898
|
export const internal = anyApi as unknown as InternalApiTypes;
|
|
762
|
-
${
|
|
899
|
+
${schedulerReferences.block}`;
|
|
763
900
|
};
|
|
764
901
|
const emitSeed = (enabled) => {
|
|
765
902
|
if (!enabled) {
|
|
@@ -928,6 +1065,7 @@ const buildStorageBucketNames = (schema, ruleBuckets = []) => {
|
|
|
928
1065
|
return ["default", ...[...named].toSorted((a, b) => a.localeCompare(b))];
|
|
929
1066
|
};
|
|
930
1067
|
const emitServer = ({
|
|
1068
|
+
agents = [],
|
|
931
1069
|
containers = [],
|
|
932
1070
|
env,
|
|
933
1071
|
hasAccessFacade = false,
|
|
@@ -986,6 +1124,11 @@ const emitServer = ({
|
|
|
986
1124
|
assertIdentifier(queue.bindingName, `queue binding "${queue.bindingName}"`);
|
|
987
1125
|
return ` /** Queue producer binding for the \`${queue.exportName}\` queue. */
|
|
988
1126
|
readonly ${queue.bindingName}?: unknown;`;
|
|
1127
|
+
}),
|
|
1128
|
+
...agents.map((agent) => {
|
|
1129
|
+
assertIdentifier(agent.bindingName, `agent binding "${agent.bindingName}"`);
|
|
1130
|
+
return ` /** Workflow binding for the \`${agent.exportName}\` agent. */
|
|
1131
|
+
readonly ${agent.bindingName}?: unknown;`;
|
|
989
1132
|
})
|
|
990
1133
|
].join("\n");
|
|
991
1134
|
const envBlock = `
|
|
@@ -1056,6 +1199,20 @@ ${queues.map((queue) => ` readonly ${queue.exportName}: QueueProducer<QueueBo
|
|
|
1056
1199
|
}` : "";
|
|
1057
1200
|
const queuesContextField = hasQueues ? `
|
|
1058
1201
|
readonly queues: LunoraQueues;` : "";
|
|
1202
|
+
const hasAgents = agents.length > 0;
|
|
1203
|
+
const agentsTypeImport = hasAgents ? `import type { AgentHandle } from "@lunora/agent";
|
|
1204
|
+
` : "";
|
|
1205
|
+
const agentsTypeBlock = hasAgents ? `
|
|
1206
|
+
|
|
1207
|
+
/** This project's declared agents, addressable from \`ctx.agents\` by their \`lunora/agents.ts\` export name. */
|
|
1208
|
+
export interface LunoraAgents {
|
|
1209
|
+
${agents.map((agent) => {
|
|
1210
|
+
assertIdentifier(agent.exportName, `agent export "${agent.exportName}"`);
|
|
1211
|
+
return ` readonly ${agent.exportName}: AgentHandle;`;
|
|
1212
|
+
}).join("\n")}
|
|
1213
|
+
}` : "";
|
|
1214
|
+
const agentsContextField = hasAgents ? `
|
|
1215
|
+
readonly agents: LunoraAgents;` : "";
|
|
1059
1216
|
const identityTypeImport = identity ? `import type { InferIdentity } from "${base.server}";
|
|
1060
1217
|
import type * as lunoraIdentityContract from "../identity.js";
|
|
1061
1218
|
` : "";
|
|
@@ -1101,11 +1258,11 @@ import type {
|
|
|
1101
1258
|
} from "${base.server}";
|
|
1102
1259
|
|
|
1103
1260
|
import type { DataModel, DatabaseReaderFacade, DatabaseWriterFacade, Doc, Id as IdOfTable, OrmReader, OrmWriter, Relations, TableName } from "./dataModel.js";
|
|
1104
|
-
${aiTypeImport}${paymentsTypeImport}${x402TypeImport}${containersTypeImport}${workflowsTypeImport}${queuesTypeImport}${identityTypeImport}${envTypeImport}
|
|
1261
|
+
${aiTypeImport}${paymentsTypeImport}${x402TypeImport}${containersTypeImport}${workflowsTypeImport}${queuesTypeImport}${agentsTypeImport}${identityTypeImport}${envTypeImport}
|
|
1105
1262
|
export type { DataModel, Doc, Id, TableName } from "./dataModel.js";
|
|
1106
1263
|
|
|
1107
1264
|
/** Storage buckets this schema declares (\`v.storage("name")\`), narrowing \`ctx.storage.bucket(name)\`. */
|
|
1108
|
-
export type StorageBucketName = ${storageBucketUnion};${envBlock}${workflowsTypeBlock}${queuesTypeBlock}${identityTypeBlock}${envTypeBlock}
|
|
1265
|
+
export type StorageBucketName = ${storageBucketUnion};${envBlock}${workflowsTypeBlock}${queuesTypeBlock}${agentsTypeBlock}${identityTypeBlock}${envTypeBlock}
|
|
1109
1266
|
|
|
1110
1267
|
/**
|
|
1111
1268
|
* Project-typed contexts. The base contexts from \`@lunora/server\` are
|
|
@@ -1143,13 +1300,13 @@ export interface QueryCtx extends Omit<QueryCtxBase, "db" | "storage"${authOmit}
|
|
|
1143
1300
|
export interface MutationCtx extends Omit<MutationCtxBase, "db" | "storage"${workflowsOmit}${authOmit}${envOmit}> {
|
|
1144
1301
|
readonly db: Omit<DatabaseWriter, "query" | "get"> & DatabaseWriterFacade & { query: TypedTableQuery; get: TypedTableGet };
|
|
1145
1302
|
readonly orm: OrmWriter;
|
|
1146
|
-
readonly storage: ReadOnlyStorage<StorageBucketName>;${accessContextField}${kvContextField}${flagsContextField}${analyticsContextField}${envContextField}${workflowsContextField}${queuesContextField}${authContextField}
|
|
1303
|
+
readonly storage: ReadOnlyStorage<StorageBucketName>;${accessContextField}${kvContextField}${flagsContextField}${analyticsContextField}${envContextField}${workflowsContextField}${queuesContextField}${agentsContextField}${authContextField}
|
|
1147
1304
|
}
|
|
1148
1305
|
|
|
1149
1306
|
export interface ActionCtx extends Omit<ActionCtxBase, "db" | "storage"${workflowsOmit}${authOmit}${envOmit}> {
|
|
1150
1307
|
readonly db: Omit<DatabaseWriter, "query" | "get"> & DatabaseWriterFacade & { query: TypedTableQuery; get: TypedTableGet };
|
|
1151
1308
|
readonly orm: OrmWriter;
|
|
1152
|
-
readonly storage: StorageBase<StorageBucketName>;${accessContextField}${aiActionField}${paymentsActionField}${x402ActionField}${containersActionField}${kvContextField}${flagsContextField}${hyperdriveActionField}${browserActionField}${imagesActionField}${analyticsContextField}${pipelinesActionField}${r2sqlActionField}${envContextField}${workflowsContextField}${queuesContextField}${authContextField}
|
|
1309
|
+
readonly storage: StorageBase<StorageBucketName>;${accessContextField}${aiActionField}${paymentsActionField}${x402ActionField}${containersActionField}${kvContextField}${flagsContextField}${hyperdriveActionField}${browserActionField}${imagesActionField}${analyticsContextField}${pipelinesActionField}${r2sqlActionField}${envContextField}${workflowsContextField}${queuesContextField}${agentsContextField}${authContextField}
|
|
1153
1310
|
}
|
|
1154
1311
|
|
|
1155
1312
|
/**
|
|
@@ -1219,10 +1376,22 @@ const renderLifecycleManifest = (functions) => {
|
|
|
1219
1376
|
}
|
|
1220
1377
|
return manifest;
|
|
1221
1378
|
};
|
|
1222
|
-
const emitFunctions = (
|
|
1379
|
+
const emitFunctions = (options) => {
|
|
1380
|
+
const { agents = [], functions, migrations = [], mutators = [], shapes = [], useUmbrella = false, usesSandbox = false } = options;
|
|
1223
1381
|
const hasFunctions = functions.length > 0;
|
|
1224
1382
|
const base = baseSpecifiers(useUmbrella);
|
|
1225
1383
|
const { dispatchBody, importBlock, installBlock, migrationBody, mutatorPaths, shapeBody } = renderFunctionRegistry(functions, migrations, mutators, shapes);
|
|
1384
|
+
const agentRegistry = renderAgentFunctionRegistry(agents, functions);
|
|
1385
|
+
const sandboxRegistry = renderSandboxFunctionRegistry(usesSandbox, functions);
|
|
1386
|
+
const autoLines = [agentRegistry.lines, sandboxRegistry.lines].filter((block) => block.length > 0).join("\n");
|
|
1387
|
+
let dispatchBodyWithAgents = dispatchBody;
|
|
1388
|
+
if (autoLines.length > 0) {
|
|
1389
|
+
dispatchBodyWithAgents = dispatchBody.length > 0 ? `${dispatchBody.trimEnd()}
|
|
1390
|
+
${autoLines}
|
|
1391
|
+
` : `
|
|
1392
|
+
${autoLines}
|
|
1393
|
+
`;
|
|
1394
|
+
}
|
|
1226
1395
|
const lifecycleHooks = renderLifecycleManifest(functions);
|
|
1227
1396
|
const shapeTypeImport = shapes.length > 0 ? `import type { RegisteredShape } from "${base.server}";
|
|
1228
1397
|
` : "";
|
|
@@ -1267,7 +1436,7 @@ ${caller.implementation}
|
|
|
1267
1436
|
const callRegisteredHelper = hasFunctions ? `${CALL_REGISTERED_HELPER}
|
|
1268
1437
|
|
|
1269
1438
|
` : "";
|
|
1270
|
-
return `${GENERATED_HEADER}${importBlock}${compiledArgsImport}${shapeTypeImport}import { LunoraError } from "${base.server}";
|
|
1439
|
+
return `${GENERATED_HEADER}${importBlock}${agentRegistry.importLine}${sandboxRegistry.importLine}${compiledArgsImport}${shapeTypeImport}import { LunoraError } from "${base.server}";
|
|
1271
1440
|
import type { ActionCtx, MutationCtx, QueryCtx } from "./server.js";
|
|
1272
1441
|
${dataModelImport}
|
|
1273
1442
|
/**
|
|
@@ -1288,12 +1457,12 @@ export interface RegisteredLunoraFunction {
|
|
|
1288
1457
|
/** \`"internal"\` functions are rejected on the external RPC path; absence === public. */
|
|
1289
1458
|
visibility?: "internal" | "public";
|
|
1290
1459
|
}
|
|
1291
|
-
|
|
1460
|
+
${agentRegistry.prelude}${sandboxRegistry.prelude}
|
|
1292
1461
|
/**
|
|
1293
1462
|
* Static dispatch table. The key matches the \`__lunoraRef\` the client
|
|
1294
1463
|
* emits (\`api[namespace][fn].__lunoraRef === "namespace:fn"\`).
|
|
1295
1464
|
*/
|
|
1296
|
-
export const LUNORA_FUNCTIONS: Record<string, RegisteredLunoraFunction> = {${
|
|
1465
|
+
export const LUNORA_FUNCTIONS: Record<string, RegisteredLunoraFunction> = {${dispatchBodyWithAgents}};
|
|
1297
1466
|
${compiledArgsInstall}${shapeRegistry}${mutatorPathsRegistry}
|
|
1298
1467
|
/**
|
|
1299
1468
|
* Connection-lifecycle manifest: the function paths the generated ShardDO
|
|
@@ -1912,10 +2081,11 @@ const emitContainerFragments = (containers, jurisdiction) => {
|
|
|
1912
2081
|
}).join("\n");
|
|
1913
2082
|
return {
|
|
1914
2083
|
// Schema `.jurisdiction("…")` pins every container DO this shard reaches
|
|
1915
|
-
// to the data-residency region
|
|
1916
|
-
//
|
|
2084
|
+
// to the data-residency region (`undefined` when undeclared). The trailing
|
|
2085
|
+
// `this.getCurrentTraceparent()` forwards the inbound RPC's W3C trace
|
|
2086
|
+
// context onto outbound container fetches so their spans join the trace.
|
|
1917
2087
|
build: `
|
|
1918
|
-
const containers = createContainerContext(env, LUNORA_CONTAINERS${jurisdiction ?
|
|
2088
|
+
const containers = createContainerContext(env, LUNORA_CONTAINERS, ${jurisdiction ? JSON.stringify(jurisdiction) : "undefined"}, this.getCurrentTraceparent());
|
|
1919
2089
|
`,
|
|
1920
2090
|
contextField: `
|
|
1921
2091
|
containers,`,
|
|
@@ -1966,6 +2136,62 @@ type WorkflowParamsOf<Definition> = Definition extends { __params?: infer Params
|
|
|
1966
2136
|
/** Output type carried by a \`defineWorkflow\` definition (its phantom \`__output\`), so each entrypoint keeps the authored return type. */
|
|
1967
2137
|
type WorkflowOutputOf<Definition> = Definition extends { __output?: infer Output } ? Output : unknown;
|
|
1968
2138
|
|
|
2139
|
+
${classes}`;
|
|
2140
|
+
};
|
|
2141
|
+
const emitAgents = (agents) => {
|
|
2142
|
+
if (agents.length === 0) {
|
|
2143
|
+
return "";
|
|
2144
|
+
}
|
|
2145
|
+
const voiceAgents = agents.filter((agent) => agent.voice);
|
|
2146
|
+
const hasVoice = voiceAgents.length > 0;
|
|
2147
|
+
const classes = agents.map((agent) => {
|
|
2148
|
+
assertIdentifier(agent.exportName, `agent export "${agent.exportName}"`);
|
|
2149
|
+
assertIdentifier(agent.className, `agent class "${agent.className}"`);
|
|
2150
|
+
const workflowClass = `/** WorkflowEntrypoint for the \`${agent.exportName}\` agent (binding \`${agent.bindingName}\`). */
|
|
2151
|
+
export class ${agent.className} extends LunoraWorkflow<AgentRunInput, AgentRunResult> {
|
|
2152
|
+
public constructor(ctx: ConstructorParameters<typeof LunoraWorkflow>[0], env: Record<string, unknown>) {
|
|
2153
|
+
super(ctx, env, compileAgentWorkflow(${agent.exportName}, "${agent.exportName}"), "${agent.exportName}");
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
`;
|
|
2157
|
+
if (!agent.voice) {
|
|
2158
|
+
return workflowClass;
|
|
2159
|
+
}
|
|
2160
|
+
const voiceClassName = agent.voiceClassName ?? "";
|
|
2161
|
+
const voiceBindingName = agent.voiceBindingName ?? "";
|
|
2162
|
+
assertIdentifier(voiceClassName, `agent voice class "${voiceClassName}"`);
|
|
2163
|
+
const voiceClass = `
|
|
2164
|
+
/** Voice-session Durable Object for the \`${agent.exportName}\` agent (binding \`${voiceBindingName}\`). */
|
|
2165
|
+
export class ${voiceClassName} extends VoiceSessionDO {
|
|
2166
|
+
public constructor(ctx: ConstructorParameters<typeof VoiceSessionDO>[0], env: Record<string, unknown>) {
|
|
2167
|
+
super(ctx, env, ${agent.exportName}, "${agent.exportName}");
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
`;
|
|
2171
|
+
return `${workflowClass}${voiceClass}`;
|
|
2172
|
+
}).join("\n");
|
|
2173
|
+
const imports = agents.map((agent) => agent.exportName).join(", ");
|
|
2174
|
+
const runtimeImport = hasVoice ? `import { compileAgentWorkflow, VoiceSessionDO } from "@lunora/agent";` : `import { compileAgentWorkflow } from "@lunora/agent";`;
|
|
2175
|
+
const voiceHeaderNote = hasVoice ? `
|
|
2176
|
+
*
|
|
2177
|
+
* Voice-enabled agents ALSO get a \`VoiceSessionDO\` subclass here — a real
|
|
2178
|
+
* Durable Object (unlike the Workflow), so wrangler needs both its
|
|
2179
|
+
* \`durable_objects\` binding and its \`class_name\` export (the same re-export
|
|
2180
|
+
* line covers it).` : "";
|
|
2181
|
+
return `${GENERATED_HEADER}/**
|
|
2182
|
+
* WorkflowEntrypoint classes for the agents declared in \`lunora/agents.ts\`.
|
|
2183
|
+
* Each \`defineAgent\` compiles a replay-safe tool-loop onto a Cloudflare
|
|
2184
|
+
* Workflow. Re-export them from your worker entry — wrangler requires each
|
|
2185
|
+
* \`workflows[].class_name\` to be exported by the worker:
|
|
2186
|
+
*
|
|
2187
|
+
* \`export * from "./lunora/_generated/agents.js";\`${voiceHeaderNote}
|
|
2188
|
+
*/
|
|
2189
|
+
import LunoraWorkflow from "@lunora/workflow/do";
|
|
2190
|
+
${runtimeImport}
|
|
2191
|
+
import type { AgentRunInput, AgentRunResult } from "@lunora/agent";
|
|
2192
|
+
|
|
2193
|
+
import { ${imports} } from "../agents.js";
|
|
2194
|
+
|
|
1969
2195
|
${classes}`;
|
|
1970
2196
|
};
|
|
1971
2197
|
const emitQueues = (queues) => {
|
|
@@ -2044,6 +2270,31 @@ ${specEntries}
|
|
|
2044
2270
|
`
|
|
2045
2271
|
};
|
|
2046
2272
|
};
|
|
2273
|
+
const emitAgentFragments = (agents) => {
|
|
2274
|
+
if (agents.length === 0) {
|
|
2275
|
+
return { build: "", contextField: "", importLines: [], specs: "" };
|
|
2276
|
+
}
|
|
2277
|
+
for (const agent of agents) {
|
|
2278
|
+
assertIdentifier(agent.exportName, `agent export "${agent.exportName}"`);
|
|
2279
|
+
assertIdentifier(agent.bindingName, `agent binding "${agent.bindingName}"`);
|
|
2280
|
+
}
|
|
2281
|
+
const specEntries = agents.map((agent) => ` { binding: "${agent.bindingName}", exportName: "${agent.exportName}"${agent.publicRun === true ? ", publicRun: true" : ""} },`).join("\n");
|
|
2282
|
+
return {
|
|
2283
|
+
build: `
|
|
2284
|
+
const agents = createAgentContext(env, LUNORA_AGENTS);
|
|
2285
|
+
`,
|
|
2286
|
+
contextField: `
|
|
2287
|
+
agents,`,
|
|
2288
|
+
importLines: [`import { createAgentContext } from "@lunora/agent";`, `import type { AgentBindingSpec } from "@lunora/agent";`],
|
|
2289
|
+
// eslint-disable-next-line no-secrets/no-secrets -- the emitted readonly-array type annotation is dense generated TS, not a credential
|
|
2290
|
+
specs: `
|
|
2291
|
+
/** Wiring specs for \`ctx.agents\` (codegen-derived from \`lunora/agents.ts\`). */
|
|
2292
|
+
const LUNORA_AGENTS: ReadonlyArray<AgentBindingSpec> = [
|
|
2293
|
+
${specEntries}
|
|
2294
|
+
];
|
|
2295
|
+
`
|
|
2296
|
+
};
|
|
2297
|
+
};
|
|
2047
2298
|
const emitWorkflowsMetadataFragments = (workflows) => {
|
|
2048
2299
|
if (workflows.length === 0) {
|
|
2049
2300
|
return { constant: "", override: "" };
|
|
@@ -2207,6 +2458,7 @@ const buildDoTypeImports = (hasVectors, hasWorkflows, hasQueues, hasFlags) => [
|
|
|
2207
2458
|
];
|
|
2208
2459
|
const emitShard = ({
|
|
2209
2460
|
advisories = [],
|
|
2461
|
+
agents = [],
|
|
2210
2462
|
containers = [],
|
|
2211
2463
|
env,
|
|
2212
2464
|
flagKeys = [],
|
|
@@ -2261,6 +2513,7 @@ const emitShard = ({
|
|
|
2261
2513
|
importLines: workflowImportLines,
|
|
2262
2514
|
specs: workflowSpecs
|
|
2263
2515
|
} = emitWorkflowFragments(workflows);
|
|
2516
|
+
const { build: agentsBuild, contextField: agentsContextField, importLines: agentImportLines, specs: agentSpecs } = emitAgentFragments(agents);
|
|
2264
2517
|
const {
|
|
2265
2518
|
build: paymentsBuild,
|
|
2266
2519
|
configField: paymentsConfigField,
|
|
@@ -2407,6 +2660,7 @@ const LUNORA_RLS_READ_REGISTRY = buildRlsReadRegistry(Object.values(LUNORA_FUNCT
|
|
|
2407
2660
|
...containerImportLines,
|
|
2408
2661
|
...workflowImportLines,
|
|
2409
2662
|
...queueImportLines,
|
|
2663
|
+
...agentImportLines,
|
|
2410
2664
|
...paymentsImports,
|
|
2411
2665
|
...x402Imports,
|
|
2412
2666
|
``,
|
|
@@ -2748,7 +3002,7 @@ const LUNORA_STORAGE_RULES: StorageRulesResult = ${JSON.stringify(storageRulesDa
|
|
|
2748
3002
|
|
|
2749
3003
|
/** Which optional package-backed features this app wires up (discovered from imports / \`ctx.*\` reads / schema signals) served via \`__lunora_admin__:studioFeatures\` so the studio hides nav pages whose package isn't enabled. */
|
|
2750
3004
|
const LUNORA_STUDIO_FEATURES: StudioFeaturesResult = ${JSON.stringify(studioFeaturesData, void 0, 4)};
|
|
2751
|
-
${flagsOverrides.constant}${shapeReadRegistryConst}${workflowsMetadataConst}${queuesMetadataConst}${containerSpecs}${workflowSpecs}${queueSpecs}
|
|
3005
|
+
${flagsOverrides.constant}${shapeReadRegistryConst}${workflowsMetadataConst}${queuesMetadataConst}${containerSpecs}${workflowSpecs}${queueSpecs}${agentSpecs}
|
|
2752
3006
|
export interface ShardDOConfig {
|
|
2753
3007
|
/** Opt into change-data-capture: records a post-image to \`__cdc_log\` on every write (backs streaming export + replay-PITR). */
|
|
2754
3008
|
cdc?: boolean;
|
|
@@ -3164,7 +3418,7 @@ ${flagsOverrides.evaluateOverride}${flagsOverrides.subscriptionOverride}${workfl
|
|
|
3164
3418
|
// dispatch path) fall back to the per-request fields as before.
|
|
3165
3419
|
const userId = options.identity ? options.identity.userId : this.getCurrentUserId();
|
|
3166
3420
|
const identity = options.identity ? options.identity.identity : this.getCurrentIdentity();
|
|
3167
|
-
${vectorsBuild}${aiBuild}${everyContextBuild}${containersBuild}${workflowsBuild}${queuesBuild}
|
|
3421
|
+
${vectorsBuild}${aiBuild}${everyContextBuild}${containersBuild}${workflowsBuild}${queuesBuild}${agentsBuild}
|
|
3168
3422
|
const scheduler = (config.scheduler?.(env) ?? schedulerStub) as SchedulerLike;
|
|
3169
3423
|
// Build the storage adapter once and share it between \`ctx.storage\`
|
|
3170
3424
|
// and \`ctx.db.system._storage\` so both read the same R2 binding. The
|
|
@@ -3206,7 +3460,7 @@ ${facadeBlock}${paymentsBuild}
|
|
|
3206
3460
|
log,
|
|
3207
3461
|
now,${ormContextField}
|
|
3208
3462
|
scheduler,
|
|
3209
|
-
storage,${vectorsContextField}${aiContextField}${everyContextField}${paymentsContextField}${containersContextField}${workflowsContextField}${queuesContextField}
|
|
3463
|
+
storage,${vectorsContextField}${aiContextField}${everyContextField}${paymentsContextField}${containersContextField}${workflowsContextField}${queuesContextField}${agentsContextField}
|
|
3210
3464
|
};
|
|
3211
3465
|
${isActionLine}${actionOnlyBlock}
|
|
3212
3466
|
ctx.runAction = (reference: FunctionReference, fnArgs: Record<string, unknown>) => dispatchRun("action", reference.__lunoraRef, fnArgs, ctx);
|
|
@@ -3470,4 +3724,4 @@ const emitWranglerCronTriggers = (crons) => {
|
|
|
3470
3724
|
return triggers;
|
|
3471
3725
|
};
|
|
3472
3726
|
|
|
3473
|
-
export { APP_METHOD_CAPABILITIES as A, CAPABILITIES as C, GENERATED_HEADER as G,
|
|
3727
|
+
export { APP_METHOD_CAPABILITIES as A, CAPABILITIES as C, GENERATED_HEADER as G, emitApi as a, emitCollections as b, emitContainers as c, emitCrons as d, emitAgents as e, emitDataModel as f, emitDrizzleSchema as g, emitFunctions as h, emitServer as i, emitShard as j, emitVectors as k, emitWorkflows as l, emitWranglerCronTriggers as m, buildStorageColumns as n, emitQueues as o, emitSeed as p };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as APP_METHOD_CAPABILITIES, G as GENERATED_HEADER } from './emit-
|
|
1
|
+
import { A as APP_METHOD_CAPABILITIES, G as GENERATED_HEADER } from './emit-DtherCpQ.mjs';
|
|
2
2
|
|
|
3
3
|
const hasFlagKey = (key) => `has${key.charAt(0).toUpperCase()}${key.slice(1)}`;
|
|
4
4
|
const LONG_TAIL = APP_METHOD_CAPABILITIES.map(
|
|
@@ -11,6 +11,9 @@ const buildAccessImports = (hasAccess, hasAuth) => hasAccess ? [
|
|
|
11
11
|
`import { createAccessResolver${hasAuth ? ", composeResolvers" : ""} } from "@lunora/cloudflare-access";`
|
|
12
12
|
] : [];
|
|
13
13
|
const buildKvImports = (hasKv) => hasKv ? [`import { createKvIntrospectorFromEnv } from "@lunora/bindings/kv";`] : [];
|
|
14
|
+
const hasEmailAgents = (options) => (options.emailAgents?.length ?? 0) > 0;
|
|
15
|
+
const buildInboundImports = (options) => hasEmailAgents(options) ? [`import { dispatchAgentEmail } from "@lunora/agent/inbound";`] : [];
|
|
16
|
+
const buildAgentDefinitionsImport = (options) => hasEmailAgents(options) ? [`import * as lunoraAgentDefinitions from "../agents.js";`] : [];
|
|
14
17
|
const buildImportLines = (options) => {
|
|
15
18
|
const {
|
|
16
19
|
hasAccess,
|
|
@@ -59,10 +62,12 @@ const buildImportLines = (options) => {
|
|
|
59
62
|
...hasScheduler ? [`import type { DurableObjectNamespaceLike } from "@lunora/scheduler";`, `import { createScheduler } from "@lunora/scheduler";`] : [],
|
|
60
63
|
...hasStorage ? [`import type { R2BucketLike, Storage } from "@lunora/storage";`, `import { createBucketStorage, createStorage } from "@lunora/storage";`] : [],
|
|
61
64
|
...hasWorkflow ? [`import { createWorkflowsRestClient } from "@lunora/workflow";`] : [],
|
|
65
|
+
...buildInboundImports(options),
|
|
62
66
|
`import type { ${[...runtimeTypeImports].toSorted((a, b) => a.localeCompare(b)).join(", ")} } from "${runtimeModule}";`,
|
|
63
67
|
`import { ${runtimeValueImports} } from "${runtimeModule}";`,
|
|
64
68
|
``,
|
|
65
69
|
...buildIdentityImports(options.identity),
|
|
70
|
+
...buildAgentDefinitionsImport(options),
|
|
66
71
|
...hasGlobal || hasHyperdriveGlobal ? [`import schema from "../schema.js";`] : [],
|
|
67
72
|
`import { LUNORA_CRONS } from "./crons.js";`,
|
|
68
73
|
`import { LUNORA_FUNCTIONS } from "./functions.js";`,
|
|
@@ -383,6 +388,18 @@ const buildWorkerOptionLines = (options) => [
|
|
|
383
388
|
}` : ` if (this.accessSelector) {
|
|
384
389
|
options.resolveIdentity = createAccessResolver(this.accessSelector(env));
|
|
385
390
|
}`
|
|
391
|
+
] : [],
|
|
392
|
+
// Voice-enabled agents: map each export name to its `VOICE_*` Durable Object
|
|
393
|
+
// namespace so the runtime serves `/_lunora/voice/<exportName>`. Read off
|
|
394
|
+
// `env` structurally (the binding is provisioned by the config layer's
|
|
395
|
+
// reconcile step, so it may not be on the generated `Env` type). Emitted only
|
|
396
|
+
// when at least one agent opted into voice — voice-free output is unchanged.
|
|
397
|
+
...options.voiceAgents && options.voiceAgents.length > 0 ? [
|
|
398
|
+
` options.voiceAgents = {
|
|
399
|
+
${options.voiceAgents.map(
|
|
400
|
+
(agent) => ` ${JSON.stringify(agent.exportName)}: (env as Record<string, unknown>)[${JSON.stringify(agent.bindingName)}] as ShardNamespaceLike,`
|
|
401
|
+
).join("\n")}
|
|
402
|
+
};`
|
|
386
403
|
] : []
|
|
387
404
|
];
|
|
388
405
|
const buildBaseWorkerOptions = (options) => [
|
|
@@ -554,6 +571,12 @@ const emitApp = (options) => {
|
|
|
554
571
|
const buildWorkerLine = options.hasFramework ? ` const buildWorker = (env: Env): LunoraWorker =>
|
|
555
572
|
host ? withFrameworkWorker(host, (hostEnv) => this.buildWorkerOptions(hostEnv as Env, ${getAuthArgument})) : createWorker(this.buildWorkerOptions(env, ${getAuthArgument}));` : ` const buildWorker = (env: Env): LunoraWorker => createWorker(this.buildWorkerOptions(env, ${getAuthArgument}));`;
|
|
556
573
|
const assembleParameter = options.hasFramework ? `host?: FrameworkHostHandler` : ``;
|
|
574
|
+
const emailAgents = options.emailAgents ?? [];
|
|
575
|
+
const emailAgentsBlock = emailAgents.length > 0 ? ` composed.email = dispatchAgentEmail([
|
|
576
|
+
${emailAgents.map((agent) => ` { agent: lunoraAgentDefinitions.${agent.exportName}, binding: ${JSON.stringify(agent.bindingName)} },`).join("\n")}
|
|
577
|
+
]);
|
|
578
|
+
|
|
579
|
+
` : "";
|
|
557
580
|
const buildTerminals = ` /** Materialise the standalone Cloudflare worker + \`ShardDO\` class. */
|
|
558
581
|
public build(): ComposedApp {
|
|
559
582
|
return this.assemble();
|
|
@@ -630,7 +653,7 @@ ${buildWorkerLine}
|
|
|
630
653
|
},` : ""}
|
|
631
654
|
};
|
|
632
655
|
|
|
633
|
-
if (this.emailHandler) {
|
|
656
|
+
${emailAgentsBlock} if (this.emailHandler) {
|
|
634
657
|
const handler = this.emailHandler;
|
|
635
658
|
|
|
636
659
|
composed.email = (message, rawEnv, context) => handler(rawEnv as Env)(message, rawEnv, context);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/codegen",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.43",
|
|
4
4
|
"description": "Code generator for Lunora: emits _generated/{api,server,dataModel}.ts from your schema",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare",
|
|
@@ -46,11 +46,12 @@
|
|
|
46
46
|
"access": "public"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@lunora/advisor": "1.0.0-alpha.
|
|
50
|
-
"@lunora/
|
|
49
|
+
"@lunora/advisor": "1.0.0-alpha.27",
|
|
50
|
+
"@lunora/agent": "1.0.0-alpha.1",
|
|
51
|
+
"@lunora/container": "1.0.0-alpha.11",
|
|
51
52
|
"@lunora/errors": "1.0.0-alpha.4",
|
|
52
53
|
"@lunora/queue": "1.0.0-alpha.7",
|
|
53
|
-
"@lunora/scheduler": "1.0.0-alpha.
|
|
54
|
+
"@lunora/scheduler": "1.0.0-alpha.9",
|
|
54
55
|
"@lunora/values": "1.0.0-alpha.7",
|
|
55
56
|
"@lunora/workflow": "1.0.0-alpha.9",
|
|
56
57
|
"ts-morph": "^28.0.0"
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import '@lunora/errors';
|
|
2
|
-
export { G as GENERATED_HEADER, m as buildStorageColumns, e as emitApi, a as emitCollections, b as emitContainers, c as emitCrons, d as emitDataModel, f as emitDrizzleSchema, g as emitFunctions, n as emitQueues, o as emitSeed, h as emitServer, i as emitShard, j as emitVectors, k as emitWorkflows, l as emitWranglerCronTriggers } from './emit-CFz-9dcD.mjs';
|
|
3
|
-
import './paths-BRd6JHuF.mjs';
|