@telorun/kernel 0.2.4 → 0.2.5

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.
Files changed (135) hide show
  1. package/dist/boot-context-registry.d.ts.map +1 -1
  2. package/dist/boot-context-registry.js +6 -6
  3. package/dist/boot-context-registry.js.map +1 -1
  4. package/dist/capabilities/capabilities/component.yaml +2 -1
  5. package/dist/capabilities/capabilities/executable.yaml +2 -1
  6. package/dist/capabilities/capabilities/handler.yaml +2 -1
  7. package/dist/capabilities/capabilities/listener.yaml +2 -1
  8. package/dist/capabilities/capabilities/provider.yaml +2 -1
  9. package/dist/capabilities/capabilities/template.yaml +2 -1
  10. package/dist/capabilities/capabilities/type.yaml +2 -1
  11. package/dist/capabilities/component.yaml +1 -1
  12. package/dist/capabilities/executable.yaml +1 -1
  13. package/dist/capabilities/handler.yaml +1 -1
  14. package/dist/capabilities/listener.yaml +1 -1
  15. package/dist/capabilities/provider.yaml +1 -1
  16. package/dist/capabilities/template.yaml +1 -1
  17. package/dist/capabilities/type.yaml +1 -1
  18. package/dist/controller-loader.d.ts +1 -1
  19. package/dist/controller-loader.d.ts.map +1 -1
  20. package/dist/controller-loader.js +4 -2
  21. package/dist/controller-loader.js.map +1 -1
  22. package/dist/controller-registry.d.ts +1 -2
  23. package/dist/controller-registry.d.ts.map +1 -1
  24. package/dist/controller-registry.js.map +1 -1
  25. package/dist/controllers/module/import-controller.d.ts +38 -0
  26. package/dist/controllers/module/import-controller.d.ts.map +1 -0
  27. package/dist/controllers/module/import-controller.js +119 -0
  28. package/dist/controllers/module/import-controller.js.map +1 -0
  29. package/dist/controllers/module/module-controller.d.ts +57 -11
  30. package/dist/controllers/module/module-controller.d.ts.map +1 -1
  31. package/dist/controllers/module/module-controller.js +46 -82
  32. package/dist/controllers/module/module-controller.js.map +1 -1
  33. package/dist/controllers/resource-definition/resource-definition-controller.d.ts.map +1 -1
  34. package/dist/controllers/resource-definition/resource-definition-controller.js +12 -4
  35. package/dist/controllers/resource-definition/resource-definition-controller.js.map +1 -1
  36. package/dist/evaluation-context.d.ts +91 -0
  37. package/dist/evaluation-context.d.ts.map +1 -0
  38. package/dist/evaluation-context.js +220 -0
  39. package/dist/evaluation-context.js.map +1 -0
  40. package/dist/execution-context.d.ts +13 -0
  41. package/dist/execution-context.d.ts.map +1 -0
  42. package/dist/execution-context.js +14 -0
  43. package/dist/execution-context.js.map +1 -0
  44. package/dist/index.d.ts +0 -3
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +0 -2
  47. package/dist/index.js.map +1 -1
  48. package/dist/kernel.d.ts +23 -31
  49. package/dist/kernel.d.ts.map +1 -1
  50. package/dist/kernel.js +212 -333
  51. package/dist/kernel.js.map +1 -1
  52. package/dist/loader.d.ts +2 -2
  53. package/dist/loader.d.ts.map +1 -1
  54. package/dist/loader.js +29 -9
  55. package/dist/loader.js.map +1 -1
  56. package/dist/manifest-adapters/local-file-adapter.d.ts.map +1 -1
  57. package/dist/manifest-adapters/local-file-adapter.js +21 -12
  58. package/dist/manifest-adapters/local-file-adapter.js.map +1 -1
  59. package/dist/manifest-schemas.d.ts +1 -25
  60. package/dist/manifest-schemas.d.ts.map +1 -1
  61. package/dist/manifest-schemas.js +3 -22
  62. package/dist/manifest-schemas.js.map +1 -1
  63. package/dist/module-context-registry.d.ts +48 -0
  64. package/dist/module-context-registry.d.ts.map +1 -0
  65. package/dist/module-context-registry.js +91 -0
  66. package/dist/module-context-registry.js.map +1 -0
  67. package/dist/module-context.d.ts +31 -0
  68. package/dist/module-context.d.ts.map +1 -0
  69. package/dist/module-context.js +67 -0
  70. package/dist/module-context.js.map +1 -0
  71. package/dist/registry.d.ts +1 -2
  72. package/dist/registry.d.ts.map +1 -1
  73. package/dist/registry.js +3 -3
  74. package/dist/registry.js.map +1 -1
  75. package/dist/resource-context.d.ts +25 -5
  76. package/dist/resource-context.d.ts.map +1 -1
  77. package/dist/resource-context.js +74 -28
  78. package/dist/resource-context.js.map +1 -1
  79. package/dist/schema-valiator.d.ts.map +1 -1
  80. package/dist/schema-valiator.js +3 -1
  81. package/dist/schema-valiator.js.map +1 -1
  82. package/dist/snapshot-serializer.d.ts +1 -2
  83. package/dist/snapshot-serializer.d.ts.map +1 -1
  84. package/dist/snapshot-serializer.js.map +1 -1
  85. package/dist/types.d.ts +3 -0
  86. package/dist/types.d.ts.map +1 -1
  87. package/dist/types.js.map +1 -1
  88. package/package.json +9 -6
  89. package/src/boot-context-registry.ts +169 -0
  90. package/src/capabilities/component.yaml +4 -0
  91. package/src/capabilities/executable.yaml +8 -0
  92. package/src/capabilities/handler.yaml +4 -0
  93. package/src/capabilities/listener.yaml +4 -0
  94. package/src/capabilities/provider.yaml +4 -0
  95. package/src/capabilities/template.yaml +4 -0
  96. package/src/capabilities/type.yaml +4 -0
  97. package/src/controller-loader.ts +298 -0
  98. package/src/controller-registry.ts +206 -0
  99. package/src/controllers/capability/capability-controller.ts +41 -0
  100. package/src/controllers/module/import-controller.ts +143 -0
  101. package/src/controllers/module/module-controller.ts +67 -0
  102. package/src/controllers/module/module.json +48 -0
  103. package/src/controllers/resource-definition/resource-definition-controller.ts +87 -0
  104. package/src/controllers/resource-definition/resource-definition.json +18 -0
  105. package/src/event-stream.ts +121 -0
  106. package/src/events.ts +99 -0
  107. package/src/index.ts +7 -0
  108. package/src/kernel.ts +558 -0
  109. package/src/loader.ts +245 -0
  110. package/src/manifest-adapters/http-adapter.ts +35 -0
  111. package/src/manifest-adapters/local-file-adapter.ts +69 -0
  112. package/src/manifest-adapters/manifest-adapter.ts +33 -0
  113. package/src/manifest-adapters/registry-adapter.ts +56 -0
  114. package/src/manifest-schemas.ts +49 -0
  115. package/src/registry.ts +137 -0
  116. package/src/resource-context.ts +266 -0
  117. package/src/resource-uri.ts +200 -0
  118. package/src/schema-valiator.ts +57 -0
  119. package/dist/cli.d.ts +0 -3
  120. package/dist/cli.d.ts.map +0 -1
  121. package/dist/cli.js +0 -109
  122. package/dist/cli.js.map +0 -1
  123. package/dist/expressions.d.ts +0 -20
  124. package/dist/expressions.d.ts.map +0 -1
  125. package/dist/expressions.js +0 -253
  126. package/dist/expressions.js.map +0 -1
  127. package/dist/template-definition.d.ts +0 -38
  128. package/dist/template-definition.d.ts.map +0 -1
  129. package/dist/template-definition.js +0 -26
  130. package/dist/template-definition.js.map +0 -1
  131. package/dist/template-expander.d.ts +0 -19
  132. package/dist/template-expander.d.ts.map +0 -1
  133. package/dist/template-expander.js +0 -425
  134. package/dist/template-expander.js.map +0 -1
  135. /package/{dist/src → src}/controllers/module/module.yaml +0 -0
@@ -0,0 +1,206 @@
1
+ import { ControllerInstance, ResourceDefinition, RuntimeResource } from "@telorun/sdk";
2
+ import * as path from "path";
3
+
4
+ /**
5
+ * ControllerRegistry: Manages controller loading and dispatch
6
+ * Maps fully-qualified resource kinds to their controller implementations
7
+ */
8
+ export class ControllerRegistry {
9
+ private controllersByKind: Map<string, ControllerInstance> = new Map();
10
+ private definitionsByKind: Map<string, ResourceDefinition> = new Map();
11
+ private controllerLoaders: Map<string, () => Promise<ControllerInstance>> = new Map();
12
+ private capabilitySchemas: Map<string, Record<string, any> | null> = new Map();
13
+
14
+ registerCapability(name: string, schema?: Record<string, any>): void {
15
+ this.capabilitySchemas.set(name, schema ?? null);
16
+ }
17
+
18
+ isCapabilityRegistered(name: string): boolean {
19
+ return this.capabilitySchemas.has(name);
20
+ }
21
+
22
+ getCapabilitySchema(name: string): Record<string, any> | null | undefined {
23
+ return this.capabilitySchemas.get(name);
24
+ }
25
+
26
+ /**
27
+ * Register a controller definition
28
+ */
29
+ registerDefinition(
30
+ definition: ResourceDefinition,
31
+ // baseDir?: string,
32
+ // namespace?: string | null,
33
+ ): void {
34
+ // Construct fully qualified kind: Namespace.Name
35
+ // Only add namespace if name is not already qualified (doesn't contain a dot)
36
+ const namespace = definition.metadata.module;
37
+ const baseDir = null;
38
+ const name = definition.metadata.name;
39
+ const kind = namespace && !name.includes(".") ? `${namespace}.${name}` : name;
40
+
41
+ this.definitionsByKind.set(kind, definition);
42
+
43
+ // If definition has controllers, register loader for them
44
+ if (definition.controllers && definition.controllers.length > 0 && baseDir) {
45
+ this.registerControllerLoader(kind, definition, baseDir);
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Get a controller instance for a kind
51
+ * Lazy-loads controller code on first access
52
+ * Throws if controller not found
53
+ */
54
+ getController(kind: string): ControllerInstance {
55
+ // Return cached instance if available
56
+ if (this.controllersByKind.has(kind)) {
57
+ return this.controllersByKind.get(kind)!;
58
+ }
59
+
60
+ // Load controller if loader is registered
61
+ // const loader = this.controllerLoaders.get(kind);
62
+ // if (loader) {
63
+ // const controller = await loader();
64
+ // this.controllersByKind.set(kind, controller);
65
+ // return controller;
66
+ // }
67
+
68
+ throw new Error(`No controller registered for kind: ${kind}`);
69
+ }
70
+
71
+ /**
72
+ * Safe get - returns undefined if controller not found
73
+ */
74
+ getControllerOrUndefined(kind: string): ControllerInstance | undefined {
75
+ // Return cached instance if available
76
+ if (this.controllersByKind.has(kind)) {
77
+ return this.controllersByKind.get(kind);
78
+ }
79
+ return undefined;
80
+ }
81
+
82
+ /**
83
+ * Check if a controller exists for this kind (definition or directly registered)
84
+ */
85
+ hasController(kind: string): boolean {
86
+ return this.controllersByKind.has(kind) || this.definitionsByKind.has(kind);
87
+ }
88
+
89
+ /**
90
+ * Get definition for a kind
91
+ */
92
+ getDefinition(kind: string): ResourceDefinition | undefined {
93
+ return this.definitionsByKind.get(kind);
94
+ }
95
+
96
+ /**
97
+ * Get all registered kinds
98
+ */
99
+ getKinds(): string[] {
100
+ return Array.from(this.definitionsByKind.keys());
101
+ }
102
+
103
+ getControllerKinds(): string[] {
104
+ return Array.from(this.controllersByKind.keys());
105
+ }
106
+
107
+ /**
108
+ * Create a resource instance using its controller
109
+ */
110
+ async create(kind: string, resource: RuntimeResource, ctx: any): Promise<any | null> {
111
+ const controller = this.getController(kind);
112
+ if (!controller || !controller.create) {
113
+ return null;
114
+ }
115
+ return controller.create(resource, ctx);
116
+ }
117
+
118
+ /**
119
+ * Register a controller for a kind
120
+ */
121
+ registerController(kind: string, controller: ControllerInstance): void {
122
+ if (!this.definitionsByKind.has(kind)) {
123
+ throw new Error(`Cannot register controller for kind ${kind} without definition`);
124
+ }
125
+ // Ensure controller has schema from definition
126
+ const definition = this.definitionsByKind.get(kind);
127
+ if (definition?.schema && !controller.schema) {
128
+ // Wrap controller to add schema without mutating the original
129
+ const wrappedController: ControllerInstance = {
130
+ ...controller,
131
+ schema: definition.schema,
132
+ };
133
+ this.controllersByKind.set(kind, wrappedController);
134
+ } else {
135
+ this.controllersByKind.set(kind, controller);
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Private: Register controller loader
141
+ */
142
+ private registerControllerLoader(
143
+ kind: string,
144
+ definition: ResourceDefinition,
145
+ moduleDir: string,
146
+ ): void {
147
+ const controllerDef = definition.controllers?.[0]; // Use first matching controller for now
148
+ if (!controllerDef) return;
149
+
150
+ this.controllerLoaders.set(kind, async () => {
151
+ const modulePath = path.resolve(moduleDir, controllerDef.entry);
152
+ const moduleRuntime = await import(modulePath);
153
+ const exported = moduleRuntime.default || moduleRuntime.Module || moduleRuntime;
154
+
155
+ const registerFn =
156
+ typeof moduleRuntime.register === "function"
157
+ ? moduleRuntime.register
158
+ : typeof exported === "function" && !this.isModuleClass(exported)
159
+ ? exported
160
+ : null;
161
+
162
+ const createFn =
163
+ typeof moduleRuntime.create === "function"
164
+ ? moduleRuntime.create
165
+ : typeof exported?.create === "function"
166
+ ? exported.create
167
+ : null;
168
+
169
+ const executeFn =
170
+ typeof moduleRuntime.execute === "function"
171
+ ? moduleRuntime.execute
172
+ : typeof exported?.execute === "function"
173
+ ? exported.execute
174
+ : null;
175
+
176
+ const compileFn =
177
+ typeof moduleRuntime.compile === "function"
178
+ ? moduleRuntime.compile
179
+ : typeof exported?.compile === "function"
180
+ ? exported.compile
181
+ : null;
182
+
183
+ if (!registerFn && !executeFn && !createFn && !compileFn) {
184
+ throw new Error(`Controller for "${kind}" exports no usable handlers`);
185
+ }
186
+
187
+ if (!definition.schema) {
188
+ throw new Error(`Definition for "${kind}" does not have schema`);
189
+ }
190
+
191
+ return {
192
+ register: registerFn ?? undefined,
193
+ create: createFn ?? undefined,
194
+ execute: executeFn ?? undefined,
195
+ compile: compileFn ?? undefined,
196
+ schema: definition.schema,
197
+ };
198
+ });
199
+ }
200
+
201
+ private isModuleClass(obj: any): boolean {
202
+ return (
203
+ typeof obj === "function" && (obj.name === "Controller" || obj.toString().includes("class"))
204
+ );
205
+ }
206
+ }
@@ -0,0 +1,41 @@
1
+ import type { ResourceContext, ResourceInstance } from "@telorun/sdk";
2
+
3
+ type CapabilityResource = {
4
+ kind: string;
5
+ metadata: {
6
+ name: string;
7
+ [key: string]: any;
8
+ };
9
+ schema?: Record<string, any>;
10
+ };
11
+
12
+ class Capability implements ResourceInstance {
13
+ constructor(private readonly resource: CapabilityResource) {}
14
+
15
+ async init(ctx: ResourceContext) {
16
+ ctx.registerCapability(this.resource.metadata.name, this.resource.schema);
17
+ }
18
+ }
19
+
20
+ export async function create(resource: any, _ctx: ResourceContext): Promise<Capability> {
21
+ return new Capability(resource as CapabilityResource);
22
+ }
23
+
24
+ export const schema = {
25
+ type: "object",
26
+ properties: {
27
+ metadata: {
28
+ type: "object",
29
+ properties: {
30
+ name: { type: "string" },
31
+ },
32
+ required: ["name"],
33
+ },
34
+ schema: {
35
+ type: "object",
36
+ additionalProperties: true,
37
+ },
38
+ },
39
+ required: ["metadata"],
40
+ additionalProperties: true,
41
+ };
@@ -0,0 +1,143 @@
1
+ import type { ResourceContext, ResourceInstance } from "@telorun/sdk";
2
+ import { EvaluationContext } from "@telorun/sdk";
3
+ import { Loader } from "../../loader.js";
4
+
5
+ export async function create(resource: any, ctx: ResourceContext): Promise<ResourceInstance> {
6
+ const alias = resource.metadata.name as string;
7
+ const declaringModule: string = resource.metadata.module ?? "default";
8
+ const loader = new Loader();
9
+
10
+ // Load target module manifests. Inject variables/secrets as compile context so that
11
+ // ${{ variables.x }} / ${{ secrets.y }} templates in the child module resolve correctly.
12
+ // No env — child modules are isolated from host environment.
13
+ const manifests = await loader.loadManifest(resource.source as string, ctx.moduleContext.source, {
14
+ // Potentially not needed
15
+ variables: (resource.variables as Record<string, unknown>) ?? {},
16
+ secrets: (resource.secrets as Record<string, unknown>) ?? {},
17
+ });
18
+ // Find the kind: Module manifest to learn the target module name and contract.
19
+ const moduleManifest = manifests.find((m: any) => m.kind === "Kernel.Module");
20
+ if (!moduleManifest) {
21
+ throw new Error(`No kind: Module manifest found in source "${resource.source as string}"`);
22
+ }
23
+ const targetModule: string = moduleManifest.metadata.name;
24
+
25
+ // Validate that every non-module manifest's metadata.module (when explicitly set)
26
+ // matches the target module name. A mismatch means the author put the wrong module
27
+ // name in a resource's metadata, which would silently give it an empty context and
28
+ // produce a confusing CEL "Identifier not found" error at runtime.
29
+ for (const manifest of manifests) {
30
+ if (manifest.kind === "Kernel.Module") continue;
31
+ const declaredModule: string | undefined = manifest.metadata?.module;
32
+ if (declaredModule && declaredModule !== targetModule) {
33
+ throw new Error(
34
+ `Resource '${manifest.metadata?.name ?? "(unnamed)"}' (kind: ${manifest.kind}) inside module '${targetModule}' ` +
35
+ `has metadata.module: '${declaredModule}', but the module is named '${targetModule}'. ` +
36
+ `Update metadata.module to '${targetModule}'.`,
37
+ );
38
+ }
39
+ }
40
+
41
+ // Validate required inputs before injecting.
42
+ validateRequiredInputs(moduleManifest.variables ?? {}, resource.variables ?? {}, "variables");
43
+ validateRequiredInputs(moduleManifest.secrets ?? {}, resource.secrets ?? {}, "secrets");
44
+
45
+ // Create child context with the imported variables/secrets baked in, so that
46
+ // ${{ variables.x }} / ${{ secrets.y }} templates resolve correctly at runtime.
47
+ const child = ctx.moduleContext.spawnChild(
48
+ new EvaluationContext(
49
+ ctx.moduleContext.source,
50
+ {
51
+ variables: (resource.variables as Record<string, unknown>) ?? {},
52
+ secrets: (resource.secrets as Record<string, unknown>) ?? {},
53
+ resources: {},
54
+ },
55
+ ctx.moduleContext.createInstance,
56
+ ctx.moduleContext.secretValues,
57
+ ctx.moduleContext.emit,
58
+ ),
59
+ );
60
+
61
+ for (const manifest of manifests) {
62
+ child.registerManifest(manifest);
63
+ }
64
+
65
+ // Link the target module context as a child of the declaring module context in
66
+ // the lifecycle tree. This enables cascading teardown (parent → child order)
67
+ // and makes the import hierarchy visible at runtime.
68
+ // const declaringCtx: ModuleContext = ctx.getModuleContext(declaringModule);
69
+ // const targetCtx: ModuleContext = (ctx as any).getModuleContext(targetModule);
70
+ // if (!targetCtx.parent) {
71
+ // declaringCtx.spawnChild(targetCtx);
72
+ // }
73
+
74
+ // Try to evaluate the target module's exports.
75
+ // Throws if resources.X is not yet populated — the kernel retry loop catches this and retries.
76
+ // const evaluatedExports: any = child.expand(moduleManifest.exports ?? {});
77
+
78
+ const exportedKinds: string[] = moduleManifest.exports?.kinds ?? [];
79
+ ctx.registerModuleImport(alias, targetModule, exportedKinds);
80
+ // Return a ResourceInstance whose snapshot() surfaces the exported values.
81
+ // The kernel's generic setResource() call stores them under resources.<alias>
82
+ // in the declaring module's evaluation context — no separate imports namespace needed.
83
+ return {
84
+ snapshot: () => ({
85
+ variables: (resource.variables as Record<string, unknown>) ?? {},
86
+ secrets: (resource.secrets as Record<string, unknown>) ?? {},
87
+ }),
88
+ run: async () => {
89
+ // Proxy run to target module
90
+ for (const target of (moduleManifest.targets as string[]) ?? []) {
91
+ await child.run(target);
92
+ }
93
+ },
94
+ invoke: async () => {
95
+ // Proxy run to target module
96
+ // for (const target of (moduleManifest.targets as string[]) ?? []) {
97
+ // child.invoke(target);
98
+ // }
99
+ console.log("invoking");
100
+ },
101
+ init: async () => {
102
+ await child.initializeResources();
103
+ },
104
+ teardown: async () => {
105
+ await child.teardownResources();
106
+ },
107
+ };
108
+ }
109
+
110
+ function validateRequiredInputs(
111
+ schemaDefs: Record<string, any>,
112
+ provided: Record<string, unknown>,
113
+ kind: "variables" | "secrets",
114
+ ): void {
115
+ for (const [key, def] of Object.entries(schemaDefs)) {
116
+ const isRequired = typeof def === "object" && def !== null && !("default" in def);
117
+ if (isRequired && !(key in provided)) {
118
+ throw new Error(`Required ${kind} input "${key}" not provided for module import`);
119
+ }
120
+ }
121
+ }
122
+
123
+ export const schema = {
124
+ type: "object",
125
+ properties: {
126
+ kind: { type: "string" },
127
+ metadata: {
128
+ type: "object",
129
+ properties: {
130
+ name: { type: "string" },
131
+ source: { type: "string" },
132
+ module: { type: "string" },
133
+ },
134
+ required: ["name"],
135
+ additionalProperties: true,
136
+ },
137
+ source: { type: "string" },
138
+ variables: { type: "object" },
139
+ secrets: { type: "object" },
140
+ },
141
+ required: ["metadata", "source"],
142
+ additionalProperties: false,
143
+ };
@@ -0,0 +1,67 @@
1
+ import type { ResourceContext, ResourceInstance } from "@telorun/sdk";
2
+ import { Loader } from "../../loader.js";
3
+
4
+ export async function create(resource: any, ctx: ResourceContext): Promise<ResourceInstance> {
5
+ const moduleName = resource.metadata.name as string;
6
+ const loader = new Loader();
7
+
8
+ for (const includePath of (resource.include as string[]) ?? []) {
9
+ const manifests = await loader.loadManifest(
10
+ includePath,
11
+ resource.metadata.source as string,
12
+ {},
13
+ );
14
+ for (const manifest of manifests) {
15
+ if (manifest.kind === "Kernel.Module") {
16
+ throw new Error(`Included file "${includePath}" must not declare kind: Module`);
17
+ }
18
+ if (!manifest.metadata.module) {
19
+ manifest.metadata.module = moduleName;
20
+ }
21
+ ctx.registerManifest(manifest);
22
+ }
23
+ }
24
+
25
+ return {
26
+ run: async () => {
27
+ for (const target of (resource.targets as string[]) ?? []) {
28
+ const [kind, name] = target.split(".");
29
+ if (!kind || !name) {
30
+ throw new Error(`Invalid target format: "${target}". Expected "Kind.Name"`);
31
+ }
32
+ await ctx.invoke(kind, name, {});
33
+ }
34
+ },
35
+ };
36
+ }
37
+
38
+ export const schema = {
39
+ type: "object",
40
+ properties: {
41
+ kind: { type: "string" },
42
+ metadata: {
43
+ type: "object",
44
+ properties: {
45
+ name: { type: "string" },
46
+ version: { type: "string" },
47
+ source: { type: "string" },
48
+ module: { type: "string" },
49
+ },
50
+ required: ["name"],
51
+ additionalProperties: true,
52
+ },
53
+ include: { type: "array", items: { type: "string" } },
54
+ variables: { type: "object" },
55
+ secrets: { type: "object" },
56
+ targets: { type: "array", items: { type: "string" } },
57
+ exports: {
58
+ type: "object",
59
+ properties: {
60
+ kinds: { type: "array", items: { type: "string" } },
61
+ },
62
+ additionalProperties: true,
63
+ },
64
+ },
65
+ required: ["metadata"],
66
+ additionalProperties: false,
67
+ };
@@ -0,0 +1,48 @@
1
+ {
2
+ "type": "object",
3
+ "properties": {
4
+ "metadata": {
5
+ "type": "object",
6
+ "properties": {
7
+ "name": {
8
+ "type": "string"
9
+ }
10
+ },
11
+ "required": ["name"]
12
+ },
13
+ "imports": {
14
+ "type": "array",
15
+ "items": {
16
+ "type": "string"
17
+ },
18
+ "description": "Paths to other modules to import"
19
+ },
20
+ "definitions": {
21
+ "type": "array",
22
+ "items": {
23
+ "type": "string"
24
+ },
25
+ "description": "Paths to ResourceDefinition files"
26
+ },
27
+ "resources": {
28
+ "type": "array",
29
+ "items": {
30
+ "oneOf": [
31
+ {
32
+ "type": "string"
33
+ },
34
+ {
35
+ "type": "object",
36
+ "properties": {
37
+ "path": {
38
+ "type": "string"
39
+ }
40
+ }
41
+ }
42
+ ]
43
+ },
44
+ "description": "Paths to resource instance files to load"
45
+ }
46
+ },
47
+ "required": ["metadata"]
48
+ }
@@ -0,0 +1,87 @@
1
+ import type {
2
+ ControllerContext,
3
+ ResourceContext,
4
+ ResourceInstance,
5
+ RuntimeResource,
6
+ } from "@telorun/sdk";
7
+ import { ControllerLoader } from "../../controller-loader.js";
8
+ import { formatAjvErrors, validateResourceDefinition } from "../../manifest-schemas.js";
9
+
10
+ type ResourceDefinitionResource = RuntimeResource & {
11
+ kind: "Definition";
12
+ metadata: {
13
+ [key: string]: any;
14
+ name: string;
15
+ module?: string;
16
+ };
17
+ schema: Record<string, any>;
18
+ capabilities: string[];
19
+ events?: string[];
20
+ controllers: Array<string>;
21
+ };
22
+
23
+ /**
24
+ * ResourceDefinition resource - acts as metadata holder for resource type definitions
25
+ * Validates incoming definitions against schema and maintains definition metadata
26
+ */
27
+ class ResourceDefinition implements ResourceInstance {
28
+ readonly kind: "ResourceDefinition" = "ResourceDefinition";
29
+
30
+ constructor(
31
+ readonly resource: ResourceDefinitionResource,
32
+ private controllerLoader: ControllerLoader,
33
+ ) {}
34
+
35
+ async init(ctx: ResourceContext) {
36
+ for (const cap of this.resource.capabilities) {
37
+ if (!ctx.isCapabilityRegistered(cap)) {
38
+ throw new Error(
39
+ `Capability "${cap}" is not registered. Declare it as a Kernel.Capability resource.`,
40
+ );
41
+ }
42
+ const capSchema = ctx.getCapabilitySchema(cap);
43
+ if (capSchema) {
44
+ ctx.validateSchema(this.resource, capSchema);
45
+ }
46
+ }
47
+ ctx.emit("ControllerLoading", { controllers: this.resource.controllers });
48
+ try {
49
+ const controllerInstance = await this.controllerLoader.load(
50
+ this.resource.controllers,
51
+ this.resource.metadata.source,
52
+ );
53
+ ctx.emit("ControllerLoaded", { schema: controllerInstance.schema });
54
+ ctx.registerDefinition(this.resource);
55
+ await ctx.registerController(
56
+ this.resource.metadata.module,
57
+ this.resource.metadata.name,
58
+ controllerInstance,
59
+ );
60
+ } catch (err) {
61
+ ctx.emit("ControllerLoadFailed", { error: (err as Error).message });
62
+ throw err;
63
+ }
64
+ }
65
+ }
66
+
67
+ export function register(ctx: ControllerContext): void {
68
+ // ResourceDefinition is a passive resource - no registration needed
69
+ }
70
+
71
+ export async function create(resource: any, ctx: ResourceContext): Promise<ResourceDefinition> {
72
+ // Validate incoming resource definition against schema
73
+ if (!validateResourceDefinition(resource)) {
74
+ throw new Error(
75
+ `Invalid ResourceDefinition "${resource.metadata.name}": ${formatAjvErrors(validateResourceDefinition.errors)}`,
76
+ );
77
+ }
78
+
79
+ // Return a fully-formed ResourceDefinition instance
80
+ const definition = resource as ResourceDefinitionResource;
81
+ return new ResourceDefinition(definition, new ControllerLoader());
82
+ }
83
+
84
+ export const schema = {
85
+ type: "object",
86
+ additionalProperties: true,
87
+ };
@@ -0,0 +1,18 @@
1
+ {
2
+ "type": "object",
3
+ "properties": {
4
+ "metadata": {
5
+ "type": "object",
6
+ "properties": {
7
+ "name": {
8
+ "type": "string"
9
+ }
10
+ },
11
+ "required": ["name"]
12
+ },
13
+ "schema": {
14
+ "type": "object"
15
+ }
16
+ },
17
+ "required": ["metadata", "schema"]
18
+ }