@nextera.one/axis-server-sdk 1.2.0 → 1.2.1
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 +131 -2
- package/dist/index.d.ts +131 -2
- package/dist/index.js +549 -113
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +537 -111
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -33,6 +33,7 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
33
33
|
if (kind && result) __defProp(target, key, result);
|
|
34
34
|
return result;
|
|
35
35
|
};
|
|
36
|
+
var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
|
|
36
37
|
|
|
37
38
|
// src/index.ts
|
|
38
39
|
var index_exports = {};
|
|
@@ -41,8 +42,13 @@ __export(index_exports, {
|
|
|
41
42
|
ATS1_SCHEMA: () => ATS1_SCHEMA,
|
|
42
43
|
AXIS_MAGIC: () => import_axis_protocol2.AXIS_MAGIC,
|
|
43
44
|
AXIS_OPCODES: () => AXIS_OPCODES,
|
|
45
|
+
AXIS_UPLOAD_FILE_STORE: () => AXIS_UPLOAD_FILE_STORE,
|
|
46
|
+
AXIS_UPLOAD_RECEIPT_SIGNER: () => AXIS_UPLOAD_RECEIPT_SIGNER,
|
|
47
|
+
AXIS_UPLOAD_SESSION_STORE: () => AXIS_UPLOAD_SESSION_STORE,
|
|
44
48
|
AXIS_VERSION: () => import_axis_protocol2.AXIS_VERSION,
|
|
45
49
|
Ats1Codec: () => ats1_exports,
|
|
50
|
+
AxisFilesDownloadHandler: () => AxisFilesDownloadHandler,
|
|
51
|
+
AxisFilesFinalizeHandler: () => AxisFilesFinalizeHandler,
|
|
46
52
|
AxisFrameZ: () => AxisFrameZ,
|
|
47
53
|
AxisIdDto: () => AxisIdDto,
|
|
48
54
|
AxisPacketTags: () => T,
|
|
@@ -55,6 +61,7 @@ __export(index_exports, {
|
|
|
55
61
|
DEFAULT_CONTRACTS: () => DEFAULT_CONTRACTS,
|
|
56
62
|
DEFAULT_TIMEOUT: () => DEFAULT_TIMEOUT,
|
|
57
63
|
Decision: () => Decision,
|
|
64
|
+
DiskUploadFileStore: () => DiskUploadFileStore,
|
|
58
65
|
ERR_BAD_SIGNATURE: () => import_axis_protocol2.ERR_BAD_SIGNATURE,
|
|
59
66
|
ERR_CONTRACT_VIOLATION: () => import_axis_protocol2.ERR_CONTRACT_VIOLATION,
|
|
60
67
|
ERR_INVALID_PACKET: () => import_axis_protocol2.ERR_INVALID_PACKET,
|
|
@@ -66,14 +73,18 @@ __export(index_exports, {
|
|
|
66
73
|
FLAG_HAS_WITNESS: () => import_axis_protocol2.FLAG_HAS_WITNESS,
|
|
67
74
|
HANDLER_METADATA_KEY: () => HANDLER_METADATA_KEY,
|
|
68
75
|
Handler: () => Handler,
|
|
76
|
+
INTENT_BODY_KEY: () => INTENT_BODY_KEY,
|
|
69
77
|
INTENT_METADATA_KEY: () => INTENT_METADATA_KEY,
|
|
70
78
|
INTENT_REQUIREMENTS: () => INTENT_REQUIREMENTS,
|
|
71
79
|
INTENT_ROUTES_KEY: () => INTENT_ROUTES_KEY,
|
|
72
80
|
INTENT_SENSITIVITY_MAP: () => INTENT_SENSITIVITY_MAP,
|
|
81
|
+
INTENT_SENSORS_KEY: () => INTENT_SENSORS_KEY,
|
|
73
82
|
INTENT_TIMEOUTS: () => INTENT_TIMEOUTS,
|
|
74
83
|
Intent: () => Intent,
|
|
84
|
+
IntentBody: () => IntentBody,
|
|
75
85
|
IntentRouter: () => IntentRouter,
|
|
76
86
|
IntentSensitivity: () => IntentSensitivity,
|
|
87
|
+
IntentSensors: () => IntentSensors,
|
|
77
88
|
MAX_BODY_LEN: () => import_axis_protocol2.MAX_BODY_LEN,
|
|
78
89
|
MAX_FRAME_LEN: () => import_axis_protocol2.MAX_FRAME_LEN,
|
|
79
90
|
MAX_HDR_LEN: () => import_axis_protocol2.MAX_HDR_LEN,
|
|
@@ -245,8 +256,26 @@ function Intent(action, options) {
|
|
|
245
256
|
};
|
|
246
257
|
}
|
|
247
258
|
|
|
248
|
-
// src/decorators/
|
|
259
|
+
// src/decorators/intent-body.decorator.ts
|
|
249
260
|
var import_reflect_metadata2 = require("reflect-metadata");
|
|
261
|
+
var INTENT_BODY_KEY = "axis:intent:body";
|
|
262
|
+
function IntentBody(decoder) {
|
|
263
|
+
return (target, propertyKey) => {
|
|
264
|
+
Reflect.defineMetadata(INTENT_BODY_KEY, decoder, target, propertyKey);
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// src/decorators/intent-sensors.decorator.ts
|
|
269
|
+
var import_reflect_metadata3 = require("reflect-metadata");
|
|
270
|
+
var INTENT_SENSORS_KEY = "axis:intent:sensors";
|
|
271
|
+
function IntentSensors(sensors) {
|
|
272
|
+
return (target, propertyKey) => {
|
|
273
|
+
Reflect.defineMetadata(INTENT_SENSORS_KEY, sensors, target, propertyKey);
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// src/decorators/tlv-field.decorator.ts
|
|
278
|
+
var import_reflect_metadata4 = require("reflect-metadata");
|
|
250
279
|
var TLV_FIELDS_KEY = "axis:tlv:fields";
|
|
251
280
|
var TLV_VALIDATORS_KEY = "axis:tlv:validators";
|
|
252
281
|
function TlvField(tag, options) {
|
|
@@ -304,7 +333,7 @@ function TlvRange(min, max, message) {
|
|
|
304
333
|
}
|
|
305
334
|
|
|
306
335
|
// src/decorators/dto-schema.util.ts
|
|
307
|
-
var
|
|
336
|
+
var import_reflect_metadata5 = require("reflect-metadata");
|
|
308
337
|
|
|
309
338
|
// src/core/tlv.ts
|
|
310
339
|
var import_axis_protocol = require("@nextera.one/axis-protocol");
|
|
@@ -405,7 +434,7 @@ __decorateClass([
|
|
|
405
434
|
], AxisIdDto.prototype, "id", 2);
|
|
406
435
|
|
|
407
436
|
// src/base/axis-partial-type.ts
|
|
408
|
-
var
|
|
437
|
+
var import_reflect_metadata6 = require("reflect-metadata");
|
|
409
438
|
function AxisPartialType(BaseDto) {
|
|
410
439
|
class PartialDto extends BaseDto {
|
|
411
440
|
}
|
|
@@ -452,10 +481,145 @@ __decorateClass([
|
|
|
452
481
|
|
|
453
482
|
// src/engine/intent.router.ts
|
|
454
483
|
var import_common2 = require("@nestjs/common");
|
|
484
|
+
|
|
485
|
+
// src/sensor/axis-sensor.ts
|
|
486
|
+
var Decision = /* @__PURE__ */ ((Decision2) => {
|
|
487
|
+
Decision2["ALLOW"] = "ALLOW";
|
|
488
|
+
Decision2["DENY"] = "DENY";
|
|
489
|
+
Decision2["THROTTLE"] = "THROTTLE";
|
|
490
|
+
Decision2["FLAG"] = "FLAG";
|
|
491
|
+
return Decision2;
|
|
492
|
+
})(Decision || {});
|
|
493
|
+
function normalizeSensorDecision(sensorDecision) {
|
|
494
|
+
if ("action" in sensorDecision) {
|
|
495
|
+
switch (sensorDecision.action) {
|
|
496
|
+
case "ALLOW":
|
|
497
|
+
return {
|
|
498
|
+
allow: true,
|
|
499
|
+
riskScore: 0,
|
|
500
|
+
reasons: [],
|
|
501
|
+
meta: sensorDecision.meta
|
|
502
|
+
};
|
|
503
|
+
case "DENY":
|
|
504
|
+
return {
|
|
505
|
+
allow: false,
|
|
506
|
+
riskScore: 100,
|
|
507
|
+
reasons: [sensorDecision.code, sensorDecision.reason].filter(
|
|
508
|
+
Boolean
|
|
509
|
+
),
|
|
510
|
+
meta: sensorDecision.meta,
|
|
511
|
+
retryAfterMs: sensorDecision.retryAfterMs
|
|
512
|
+
};
|
|
513
|
+
case "THROTTLE":
|
|
514
|
+
return {
|
|
515
|
+
allow: false,
|
|
516
|
+
riskScore: 50,
|
|
517
|
+
reasons: ["RATE_LIMIT"],
|
|
518
|
+
retryAfterMs: sensorDecision.retryAfterMs,
|
|
519
|
+
meta: sensorDecision.meta
|
|
520
|
+
};
|
|
521
|
+
case "FLAG":
|
|
522
|
+
return {
|
|
523
|
+
allow: true,
|
|
524
|
+
riskScore: sensorDecision.scoreDelta,
|
|
525
|
+
reasons: sensorDecision.reasons,
|
|
526
|
+
meta: sensorDecision.meta
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
return {
|
|
531
|
+
allow: sensorDecision.allow,
|
|
532
|
+
riskScore: sensorDecision.riskScore,
|
|
533
|
+
reasons: sensorDecision.reasons,
|
|
534
|
+
tags: sensorDecision.tags,
|
|
535
|
+
meta: sensorDecision.meta,
|
|
536
|
+
tighten: sensorDecision.tighten,
|
|
537
|
+
retryAfterMs: sensorDecision.retryAfterMs
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
var SensorDecisions = {
|
|
541
|
+
allow(meta, tags) {
|
|
542
|
+
return {
|
|
543
|
+
decision: "ALLOW" /* ALLOW */,
|
|
544
|
+
allow: true,
|
|
545
|
+
riskScore: 0,
|
|
546
|
+
reasons: [],
|
|
547
|
+
tags,
|
|
548
|
+
meta
|
|
549
|
+
};
|
|
550
|
+
},
|
|
551
|
+
deny(code, reason, meta) {
|
|
552
|
+
return {
|
|
553
|
+
decision: "DENY" /* DENY */,
|
|
554
|
+
allow: false,
|
|
555
|
+
riskScore: 100,
|
|
556
|
+
code,
|
|
557
|
+
reasons: [code, reason].filter(Boolean),
|
|
558
|
+
meta
|
|
559
|
+
};
|
|
560
|
+
},
|
|
561
|
+
throttle(retryAfterMs, meta) {
|
|
562
|
+
return {
|
|
563
|
+
decision: "THROTTLE" /* THROTTLE */,
|
|
564
|
+
allow: false,
|
|
565
|
+
riskScore: 50,
|
|
566
|
+
retryAfterMs,
|
|
567
|
+
code: "RATE_LIMIT",
|
|
568
|
+
reasons: ["RATE_LIMIT"],
|
|
569
|
+
meta
|
|
570
|
+
};
|
|
571
|
+
},
|
|
572
|
+
flag(scoreDelta, reasons, meta) {
|
|
573
|
+
return {
|
|
574
|
+
decision: "FLAG" /* FLAG */,
|
|
575
|
+
allow: true,
|
|
576
|
+
riskScore: scoreDelta,
|
|
577
|
+
scoreDelta,
|
|
578
|
+
reasons,
|
|
579
|
+
meta
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
};
|
|
583
|
+
|
|
584
|
+
// src/engine/intent.router.ts
|
|
455
585
|
var IntentRouter = class {
|
|
456
|
-
constructor() {
|
|
586
|
+
constructor(moduleRef) {
|
|
587
|
+
this.moduleRef = moduleRef;
|
|
588
|
+
this.logger = new import_common2.Logger(IntentRouter.name);
|
|
457
589
|
/** Internal registry of dynamic intent handlers */
|
|
458
590
|
this.handlers = /* @__PURE__ */ new Map();
|
|
591
|
+
/** Per-intent sensor classes (resolved at call time) */
|
|
592
|
+
this.intentSensors = /* @__PURE__ */ new Map();
|
|
593
|
+
/** Per-intent body decoders */
|
|
594
|
+
this.intentDecoders = /* @__PURE__ */ new Map();
|
|
595
|
+
/** Per-intent TLV schemas */
|
|
596
|
+
this.intentSchemas = /* @__PURE__ */ new Map();
|
|
597
|
+
/** Per-intent custom validators */
|
|
598
|
+
this.intentValidators = /* @__PURE__ */ new Map();
|
|
599
|
+
/** Per-intent operation kind */
|
|
600
|
+
this.intentKinds = /* @__PURE__ */ new Map();
|
|
601
|
+
}
|
|
602
|
+
getSchema(intent) {
|
|
603
|
+
return this.intentSchemas.get(intent);
|
|
604
|
+
}
|
|
605
|
+
getValidators(intent) {
|
|
606
|
+
return this.intentValidators.get(intent);
|
|
607
|
+
}
|
|
608
|
+
has(intent) {
|
|
609
|
+
return this.handlers.has(intent) || IntentRouter.BUILTIN_INTENTS.has(intent);
|
|
610
|
+
}
|
|
611
|
+
getRegisteredIntents() {
|
|
612
|
+
return [...IntentRouter.BUILTIN_INTENTS, ...this.handlers.keys()];
|
|
613
|
+
}
|
|
614
|
+
getIntentEntry(intent) {
|
|
615
|
+
if (!this.has(intent)) return null;
|
|
616
|
+
return {
|
|
617
|
+
schema: this.intentSchemas.get(intent),
|
|
618
|
+
validators: this.intentValidators.get(intent),
|
|
619
|
+
hasSensors: this.intentSensors.has(intent),
|
|
620
|
+
builtin: IntentRouter.BUILTIN_INTENTS.has(intent),
|
|
621
|
+
kind: this.intentKinds.get(intent)
|
|
622
|
+
};
|
|
459
623
|
}
|
|
460
624
|
/**
|
|
461
625
|
* Registers a handler for a specific intent.
|
|
@@ -491,6 +655,16 @@ var IntentRouter = class {
|
|
|
491
655
|
} else {
|
|
492
656
|
this.register(intentName, fn);
|
|
493
657
|
}
|
|
658
|
+
this.registerIntentMeta(intentName, Object.getPrototypeOf(instance), String(route.methodName));
|
|
659
|
+
}
|
|
660
|
+
const proto = Object.getPrototypeOf(instance);
|
|
661
|
+
for (const key of Object.getOwnPropertyNames(proto)) {
|
|
662
|
+
const meta = Reflect.getMetadata(INTENT_METADATA_KEY, proto, key);
|
|
663
|
+
if (!meta?.intent) continue;
|
|
664
|
+
if (!this.handlers.has(meta.intent)) {
|
|
665
|
+
this.register(meta.intent, instance[key].bind(instance));
|
|
666
|
+
}
|
|
667
|
+
this.registerIntentMeta(meta.intent, proto, key);
|
|
494
668
|
}
|
|
495
669
|
}
|
|
496
670
|
/**
|
|
@@ -514,6 +688,7 @@ var IntentRouter = class {
|
|
|
514
688
|
intent = new TextDecoder().decode(intentBytes);
|
|
515
689
|
let effect;
|
|
516
690
|
if (intent === "system.ping" || intent === "public.ping") {
|
|
691
|
+
this.logger.debug("PING received");
|
|
517
692
|
effect = {
|
|
518
693
|
ok: true,
|
|
519
694
|
effect: "pong",
|
|
@@ -554,6 +729,7 @@ var IntentRouter = class {
|
|
|
554
729
|
if (!innerIntent) {
|
|
555
730
|
throw new Error("INTENT.EXEC missing inner intent");
|
|
556
731
|
}
|
|
732
|
+
this.logger.debug(`EXEC: routing to inner intent '${innerIntent}'`);
|
|
557
733
|
const innerFrame = {
|
|
558
734
|
...frame,
|
|
559
735
|
headers: new Map(frame.headers),
|
|
@@ -569,8 +745,23 @@ var IntentRouter = class {
|
|
|
569
745
|
if (!handler) {
|
|
570
746
|
throw new Error(`Intent not found: ${intent}`);
|
|
571
747
|
}
|
|
748
|
+
const sensorClasses = this.intentSensors.get(intent);
|
|
749
|
+
if (sensorClasses && sensorClasses.length > 0) {
|
|
750
|
+
await this.runIntentSensors(sensorClasses, intent, frame);
|
|
751
|
+
}
|
|
752
|
+
const decoder = this.intentDecoders.get(intent);
|
|
753
|
+
let decodedBody = frame.body;
|
|
754
|
+
if (decoder) {
|
|
755
|
+
try {
|
|
756
|
+
decodedBody = decoder(Buffer.from(frame.body));
|
|
757
|
+
} catch (decodeErr) {
|
|
758
|
+
throw new Error(
|
|
759
|
+
`IntentBody decode failed for ${intent}: ${decodeErr.message}`
|
|
760
|
+
);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
572
763
|
if (typeof handler === "function") {
|
|
573
|
-
const resultBody = await handler(frame.body, frame.headers);
|
|
764
|
+
const resultBody = decoder ? await handler(decodedBody, frame.headers) : await handler(frame.body, frame.headers);
|
|
574
765
|
effect = {
|
|
575
766
|
ok: true,
|
|
576
767
|
effect: "complete",
|
|
@@ -580,10 +771,7 @@ var IntentRouter = class {
|
|
|
580
771
|
if (typeof handler.handle === "function") {
|
|
581
772
|
effect = await handler.handle(frame);
|
|
582
773
|
} else if (typeof handler.execute === "function") {
|
|
583
|
-
const bodyRes = await handler.execute(
|
|
584
|
-
frame.body,
|
|
585
|
-
frame.headers
|
|
586
|
-
);
|
|
774
|
+
const bodyRes = decoder ? await handler.execute(decodedBody, frame.headers) : await handler.execute(frame.body, frame.headers);
|
|
587
775
|
effect = {
|
|
588
776
|
ok: true,
|
|
589
777
|
effect: "complete",
|
|
@@ -596,20 +784,130 @@ var IntentRouter = class {
|
|
|
596
784
|
}
|
|
597
785
|
}
|
|
598
786
|
}
|
|
599
|
-
this.
|
|
787
|
+
this.logIntent(intent, start, true);
|
|
600
788
|
return effect;
|
|
601
789
|
} catch (e) {
|
|
602
|
-
|
|
790
|
+
this.logIntent(intent, start, false, e.message);
|
|
603
791
|
throw e;
|
|
604
792
|
}
|
|
605
793
|
}
|
|
606
|
-
|
|
794
|
+
logIntent(intent, start, ok, error) {
|
|
607
795
|
const diff = process.hrtime(start);
|
|
608
|
-
|
|
796
|
+
const ms = (diff[0] * 1e3 + diff[1] / 1e6).toFixed(2);
|
|
797
|
+
if (ok) {
|
|
798
|
+
this.logger.debug(`${intent} completed in ${ms}ms`);
|
|
799
|
+
} else {
|
|
800
|
+
this.logger.warn(`${intent} failed in ${ms}ms - ${error}`);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
registerIntentMeta(intent, proto, methodName) {
|
|
804
|
+
const decoder = Reflect.getMetadata(INTENT_BODY_KEY, proto, methodName);
|
|
805
|
+
if (decoder) {
|
|
806
|
+
this.intentDecoders.set(intent, decoder);
|
|
807
|
+
}
|
|
808
|
+
const sensors = Reflect.getMetadata(INTENT_SENSORS_KEY, proto, methodName);
|
|
809
|
+
if (sensors && Array.isArray(sensors) && sensors.length > 0) {
|
|
810
|
+
this.intentSensors.set(intent, sensors);
|
|
811
|
+
}
|
|
812
|
+
const meta = Reflect.getMetadata(INTENT_METADATA_KEY, proto, methodName);
|
|
813
|
+
if (meta) {
|
|
814
|
+
this.storeSchema(meta);
|
|
815
|
+
if (meta.kind) {
|
|
816
|
+
this.intentKinds.set(intent, meta.kind);
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
async runIntentSensors(sensorClasses, intent, frame) {
|
|
821
|
+
if (!this.moduleRef) return;
|
|
822
|
+
for (const SensorClass of sensorClasses) {
|
|
823
|
+
let sensor;
|
|
824
|
+
try {
|
|
825
|
+
sensor = this.moduleRef.get(SensorClass, { strict: false });
|
|
826
|
+
} catch {
|
|
827
|
+
this.logger.warn(
|
|
828
|
+
`@IntentSensors: could not resolve ${SensorClass.name} for ${intent}`
|
|
829
|
+
);
|
|
830
|
+
continue;
|
|
831
|
+
}
|
|
832
|
+
const sensorInput = {
|
|
833
|
+
rawBytes: frame.body,
|
|
834
|
+
intent,
|
|
835
|
+
body: frame.body,
|
|
836
|
+
headerTLVs: frame.headers,
|
|
837
|
+
metadata: { phase: "intent", intent }
|
|
838
|
+
};
|
|
839
|
+
if (sensor.supports && !sensor.supports(sensorInput)) continue;
|
|
840
|
+
const decision = normalizeSensorDecision(await sensor.run(sensorInput));
|
|
841
|
+
if (!decision.allow) {
|
|
842
|
+
const reason = decision.reasons[0] || `${sensor.name}:DENIED`;
|
|
843
|
+
this.logger.warn(
|
|
844
|
+
`Intent sensor ${sensor.name} denied ${intent}: ${reason}`
|
|
845
|
+
);
|
|
846
|
+
throw new Error(`SENSOR_DENY:${reason}`);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
storeSchema(meta) {
|
|
851
|
+
if (meta.dto) {
|
|
852
|
+
if (meta.tlv && meta.tlv.length > 0) {
|
|
853
|
+
this.logger.warn(
|
|
854
|
+
`${meta.intent}: both 'dto' and 'tlv' specified - using dto, ignoring tlv`
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
const extracted = extractDtoSchema(meta.dto);
|
|
858
|
+
const schema2 = {
|
|
859
|
+
intent: meta.intent,
|
|
860
|
+
version: 1,
|
|
861
|
+
bodyProfile: meta.bodyProfile || "TLV_MAP",
|
|
862
|
+
fields: extracted.fields.map((f) => ({
|
|
863
|
+
name: f.name,
|
|
864
|
+
tlv: f.tag,
|
|
865
|
+
kind: f.kind,
|
|
866
|
+
required: f.required,
|
|
867
|
+
maxLen: f.maxLen,
|
|
868
|
+
max: f.max,
|
|
869
|
+
scope: f.scope
|
|
870
|
+
}))
|
|
871
|
+
};
|
|
872
|
+
this.intentSchemas.set(meta.intent, schema2);
|
|
873
|
+
if (extracted.validators.size > 0) {
|
|
874
|
+
this.intentValidators.set(meta.intent, extracted.validators);
|
|
875
|
+
}
|
|
876
|
+
if (!this.intentDecoders.has(meta.intent)) {
|
|
877
|
+
this.intentDecoders.set(meta.intent, buildDtoDecoder(meta.dto));
|
|
878
|
+
}
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
881
|
+
if (!meta.tlv || meta.tlv.length === 0) return;
|
|
882
|
+
const schema = {
|
|
883
|
+
intent: meta.intent,
|
|
884
|
+
version: 1,
|
|
885
|
+
bodyProfile: meta.bodyProfile || "TLV_MAP",
|
|
886
|
+
fields: meta.tlv.map((f) => ({
|
|
887
|
+
name: f.name,
|
|
888
|
+
tlv: f.tag,
|
|
889
|
+
kind: f.kind,
|
|
890
|
+
required: f.required,
|
|
891
|
+
maxLen: f.maxLen,
|
|
892
|
+
max: f.max,
|
|
893
|
+
scope: f.scope
|
|
894
|
+
}))
|
|
895
|
+
};
|
|
896
|
+
this.intentSchemas.set(meta.intent, schema);
|
|
609
897
|
}
|
|
610
898
|
};
|
|
899
|
+
/** Intents handled inline in route() — not in `handlers` map */
|
|
900
|
+
IntentRouter.BUILTIN_INTENTS = /* @__PURE__ */ new Set([
|
|
901
|
+
"system.ping",
|
|
902
|
+
"public.ping",
|
|
903
|
+
"system.time",
|
|
904
|
+
"system.echo",
|
|
905
|
+
"INTENT.EXEC",
|
|
906
|
+
"axis.intent.exec"
|
|
907
|
+
]);
|
|
611
908
|
IntentRouter = __decorateClass([
|
|
612
|
-
(0, import_common2.Injectable)()
|
|
909
|
+
(0, import_common2.Injectable)(),
|
|
910
|
+
__decorateParam(0, (0, import_common2.Optional)())
|
|
613
911
|
], IntentRouter);
|
|
614
912
|
|
|
615
913
|
// src/core/constants.ts
|
|
@@ -1985,105 +2283,6 @@ function buildPacket(hdr, body, sig, flags = 0) {
|
|
|
1985
2283
|
};
|
|
1986
2284
|
}
|
|
1987
2285
|
|
|
1988
|
-
// src/sensor/axis-sensor.ts
|
|
1989
|
-
var Decision = /* @__PURE__ */ ((Decision2) => {
|
|
1990
|
-
Decision2["ALLOW"] = "ALLOW";
|
|
1991
|
-
Decision2["DENY"] = "DENY";
|
|
1992
|
-
Decision2["THROTTLE"] = "THROTTLE";
|
|
1993
|
-
Decision2["FLAG"] = "FLAG";
|
|
1994
|
-
return Decision2;
|
|
1995
|
-
})(Decision || {});
|
|
1996
|
-
function normalizeSensorDecision(sensorDecision) {
|
|
1997
|
-
if ("action" in sensorDecision) {
|
|
1998
|
-
switch (sensorDecision.action) {
|
|
1999
|
-
case "ALLOW":
|
|
2000
|
-
return {
|
|
2001
|
-
allow: true,
|
|
2002
|
-
riskScore: 0,
|
|
2003
|
-
reasons: [],
|
|
2004
|
-
meta: sensorDecision.meta
|
|
2005
|
-
};
|
|
2006
|
-
case "DENY":
|
|
2007
|
-
return {
|
|
2008
|
-
allow: false,
|
|
2009
|
-
riskScore: 100,
|
|
2010
|
-
reasons: [sensorDecision.code, sensorDecision.reason].filter(
|
|
2011
|
-
Boolean
|
|
2012
|
-
),
|
|
2013
|
-
meta: sensorDecision.meta,
|
|
2014
|
-
retryAfterMs: sensorDecision.retryAfterMs
|
|
2015
|
-
};
|
|
2016
|
-
case "THROTTLE":
|
|
2017
|
-
return {
|
|
2018
|
-
allow: false,
|
|
2019
|
-
riskScore: 50,
|
|
2020
|
-
reasons: ["RATE_LIMIT"],
|
|
2021
|
-
retryAfterMs: sensorDecision.retryAfterMs,
|
|
2022
|
-
meta: sensorDecision.meta
|
|
2023
|
-
};
|
|
2024
|
-
case "FLAG":
|
|
2025
|
-
return {
|
|
2026
|
-
allow: true,
|
|
2027
|
-
riskScore: sensorDecision.scoreDelta,
|
|
2028
|
-
reasons: sensorDecision.reasons,
|
|
2029
|
-
meta: sensorDecision.meta
|
|
2030
|
-
};
|
|
2031
|
-
}
|
|
2032
|
-
}
|
|
2033
|
-
return {
|
|
2034
|
-
allow: sensorDecision.allow,
|
|
2035
|
-
riskScore: sensorDecision.riskScore,
|
|
2036
|
-
reasons: sensorDecision.reasons,
|
|
2037
|
-
tags: sensorDecision.tags,
|
|
2038
|
-
meta: sensorDecision.meta,
|
|
2039
|
-
tighten: sensorDecision.tighten,
|
|
2040
|
-
retryAfterMs: sensorDecision.retryAfterMs
|
|
2041
|
-
};
|
|
2042
|
-
}
|
|
2043
|
-
var SensorDecisions = {
|
|
2044
|
-
allow(meta, tags) {
|
|
2045
|
-
return {
|
|
2046
|
-
decision: "ALLOW" /* ALLOW */,
|
|
2047
|
-
allow: true,
|
|
2048
|
-
riskScore: 0,
|
|
2049
|
-
reasons: [],
|
|
2050
|
-
tags,
|
|
2051
|
-
meta
|
|
2052
|
-
};
|
|
2053
|
-
},
|
|
2054
|
-
deny(code, reason, meta) {
|
|
2055
|
-
return {
|
|
2056
|
-
decision: "DENY" /* DENY */,
|
|
2057
|
-
allow: false,
|
|
2058
|
-
riskScore: 100,
|
|
2059
|
-
code,
|
|
2060
|
-
reasons: [code, reason].filter(Boolean),
|
|
2061
|
-
meta
|
|
2062
|
-
};
|
|
2063
|
-
},
|
|
2064
|
-
throttle(retryAfterMs, meta) {
|
|
2065
|
-
return {
|
|
2066
|
-
decision: "THROTTLE" /* THROTTLE */,
|
|
2067
|
-
allow: false,
|
|
2068
|
-
riskScore: 50,
|
|
2069
|
-
retryAfterMs,
|
|
2070
|
-
code: "RATE_LIMIT",
|
|
2071
|
-
reasons: ["RATE_LIMIT"],
|
|
2072
|
-
meta
|
|
2073
|
-
};
|
|
2074
|
-
},
|
|
2075
|
-
flag(scoreDelta, reasons, meta) {
|
|
2076
|
-
return {
|
|
2077
|
-
decision: "FLAG" /* FLAG */,
|
|
2078
|
-
allow: true,
|
|
2079
|
-
riskScore: scoreDelta,
|
|
2080
|
-
scoreDelta,
|
|
2081
|
-
reasons,
|
|
2082
|
-
meta
|
|
2083
|
-
};
|
|
2084
|
-
}
|
|
2085
|
-
};
|
|
2086
|
-
|
|
2087
2286
|
// src/security/scopes.ts
|
|
2088
2287
|
function hasScope(scopes, required) {
|
|
2089
2288
|
if (!Array.isArray(scopes) || scopes.length === 0) {
|
|
@@ -2381,14 +2580,246 @@ function isTimestampValid(ts, skewSeconds = 120) {
|
|
|
2381
2580
|
const diff = Math.abs(now - ts);
|
|
2382
2581
|
return diff <= skewSeconds;
|
|
2383
2582
|
}
|
|
2583
|
+
|
|
2584
|
+
// src/upload/axis-files.handlers.ts
|
|
2585
|
+
var import_common3 = require("@nestjs/common");
|
|
2586
|
+
var crypto2 = __toESM(require("crypto"));
|
|
2587
|
+
|
|
2588
|
+
// src/upload/upload.tokens.ts
|
|
2589
|
+
var AXIS_UPLOAD_SESSION_STORE = "AXIS_UPLOAD_SESSION_STORE";
|
|
2590
|
+
var AXIS_UPLOAD_FILE_STORE = "AXIS_UPLOAD_FILE_STORE";
|
|
2591
|
+
var AXIS_UPLOAD_RECEIPT_SIGNER = "AXIS_UPLOAD_RECEIPT_SIGNER";
|
|
2592
|
+
|
|
2593
|
+
// src/upload/axis-files.handlers.ts
|
|
2594
|
+
var AxisFilesDownloadHandler = class {
|
|
2595
|
+
constructor(sessions, files) {
|
|
2596
|
+
this.sessions = sessions;
|
|
2597
|
+
this.files = files;
|
|
2598
|
+
this.logger = new import_common3.Logger(AxisFilesDownloadHandler.name);
|
|
2599
|
+
this.name = "axis.files.download";
|
|
2600
|
+
this.open = true;
|
|
2601
|
+
this.description = "File download handler";
|
|
2602
|
+
}
|
|
2603
|
+
async execute(body, headers) {
|
|
2604
|
+
const h = headers;
|
|
2605
|
+
if (!h) throw new Error("MISSING_HEADERS");
|
|
2606
|
+
const uploadIdBytes = h.get(20);
|
|
2607
|
+
if (!uploadIdBytes) throw new Error("MISSING_UPLOAD_ID");
|
|
2608
|
+
const uploadId = new TextDecoder().decode(uploadIdBytes);
|
|
2609
|
+
let rangeStart = 0;
|
|
2610
|
+
let rangeLen = -1;
|
|
2611
|
+
const startBytes = h.get(21);
|
|
2612
|
+
if (startBytes) {
|
|
2613
|
+
const { value } = (0, import_axis_protocol3.decodeVarint)(startBytes);
|
|
2614
|
+
rangeStart = value;
|
|
2615
|
+
}
|
|
2616
|
+
const lenBytes = h.get(22);
|
|
2617
|
+
if (lenBytes) {
|
|
2618
|
+
const { value } = (0, import_axis_protocol3.decodeVarint)(lenBytes);
|
|
2619
|
+
rangeLen = value;
|
|
2620
|
+
}
|
|
2621
|
+
const session = await this.sessions.findByFileId(uploadId);
|
|
2622
|
+
if (!session) {
|
|
2623
|
+
throw new Error(`SESSION_NOT_FOUND: ${uploadId}`);
|
|
2624
|
+
}
|
|
2625
|
+
if (session.status !== "COMPLETE") {
|
|
2626
|
+
throw new Error(`FILE_NOT_READY: Status is ${session.status}`);
|
|
2627
|
+
}
|
|
2628
|
+
const stat = await this.files.statFinal(
|
|
2629
|
+
uploadId,
|
|
2630
|
+
session.filename
|
|
2631
|
+
);
|
|
2632
|
+
const fileSize = stat.size;
|
|
2633
|
+
if (rangeStart < 0) rangeStart = 0;
|
|
2634
|
+
if (rangeStart >= fileSize) throw new Error("RANGE_OUT_OF_BOUNDS");
|
|
2635
|
+
let end = fileSize;
|
|
2636
|
+
if (rangeLen >= 0) {
|
|
2637
|
+
end = Math.min(rangeStart + rangeLen, fileSize);
|
|
2638
|
+
}
|
|
2639
|
+
const actualLen = end - rangeStart;
|
|
2640
|
+
const buffer = await this.files.readFinalRange(
|
|
2641
|
+
uploadId,
|
|
2642
|
+
session.filename,
|
|
2643
|
+
rangeStart,
|
|
2644
|
+
actualLen
|
|
2645
|
+
);
|
|
2646
|
+
const responseHeaders = /* @__PURE__ */ new Map();
|
|
2647
|
+
responseHeaders.set(30, (0, import_axis_protocol3.encodeVarint)(fileSize));
|
|
2648
|
+
responseHeaders.set(31, (0, import_axis_protocol3.encodeVarint)(rangeStart));
|
|
2649
|
+
responseHeaders.set(32, (0, import_axis_protocol3.encodeVarint)(actualLen));
|
|
2650
|
+
return {
|
|
2651
|
+
ok: true,
|
|
2652
|
+
effect: "FILE_PART",
|
|
2653
|
+
body: buffer,
|
|
2654
|
+
headers: responseHeaders
|
|
2655
|
+
};
|
|
2656
|
+
}
|
|
2657
|
+
};
|
|
2658
|
+
__decorateClass([
|
|
2659
|
+
Intent("file.download", { absolute: true, kind: "read" })
|
|
2660
|
+
], AxisFilesDownloadHandler.prototype, "execute", 1);
|
|
2661
|
+
AxisFilesDownloadHandler = __decorateClass([
|
|
2662
|
+
Handler("axis.files.download"),
|
|
2663
|
+
(0, import_common3.Injectable)(),
|
|
2664
|
+
__decorateParam(0, (0, import_common3.Inject)(AXIS_UPLOAD_SESSION_STORE)),
|
|
2665
|
+
__decorateParam(1, (0, import_common3.Inject)(AXIS_UPLOAD_FILE_STORE))
|
|
2666
|
+
], AxisFilesDownloadHandler);
|
|
2667
|
+
var AxisFilesFinalizeHandler = class {
|
|
2668
|
+
constructor(sessions, files, keyring) {
|
|
2669
|
+
this.sessions = sessions;
|
|
2670
|
+
this.files = files;
|
|
2671
|
+
this.keyring = keyring;
|
|
2672
|
+
this.logger = new import_common3.Logger(AxisFilesFinalizeHandler.name);
|
|
2673
|
+
this.name = "axis.files.finalize";
|
|
2674
|
+
this.open = false;
|
|
2675
|
+
this.description = "File upload finalization handler";
|
|
2676
|
+
}
|
|
2677
|
+
async execute(body, headers) {
|
|
2678
|
+
const bodyStr = new TextDecoder().decode(body);
|
|
2679
|
+
const req = JSON.parse(bodyStr);
|
|
2680
|
+
const { fileId, expectedHash } = req;
|
|
2681
|
+
if (!fileId) throw new Error("MISSING_FILE_ID");
|
|
2682
|
+
const session = await this.sessions.findByFileId(fileId);
|
|
2683
|
+
if (!session) throw new Error("SESSION_NOT_FOUND");
|
|
2684
|
+
if (!await this.files.hasTemp(fileId)) {
|
|
2685
|
+
throw new Error("CHUNKS_NOT_FOUND");
|
|
2686
|
+
}
|
|
2687
|
+
const hash = crypto2.createHash("sha256");
|
|
2688
|
+
const rs = this.files.createTempReadStream(fileId);
|
|
2689
|
+
for await (const chunk of rs) {
|
|
2690
|
+
hash.update(chunk);
|
|
2691
|
+
}
|
|
2692
|
+
const finalHash = hash.digest("hex");
|
|
2693
|
+
if (expectedHash && finalHash !== expectedHash) {
|
|
2694
|
+
throw new Error("HASH_MISMATCH");
|
|
2695
|
+
}
|
|
2696
|
+
const finalPath = await this.files.moveTempToFinal(
|
|
2697
|
+
fileId,
|
|
2698
|
+
session.filename
|
|
2699
|
+
);
|
|
2700
|
+
await this.sessions.updateStatus(fileId, "COMPLETE", null);
|
|
2701
|
+
if (!this.keyring) {
|
|
2702
|
+
this.logger.warn("Receipt signer not configured; returning unsigned receipt");
|
|
2703
|
+
return {
|
|
2704
|
+
ok: true,
|
|
2705
|
+
effect: "FILE_FINALIZED",
|
|
2706
|
+
body: new TextEncoder().encode(
|
|
2707
|
+
JSON.stringify({
|
|
2708
|
+
uploadId: fileId,
|
|
2709
|
+
sha256_final: finalHash,
|
|
2710
|
+
totalSize: session.totalSize,
|
|
2711
|
+
tsMs: Date.now(),
|
|
2712
|
+
path: finalPath
|
|
2713
|
+
})
|
|
2714
|
+
)
|
|
2715
|
+
};
|
|
2716
|
+
}
|
|
2717
|
+
const receiptData = {
|
|
2718
|
+
uploadId: fileId,
|
|
2719
|
+
sha256_final: finalHash,
|
|
2720
|
+
totalSize: session.totalSize,
|
|
2721
|
+
tsMs: Date.now()
|
|
2722
|
+
};
|
|
2723
|
+
const receiptJson = JSON.stringify(receiptData);
|
|
2724
|
+
const receiptBody = new TextEncoder().encode(receiptJson);
|
|
2725
|
+
const SIG_PRESENT = 1;
|
|
2726
|
+
const responseFrame = {
|
|
2727
|
+
flags: SIG_PRESENT,
|
|
2728
|
+
headers: /* @__PURE__ */ new Map(),
|
|
2729
|
+
body: receiptBody,
|
|
2730
|
+
sig: new Uint8Array(0)
|
|
2731
|
+
};
|
|
2732
|
+
const signTarget = getSignTarget(responseFrame);
|
|
2733
|
+
const { sig, kid } = this.keyring.signActive(signTarget);
|
|
2734
|
+
responseFrame.sig = sig;
|
|
2735
|
+
return {
|
|
2736
|
+
ok: true,
|
|
2737
|
+
effect: "FILE_FINALIZED",
|
|
2738
|
+
data: encodeFrame(responseFrame),
|
|
2739
|
+
headers: /* @__PURE__ */ new Map([[1, new TextEncoder().encode(kid)]])
|
|
2740
|
+
};
|
|
2741
|
+
}
|
|
2742
|
+
};
|
|
2743
|
+
__decorateClass([
|
|
2744
|
+
Intent("file.finalize", { absolute: true, kind: "action" })
|
|
2745
|
+
], AxisFilesFinalizeHandler.prototype, "execute", 1);
|
|
2746
|
+
AxisFilesFinalizeHandler = __decorateClass([
|
|
2747
|
+
Handler("axis.files.finalize"),
|
|
2748
|
+
(0, import_common3.Injectable)(),
|
|
2749
|
+
__decorateParam(0, (0, import_common3.Inject)(AXIS_UPLOAD_SESSION_STORE)),
|
|
2750
|
+
__decorateParam(1, (0, import_common3.Inject)(AXIS_UPLOAD_FILE_STORE)),
|
|
2751
|
+
__decorateParam(2, (0, import_common3.Optional)()),
|
|
2752
|
+
__decorateParam(2, (0, import_common3.Inject)(AXIS_UPLOAD_RECEIPT_SIGNER))
|
|
2753
|
+
], AxisFilesFinalizeHandler);
|
|
2754
|
+
|
|
2755
|
+
// src/upload/disk-upload-file.store.ts
|
|
2756
|
+
var fs = __toESM(require("fs"));
|
|
2757
|
+
var path = __toESM(require("path"));
|
|
2758
|
+
var DiskUploadFileStore = class {
|
|
2759
|
+
constructor(options) {
|
|
2760
|
+
this.uploadDir = options.uploadDir;
|
|
2761
|
+
this.chunkDir = options.chunkDir;
|
|
2762
|
+
}
|
|
2763
|
+
getFinalPath(fileId, filename) {
|
|
2764
|
+
const safeFilename = filename ? path.basename(filename) : fileId;
|
|
2765
|
+
return path.join(this.uploadDir, safeFilename);
|
|
2766
|
+
}
|
|
2767
|
+
getTempPath(fileId) {
|
|
2768
|
+
const safeId = path.basename(fileId);
|
|
2769
|
+
return path.join(this.chunkDir, safeId);
|
|
2770
|
+
}
|
|
2771
|
+
async statFinal(fileId, filename) {
|
|
2772
|
+
const finalPath = this.getFinalPath(fileId, filename);
|
|
2773
|
+
if (!fs.existsSync(finalPath)) {
|
|
2774
|
+
throw new Error("FILE_MISSING_ON_DISK");
|
|
2775
|
+
}
|
|
2776
|
+
const stat = fs.statSync(finalPath);
|
|
2777
|
+
return { path: finalPath, size: stat.size };
|
|
2778
|
+
}
|
|
2779
|
+
async readFinalRange(fileId, filename, start, length) {
|
|
2780
|
+
const finalPath = this.getFinalPath(fileId, filename);
|
|
2781
|
+
const buffer = Buffer.alloc(length);
|
|
2782
|
+
const fd = fs.openSync(finalPath, "r");
|
|
2783
|
+
try {
|
|
2784
|
+
fs.readSync(fd, buffer, 0, length, start);
|
|
2785
|
+
} finally {
|
|
2786
|
+
fs.closeSync(fd);
|
|
2787
|
+
}
|
|
2788
|
+
return buffer;
|
|
2789
|
+
}
|
|
2790
|
+
async hasTemp(fileId) {
|
|
2791
|
+
const tempPath = this.getTempPath(fileId);
|
|
2792
|
+
return fs.existsSync(tempPath);
|
|
2793
|
+
}
|
|
2794
|
+
async moveTempToFinal(fileId, filename) {
|
|
2795
|
+
const tempPath = this.getTempPath(fileId);
|
|
2796
|
+
const finalPath = this.getFinalPath(fileId, filename);
|
|
2797
|
+
try {
|
|
2798
|
+
await fs.promises.rename(tempPath, finalPath);
|
|
2799
|
+
} catch {
|
|
2800
|
+
await fs.promises.copyFile(tempPath, finalPath);
|
|
2801
|
+
await fs.promises.unlink(tempPath);
|
|
2802
|
+
}
|
|
2803
|
+
return finalPath;
|
|
2804
|
+
}
|
|
2805
|
+
createTempReadStream(fileId) {
|
|
2806
|
+
const tempPath = this.getTempPath(fileId);
|
|
2807
|
+
return fs.createReadStream(tempPath);
|
|
2808
|
+
}
|
|
2809
|
+
};
|
|
2384
2810
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2385
2811
|
0 && (module.exports = {
|
|
2386
2812
|
ATS1_HDR,
|
|
2387
2813
|
ATS1_SCHEMA,
|
|
2388
2814
|
AXIS_MAGIC,
|
|
2389
2815
|
AXIS_OPCODES,
|
|
2816
|
+
AXIS_UPLOAD_FILE_STORE,
|
|
2817
|
+
AXIS_UPLOAD_RECEIPT_SIGNER,
|
|
2818
|
+
AXIS_UPLOAD_SESSION_STORE,
|
|
2390
2819
|
AXIS_VERSION,
|
|
2391
2820
|
Ats1Codec,
|
|
2821
|
+
AxisFilesDownloadHandler,
|
|
2822
|
+
AxisFilesFinalizeHandler,
|
|
2392
2823
|
AxisFrameZ,
|
|
2393
2824
|
AxisIdDto,
|
|
2394
2825
|
AxisPacketTags,
|
|
@@ -2401,6 +2832,7 @@ function isTimestampValid(ts, skewSeconds = 120) {
|
|
|
2401
2832
|
DEFAULT_CONTRACTS,
|
|
2402
2833
|
DEFAULT_TIMEOUT,
|
|
2403
2834
|
Decision,
|
|
2835
|
+
DiskUploadFileStore,
|
|
2404
2836
|
ERR_BAD_SIGNATURE,
|
|
2405
2837
|
ERR_CONTRACT_VIOLATION,
|
|
2406
2838
|
ERR_INVALID_PACKET,
|
|
@@ -2412,14 +2844,18 @@ function isTimestampValid(ts, skewSeconds = 120) {
|
|
|
2412
2844
|
FLAG_HAS_WITNESS,
|
|
2413
2845
|
HANDLER_METADATA_KEY,
|
|
2414
2846
|
Handler,
|
|
2847
|
+
INTENT_BODY_KEY,
|
|
2415
2848
|
INTENT_METADATA_KEY,
|
|
2416
2849
|
INTENT_REQUIREMENTS,
|
|
2417
2850
|
INTENT_ROUTES_KEY,
|
|
2418
2851
|
INTENT_SENSITIVITY_MAP,
|
|
2852
|
+
INTENT_SENSORS_KEY,
|
|
2419
2853
|
INTENT_TIMEOUTS,
|
|
2420
2854
|
Intent,
|
|
2855
|
+
IntentBody,
|
|
2421
2856
|
IntentRouter,
|
|
2422
2857
|
IntentSensitivity,
|
|
2858
|
+
IntentSensors,
|
|
2423
2859
|
MAX_BODY_LEN,
|
|
2424
2860
|
MAX_FRAME_LEN,
|
|
2425
2861
|
MAX_HDR_LEN,
|