@telorun/kernel 0.30.2 → 0.32.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/dist/dependency-injection.d.ts.map +1 -1
- package/dist/dependency-injection.js +7 -1
- package/dist/dependency-injection.js.map +1 -1
- package/dist/evaluation-context.d.ts +53 -1
- package/dist/evaluation-context.d.ts.map +1 -1
- package/dist/evaluation-context.js +203 -30
- package/dist/evaluation-context.js.map +1 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +6 -0
- package/dist/events.js.map +1 -1
- package/dist/kernel.d.ts +9 -0
- package/dist/kernel.d.ts.map +1 -1
- package/dist/kernel.js +15 -2
- package/dist/kernel.js.map +1 -1
- package/dist/module-context.d.ts +4 -1
- package/dist/module-context.d.ts.map +1 -1
- package/dist/module-context.js +77 -15
- package/dist/module-context.js.map +1 -1
- package/dist/resource-context.d.ts +2 -1
- package/dist/resource-context.d.ts.map +1 -1
- package/dist/resource-context.js +4 -1
- package/dist/resource-context.js.map +1 -1
- package/dist/tracing.d.ts +19 -0
- package/dist/tracing.d.ts.map +1 -0
- package/dist/tracing.js +39 -0
- package/dist/tracing.js.map +1 -0
- package/package.json +2 -2
- package/src/dependency-injection.ts +7 -1
- package/src/evaluation-context.ts +306 -35
- package/src/events.ts +5 -0
- package/src/kernel.ts +19 -2
- package/src/module-context.ts +100 -17
- package/src/resource-context.ts +7 -1
- package/src/tracing.ts +27 -0
package/src/module-context.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { executeInvokeStep, RuntimeError } from "@telorun/sdk";
|
|
1
|
+
import { executeInvokeStep, getRefIdentity, RuntimeError } from "@telorun/sdk";
|
|
2
2
|
import type {
|
|
3
3
|
BootTarget,
|
|
4
4
|
ControllerPolicy,
|
|
@@ -432,6 +432,29 @@ export class ModuleContext extends EvaluationContext implements IModuleContext {
|
|
|
432
432
|
return `${realModule}.${suffix}`;
|
|
433
433
|
}
|
|
434
434
|
|
|
435
|
+
protected override resolveKindSafe(kind: string): string {
|
|
436
|
+
// `resolveKind` throws for unqualified / ungated kinds — an expected signal,
|
|
437
|
+
// not a failure: a capability probe that can't resolve falls back to the raw
|
|
438
|
+
// kind (lookup misses → treated as non-service → keeps its ALS scope).
|
|
439
|
+
try {
|
|
440
|
+
return this.resolveKind(kind);
|
|
441
|
+
} catch {
|
|
442
|
+
return kind;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
protected override traceRootScope(): Record<string, unknown> {
|
|
447
|
+
// Mask secret values (keep keys so availability is visible, never the value).
|
|
448
|
+
const secrets: Record<string, unknown> = {};
|
|
449
|
+
for (const key of Object.keys(this._secrets)) secrets[key] = "[secret]";
|
|
450
|
+
return {
|
|
451
|
+
variables: this._variables,
|
|
452
|
+
secrets,
|
|
453
|
+
resources: this._resources,
|
|
454
|
+
ports: this._ports,
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
|
|
435
458
|
private _rebuildContext(): void {
|
|
436
459
|
this._context = {
|
|
437
460
|
variables: this._variables,
|
|
@@ -473,26 +496,84 @@ export class ModuleContext extends EvaluationContext implements IModuleContext {
|
|
|
473
496
|
await super.run(name, ctx);
|
|
474
497
|
}
|
|
475
498
|
|
|
476
|
-
async runTargets(ctx?: InvokeContext) {
|
|
499
|
+
async runTargets(ctx?: InvokeContext, appName?: string) {
|
|
500
|
+
// Open a trace span for the application's boot run so the app itself is a
|
|
501
|
+
// trace participant: every target dispatches under `targetCtx`, parenting to
|
|
502
|
+
// this span instead of becoming a detached root. The span is gated on tracing
|
|
503
|
+
// (zero cost otherwise) and rides the boot cancellation token unchanged.
|
|
504
|
+
const tracing = this.tracer?.enabled === true;
|
|
505
|
+
const appSpanId = tracing ? this.tracer!.next() : undefined;
|
|
506
|
+
// The boot run roots a fresh trace; targets inherit it via `targetCtx`.
|
|
507
|
+
const appTraceId = tracing ? this.tracer!.newTraceId() : undefined;
|
|
508
|
+
const appRootScope = tracing ? this.traceRootScope() : undefined;
|
|
509
|
+
const appLabel = appName ?? "application";
|
|
510
|
+
const appSpan = (
|
|
511
|
+
phase: "start" | "end",
|
|
512
|
+
outcome: "ok" | "failed" | "cancelled" | undefined,
|
|
513
|
+
) =>
|
|
514
|
+
this.tracePayload(
|
|
515
|
+
"Telo.Application",
|
|
516
|
+
appLabel,
|
|
517
|
+
appSpanId,
|
|
518
|
+
undefined,
|
|
519
|
+
appTraceId,
|
|
520
|
+
"run",
|
|
521
|
+
phase,
|
|
522
|
+
outcome,
|
|
523
|
+
phase === "end" && appRootScope ? { context: appRootScope } : {},
|
|
524
|
+
);
|
|
525
|
+
const targetCtx: InvokeContext | undefined =
|
|
526
|
+
tracing && ctx
|
|
527
|
+
? {
|
|
528
|
+
cancellation: ctx.cancellation,
|
|
529
|
+
invocationId: appSpanId,
|
|
530
|
+
parentInvocationId: undefined,
|
|
531
|
+
traceId: appTraceId,
|
|
532
|
+
}
|
|
533
|
+
: ctx;
|
|
534
|
+
|
|
477
535
|
const steps: Record<string, unknown> = {};
|
|
478
536
|
const stepCtx: InvokeStepContext = {
|
|
479
537
|
expandValue: (value, context) => this.expandWith(value, context),
|
|
480
|
-
invoke: (kind, name, inputs) => this.invoke(kind, name, inputs,
|
|
538
|
+
invoke: (kind, name, inputs) => this.invoke(kind, name, inputs, targetCtx),
|
|
481
539
|
invokeResolved: (kind, name, instance, inputs) =>
|
|
482
|
-
this.invokeResolved(kind, name, instance, inputs,
|
|
540
|
+
this.invokeResolved(kind, name, instance, inputs, targetCtx),
|
|
483
541
|
resolveImportedInstance: (alias, name) => this.resolveImportedInstance(alias, name),
|
|
484
542
|
};
|
|
485
|
-
//
|
|
486
|
-
//
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
543
|
+
// Route a pre-resolved boot target through the instrumented chokepoint
|
|
544
|
+
// (`runResolved`) instead of calling `instance.run()` directly, so it emits a
|
|
545
|
+
// run span nested under the app. Kind/name come from the `!ref` identity the
|
|
546
|
+
// kernel stamped at injection, falling back to a positional label.
|
|
547
|
+
const runResolvedInstance = async (inst: ResourceInstance, kind: string, name: string) =>
|
|
548
|
+
this.runResolved(kind, name, inst, targetCtx);
|
|
549
|
+
|
|
550
|
+
if (tracing) await this.emit(`${appLabel}.Running`, appSpan("start", undefined));
|
|
551
|
+
try {
|
|
552
|
+
await this.dispatchTargets(stepCtx, steps, runResolvedInstance, targetCtx);
|
|
553
|
+
if (tracing) await this.emit(`${appLabel}.Run`, appSpan("end", "ok"));
|
|
554
|
+
} catch (err) {
|
|
555
|
+
if (tracing) {
|
|
556
|
+
const cancelled = (err as { code?: unknown })?.code === "ERR_INVOKE_CANCELLED";
|
|
557
|
+
await this.emit(
|
|
558
|
+
`${appLabel}.${cancelled ? "RunCancelled" : "RunFailed"}`,
|
|
559
|
+
appSpan("end", cancelled ? "cancelled" : "failed"),
|
|
493
560
|
);
|
|
494
561
|
}
|
|
495
|
-
|
|
562
|
+
throw err;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
private async dispatchTargets(
|
|
567
|
+
stepCtx: InvokeStepContext,
|
|
568
|
+
steps: Record<string, unknown>,
|
|
569
|
+
runResolvedInstance: (inst: ResourceInstance, kind: string, name: string) => Promise<void>,
|
|
570
|
+
ctx?: InvokeContext,
|
|
571
|
+
) {
|
|
572
|
+
// Recover the kind+name the kernel stamped onto a `!ref`-injected instance,
|
|
573
|
+
// so the run span is properly labelled; fall back to a positional name.
|
|
574
|
+
const idOf = (inst: ResourceInstance, fallbackName: string) => {
|
|
575
|
+
const id = getRefIdentity(inst as object);
|
|
576
|
+
return { kind: id?.kind ?? "", name: id?.name ?? fallbackName };
|
|
496
577
|
};
|
|
497
578
|
for (let i = 0; i < this.targets.length; i++) {
|
|
498
579
|
const target = this.targets[i]!;
|
|
@@ -524,7 +605,8 @@ export class ModuleContext extends EvaluationContext implements IModuleContext {
|
|
|
524
605
|
// Phase 5 injection may have replaced the ref slot with the live
|
|
525
606
|
// instance; run it directly.
|
|
526
607
|
if (isRunnableInstance(ref)) {
|
|
527
|
-
|
|
608
|
+
const id = idOf(ref, `target[${i}]`);
|
|
609
|
+
await runResolvedInstance(ref, id.kind, id.name);
|
|
528
610
|
} else {
|
|
529
611
|
const r =
|
|
530
612
|
ref && typeof ref === "object"
|
|
@@ -537,7 +619,7 @@ export class ModuleContext extends EvaluationContext implements IModuleContext {
|
|
|
537
619
|
`Boot target '${r.alias}.${r.name}' is not a runnable exported instance`,
|
|
538
620
|
);
|
|
539
621
|
}
|
|
540
|
-
await runResolvedInstance(inst,
|
|
622
|
+
await runResolvedInstance(inst, getRefIdentity(inst as object)?.kind ?? "", r.name);
|
|
541
623
|
} else {
|
|
542
624
|
await this.run(typeof ref === "string" ? ref : r!.name, ctx);
|
|
543
625
|
}
|
|
@@ -550,7 +632,8 @@ export class ModuleContext extends EvaluationContext implements IModuleContext {
|
|
|
550
632
|
// so the common runtime shape here is a pre-resolved ResourceInstance;
|
|
551
633
|
// fall back to the structural ref forms when injection left them in place.
|
|
552
634
|
if (isRunnableInstance(target)) {
|
|
553
|
-
|
|
635
|
+
const id = idOf(target, `target[${i}]`);
|
|
636
|
+
await runResolvedInstance(target, id.kind, id.name);
|
|
554
637
|
continue;
|
|
555
638
|
}
|
|
556
639
|
const bare = target as { name?: unknown; alias?: unknown };
|
|
@@ -561,7 +644,7 @@ export class ModuleContext extends EvaluationContext implements IModuleContext {
|
|
|
561
644
|
`Boot target '${bare.alias}.${bare.name}' is not a runnable exported instance`,
|
|
562
645
|
);
|
|
563
646
|
}
|
|
564
|
-
await runResolvedInstance(inst,
|
|
647
|
+
await runResolvedInstance(inst, getRefIdentity(inst as object)?.kind ?? "", bare.name);
|
|
565
648
|
continue;
|
|
566
649
|
}
|
|
567
650
|
if (typeof bare.name === "string") {
|
package/src/resource-context.ts
CHANGED
|
@@ -12,6 +12,8 @@ import {
|
|
|
12
12
|
type InvokeContext,
|
|
13
13
|
type LoadOptions,
|
|
14
14
|
type ModuleContext,
|
|
15
|
+
type OpenSpan,
|
|
16
|
+
type OpenSpanOptions,
|
|
15
17
|
type ParsedArgs,
|
|
16
18
|
type TypeRule,
|
|
17
19
|
} from "@telorun/sdk";
|
|
@@ -155,6 +157,10 @@ export class ResourceContextImpl implements ResourceContext {
|
|
|
155
157
|
return createCancellationSource();
|
|
156
158
|
}
|
|
157
159
|
|
|
160
|
+
openSpan(base: InvokeContext | undefined, opts: OpenSpanOptions): Promise<OpenSpan> {
|
|
161
|
+
return this.moduleContext.openSpan(base, opts);
|
|
162
|
+
}
|
|
163
|
+
|
|
158
164
|
invoke<TInputs>(kind: string, name: string, inputs: TInputs): Promise<any> {
|
|
159
165
|
return this.moduleContext.invoke(kind, name, inputs);
|
|
160
166
|
}
|
|
@@ -223,7 +229,7 @@ export class ResourceContextImpl implements ResourceContext {
|
|
|
223
229
|
// analyzer's field-map walker descends `oneOf`/`anyOf` variant
|
|
224
230
|
// properties but intentionally early-returns on `$ref` (see
|
|
225
231
|
// `analyzer/nodejs/src/reference-field-map.ts`). Enabling the `$ref`
|
|
226
|
-
// descent regresses the kernel's `<
|
|
232
|
+
// descent regresses the kernel's `<name>.Invoked` event
|
|
227
233
|
// emission for kinds (notably `Run.Sequence`) whose controllers
|
|
228
234
|
// call `instance.invoke()` directly on Phase-5-injected instances;
|
|
229
235
|
// the walker fix needs to land together with routing those callers
|
package/src/tracing.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import type { Tracer } from "@telorun/sdk";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The kernel's invocation tracer: a monotonic counter plus the `enabled` gate a
|
|
6
|
+
* debug consumer flips on attach (`Kernel.setTracing`). One instance per kernel,
|
|
7
|
+
* shared by reference across the whole context tree, so invocation ids are unique
|
|
8
|
+
* within the run and `enabled` toggles everywhere at once.
|
|
9
|
+
*
|
|
10
|
+
* When `enabled` is `false` (the default), `invoke` skips id minting and the extra
|
|
11
|
+
* ALS scope entirely — tracing costs nothing until someone is watching.
|
|
12
|
+
*/
|
|
13
|
+
export class KernelTracer implements Tracer {
|
|
14
|
+
enabled = false;
|
|
15
|
+
#next = 0;
|
|
16
|
+
|
|
17
|
+
next(): number {
|
|
18
|
+
this.#next += 1;
|
|
19
|
+
return this.#next;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** A fresh OTel-compatible 16-byte hex trace id. Globally unique, so a trace
|
|
23
|
+
* stays identifiable once it crosses process boundaries. */
|
|
24
|
+
newTraceId(): string {
|
|
25
|
+
return randomBytes(16).toString("hex");
|
|
26
|
+
}
|
|
27
|
+
}
|