@things-factory/headless-twin 10.0.19 → 10.1.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-server/engine/canonical-ingest.d.ts +10 -1
- package/dist-server/engine/canonical-ingest.js +110 -33
- package/dist-server/engine/canonical-ingest.js.map +1 -1
- package/dist-server/engine/event-time.d.ts +25 -0
- package/dist-server/engine/event-time.js +98 -0
- package/dist-server/engine/event-time.js.map +1 -0
- package/dist-server/engine/index.d.ts +2 -0
- package/dist-server/engine/index.js +23 -0
- package/dist-server/engine/index.js.map +1 -1
- package/dist-server/engine/ingest-dedupe.d.ts +18 -6
- package/dist-server/engine/ingest-dedupe.js +201 -15
- package/dist-server/engine/ingest-dedupe.js.map +1 -1
- package/dist-server/engine/ingest-health.d.ts +60 -1
- package/dist-server/engine/ingest-health.js +43 -8
- package/dist-server/engine/ingest-health.js.map +1 -1
- package/dist-server/engine/kpi-fold.d.ts +19 -0
- package/dist-server/engine/kpi-fold.js +4 -1
- package/dist-server/engine/kpi-fold.js.map +1 -1
- package/dist-server/engine/kpi-query.js +23 -3
- package/dist-server/engine/kpi-query.js.map +1 -1
- package/dist-server/engine/oee-accumulator.d.ts +35 -2
- package/dist-server/engine/oee-accumulator.js +56 -4
- package/dist-server/engine/oee-accumulator.js.map +1 -1
- package/dist-server/engine/twin-engine.d.ts +72 -3
- package/dist-server/engine/twin-engine.js +118 -5
- package/dist-server/engine/twin-engine.js.map +1 -1
- package/dist-server/index.js +21 -0
- package/dist-server/index.js.map +1 -1
- package/dist-server/migrations/1786600000000-RenameOperatoMesAdapterType.d.ts +5 -0
- package/dist-server/migrations/1786600000000-RenameOperatoMesAdapterType.js +66 -0
- package/dist-server/migrations/1786600000000-RenameOperatoMesAdapterType.js.map +1 -0
- package/dist-server/migrations/1786700000000-AddDispatchRetryColumns.d.ts +5 -0
- package/dist-server/migrations/1786700000000-AddDispatchRetryColumns.js +79 -0
- package/dist-server/migrations/1786700000000-AddDispatchRetryColumns.js.map +1 -0
- package/dist-server/migrations/index.js +5 -1
- package/dist-server/migrations/index.js.map +1 -1
- package/dist-server/routes.js +12 -20
- package/dist-server/routes.js.map +1 -1
- package/dist-server/service/actuation/actuation-advisor.d.ts +60 -0
- package/dist-server/service/actuation/actuation-advisor.js +53 -0
- package/dist-server/service/actuation/actuation-advisor.js.map +1 -0
- package/dist-server/service/actuation/actuation-outcome.d.ts +52 -0
- package/dist-server/service/actuation/actuation-outcome.js +34 -0
- package/dist-server/service/actuation/actuation-outcome.js.map +1 -0
- package/dist-server/service/actuation/actuation-rule-resolver.d.ts +19 -0
- package/dist-server/service/actuation/actuation-rule-resolver.js +161 -0
- package/dist-server/service/actuation/actuation-rule-resolver.js.map +1 -0
- package/dist-server/service/actuation/actuation-rule.d.ts +55 -0
- package/dist-server/service/actuation/actuation-rule.js +130 -0
- package/dist-server/service/actuation/actuation-rule.js.map +1 -0
- package/dist-server/service/actuation/approval-gateway.d.ts +20 -0
- package/dist-server/service/actuation/approval-gateway.js +14 -0
- package/dist-server/service/actuation/approval-gateway.js.map +1 -0
- package/dist-server/service/actuation/catch-up-effects.d.ts +16 -0
- package/dist-server/service/actuation/catch-up-effects.js +120 -0
- package/dist-server/service/actuation/catch-up-effects.js.map +1 -0
- package/dist-server/service/actuation/command-dispatcher.d.ts +206 -8
- package/dist-server/service/actuation/command-dispatcher.js +191 -14
- package/dist-server/service/actuation/command-dispatcher.js.map +1 -1
- package/dist-server/service/actuation/command-store.d.ts +10 -2
- package/dist-server/service/actuation/command-store.js +168 -5
- package/dist-server/service/actuation/command-store.js.map +1 -1
- package/dist-server/service/actuation/dispatch-after-approval.d.ts +11 -0
- package/dist-server/service/actuation/dispatch-after-approval.js +73 -0
- package/dist-server/service/actuation/dispatch-after-approval.js.map +1 -0
- package/dist-server/service/actuation/effect-match.d.ts +53 -0
- package/dist-server/service/actuation/effect-match.js +65 -0
- package/dist-server/service/actuation/effect-match.js.map +1 -0
- package/dist-server/service/actuation/index.d.ts +21 -2
- package/dist-server/service/actuation/index.js +21 -2
- package/dist-server/service/actuation/index.js.map +1 -1
- package/dist-server/service/actuation/retry-plan.d.ts +63 -0
- package/dist-server/service/actuation/retry-plan.js +99 -0
- package/dist-server/service/actuation/retry-plan.js.map +1 -0
- package/dist-server/service/actuation/rule-evaluate.d.ts +127 -0
- package/dist-server/service/actuation/rule-evaluate.js +205 -0
- package/dist-server/service/actuation/rule-evaluate.js.map +1 -0
- package/dist-server/service/actuation/rule-loop.d.ts +5 -0
- package/dist-server/service/actuation/rule-loop.js +156 -0
- package/dist-server/service/actuation/rule-loop.js.map +1 -0
- package/dist-server/service/actuation/rule-runner.d.ts +52 -0
- package/dist-server/service/actuation/rule-runner.js +135 -0
- package/dist-server/service/actuation/rule-runner.js.map +1 -0
- package/dist-server/service/actuation/rule-wiring.d.ts +24 -0
- package/dist-server/service/actuation/rule-wiring.js +80 -0
- package/dist-server/service/actuation/rule-wiring.js.map +1 -0
- package/dist-server/service/actuation/settle-effects.d.ts +21 -0
- package/dist-server/service/actuation/settle-effects.js +101 -0
- package/dist-server/service/actuation/settle-effects.js.map +1 -0
- package/dist-server/service/actuation/sweep-dispatch.d.ts +37 -0
- package/dist-server/service/actuation/sweep-dispatch.js +125 -0
- package/dist-server/service/actuation/sweep-dispatch.js.map +1 -0
- package/dist-server/service/actuation/twin-command-resolver.d.ts +120 -0
- package/dist-server/service/actuation/twin-command-resolver.js +406 -0
- package/dist-server/service/actuation/twin-command-resolver.js.map +1 -0
- package/dist-server/service/actuation/twin-command.d.ts +81 -0
- package/dist-server/service/actuation/twin-command.js +60 -0
- package/dist-server/service/actuation/twin-command.js.map +1 -1
- package/dist-server/service/actuation/twin-now.d.ts +41 -0
- package/dist-server/service/actuation/twin-now.js +65 -0
- package/dist-server/service/actuation/twin-now.js.map +1 -0
- package/dist-server/service/index.d.ts +5 -2
- package/dist-server/service/index.js +8 -0
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/reference/actuation-routing.d.ts +60 -0
- package/dist-server/service/reference/actuation-routing.js +126 -0
- package/dist-server/service/reference/actuation-routing.js.map +1 -0
- package/dist-server/service/reference/actuation-target.d.ts +65 -0
- package/dist-server/service/reference/actuation-target.js +94 -0
- package/dist-server/service/reference/actuation-target.js.map +1 -0
- package/dist-server/service/reference/connection-portability-resolver.d.ts +29 -0
- package/dist-server/service/reference/connection-portability-resolver.js +181 -0
- package/dist-server/service/reference/connection-portability-resolver.js.map +1 -0
- package/dist-server/service/reference/connection-portability.d.ts +138 -0
- package/dist-server/service/reference/connection-portability.js +230 -0
- package/dist-server/service/reference/connection-portability.js.map +1 -0
- package/dist-server/service/reference/fill-cadence.d.ts +17 -0
- package/dist-server/service/reference/fill-cadence.js +31 -0
- package/dist-server/service/reference/fill-cadence.js.map +1 -0
- package/dist-server/service/reference/fill-dedupe.d.ts +16 -0
- package/dist-server/service/reference/fill-dedupe.js +63 -0
- package/dist-server/service/reference/fill-dedupe.js.map +1 -0
- package/dist-server/service/reference/fill-loop.d.ts +35 -0
- package/dist-server/service/reference/fill-loop.js +187 -0
- package/dist-server/service/reference/fill-loop.js.map +1 -0
- package/dist-server/service/reference/fill-span.d.ts +10 -0
- package/dist-server/service/reference/fill-span.js +37 -0
- package/dist-server/service/reference/fill-span.js.map +1 -0
- package/dist-server/service/reference/hook-contract.d.ts +88 -4
- package/dist-server/service/reference/hook-contract.js +105 -4
- package/dist-server/service/reference/hook-contract.js.map +1 -1
- package/dist-server/service/reference/hook-rejected.d.ts +25 -0
- package/dist-server/service/reference/hook-rejected.js +48 -0
- package/dist-server/service/reference/hook-rejected.js.map +1 -0
- package/dist-server/service/reference/index.d.ts +4 -1
- package/dist-server/service/reference/index.js +5 -1
- package/dist-server/service/reference/index.js.map +1 -1
- package/dist-server/service/reference/live-cadence.d.ts +68 -0
- package/dist-server/service/reference/live-cadence.js +44 -0
- package/dist-server/service/reference/live-cadence.js.map +1 -0
- package/dist-server/service/reference/live-feed-lease.d.ts +15 -0
- package/dist-server/service/reference/live-feed-lease.js +56 -0
- package/dist-server/service/reference/live-feed-lease.js.map +1 -0
- package/dist-server/service/reference/live-ingest.d.ts +21 -0
- package/dist-server/service/reference/live-ingest.js +66 -0
- package/dist-server/service/reference/live-ingest.js.map +1 -0
- package/dist-server/service/reference/reference-adapter.d.ts +291 -2
- package/dist-server/service/reference/reference-adapter.js +19 -1
- package/dist-server/service/reference/reference-adapter.js.map +1 -1
- package/dist-server/service/reference/reference-fill-run.d.ts +50 -0
- package/dist-server/service/reference/reference-fill-run.js +142 -0
- package/dist-server/service/reference/reference-fill-run.js.map +1 -0
- package/dist-server/service/reference/reference-fill.d.ts +90 -0
- package/dist-server/service/reference/reference-fill.js +141 -0
- package/dist-server/service/reference/reference-fill.js.map +1 -0
- package/dist-server/service/reference/reference-hook.d.ts +22 -0
- package/dist-server/service/reference/reference-hook.js +135 -55
- package/dist-server/service/reference/reference-hook.js.map +1 -1
- package/dist-server/service/reference/reference-link-groups.d.ts +10 -0
- package/dist-server/service/reference/reference-link-groups.js +15 -0
- package/dist-server/service/reference/reference-link-groups.js.map +1 -0
- package/dist-server/service/reference/reference-links.d.ts +14 -0
- package/dist-server/service/reference/reference-links.js +73 -0
- package/dist-server/service/reference/reference-links.js.map +1 -0
- package/dist-server/service/reference/reference-live.d.ts +1 -0
- package/dist-server/service/reference/reference-live.js +32 -6
- package/dist-server/service/reference/reference-live.js.map +1 -1
- package/dist-server/service/reference/reference-master.d.ts +56 -2
- package/dist-server/service/reference/reference-master.js +33 -3
- package/dist-server/service/reference/reference-master.js.map +1 -1
- package/dist-server/service/reference/reference-resolver.d.ts +27 -0
- package/dist-server/service/reference/reference-resolver.js +79 -1
- package/dist-server/service/reference/reference-resolver.js.map +1 -1
- package/dist-server/service/twin-event/twin-event-keys.d.ts +31 -3
- package/dist-server/service/twin-event/twin-event-keys.js +36 -4
- package/dist-server/service/twin-event/twin-event-keys.js.map +1 -1
- package/dist-server/service/twin-event/twin-event.d.ts +1 -0
- package/dist-server/service/twin-event/twin-event.js +11 -2
- package/dist-server/service/twin-event/twin-event.js.map +1 -1
- package/dist-server/service/twin-model/choose-recipe.d.ts +15 -0
- package/dist-server/service/twin-model/choose-recipe.js +33 -0
- package/dist-server/service/twin-model/choose-recipe.js.map +1 -0
- package/dist-server/service/twin-model/external-resolver.d.ts +76 -0
- package/dist-server/service/twin-model/external-resolver.js +109 -0
- package/dist-server/service/twin-model/external-resolver.js.map +1 -0
- package/dist-server/service/twin-model/project-structure.js +16 -3
- package/dist-server/service/twin-model/project-structure.js.map +1 -1
- package/dist-server/service/twin-model/twin-equipment.d.ts +17 -0
- package/dist-server/service/twin-model/twin-equipment.js +10 -0
- package/dist-server/service/twin-model/twin-equipment.js.map +1 -1
- package/dist-server/service/twin-model/twin-lineage-query.js +75 -1
- package/dist-server/service/twin-model/twin-lineage-query.js.map +1 -1
- package/dist-server/service/twin-model/twin-model-item-query.js +94 -5
- package/dist-server/service/twin-model/twin-model-item-query.js.map +1 -1
- package/dist-server/service/twin-model/twin-model-tree-query.js.map +1 -1
- package/dist-server/service/twin-space/twin-space.d.ts +29 -0
- package/dist-server/service/twin-space/twin-space.js +13 -0
- package/dist-server/service/twin-space/twin-space.js.map +1 -1
- package/dist-server/service/twin-subject/event-subjects.d.ts +3 -1
- package/dist-server/service/twin-subject/event-subjects.js +8 -1
- package/dist-server/service/twin-subject/event-subjects.js.map +1 -1
- package/dist-shared/entity-delta.js +1 -1
- package/dist-shared/entity-delta.js.map +1 -1
- package/package.json +7 -7
- package/server/engine/canonical-ingest.ts +119 -33
- package/server/engine/event-time.ts +96 -0
- package/server/engine/index.ts +20 -0
- package/server/engine/ingest-dedupe.ts +203 -27
- package/server/engine/ingest-health.ts +91 -6
- package/server/engine/kpi-fold.ts +24 -1
- package/server/engine/kpi-query.ts +23 -4
- package/server/engine/oee-accumulator.ts +80 -8
- package/server/engine/twin-engine.ts +119 -5
- package/server/index.ts +23 -0
- package/server/migrations/1786600000000-RenameOperatoMesAdapterType.ts +66 -0
- package/server/migrations/1786700000000-AddDispatchRetryColumns.ts +82 -0
- package/server/migrations/index.ts +5 -1
- package/server/routes.ts +12 -24
- package/server/service/actuation/actuation-advisor.ts +124 -0
- package/server/service/actuation/actuation-outcome.ts +97 -0
- package/server/service/actuation/actuation-rule-resolver.ts +147 -0
- package/server/service/actuation/actuation-rule.ts +139 -0
- package/server/service/actuation/approval-gateway.ts +44 -0
- package/server/service/actuation/catch-up-effects.ts +152 -0
- package/server/service/actuation/command-dispatcher.ts +401 -20
- package/server/service/actuation/command-store.ts +182 -8
- package/server/service/actuation/dispatch-after-approval.ts +71 -0
- package/server/service/actuation/effect-match.ts +124 -0
- package/server/service/actuation/index.ts +21 -2
- package/server/service/actuation/retry-plan.ts +145 -0
- package/server/service/actuation/rule-evaluate.ts +352 -0
- package/server/service/actuation/rule-loop.ts +160 -0
- package/server/service/actuation/rule-runner.ts +216 -0
- package/server/service/actuation/rule-wiring.ts +84 -0
- package/server/service/actuation/settle-effects.ts +115 -0
- package/server/service/actuation/sweep-dispatch.ts +179 -0
- package/server/service/actuation/twin-command-resolver.ts +387 -0
- package/server/service/actuation/twin-command.ts +128 -1
- package/server/service/actuation/twin-now.ts +97 -0
- package/server/service/index.ts +9 -1
- package/server/service/reference/actuation-routing.ts +165 -0
- package/server/service/reference/actuation-target.ts +163 -0
- package/server/service/reference/connection-portability-resolver.ts +171 -0
- package/server/service/reference/connection-portability.ts +285 -0
- package/server/service/reference/fill-cadence.ts +29 -0
- package/server/service/reference/fill-dedupe.ts +66 -0
- package/server/service/reference/fill-loop.ts +153 -0
- package/server/service/reference/fill-span.ts +31 -0
- package/server/service/reference/hook-contract.ts +185 -5
- package/server/service/reference/hook-rejected.ts +53 -0
- package/server/service/reference/index.ts +5 -1
- package/server/service/reference/live-cadence.ts +129 -0
- package/server/service/reference/live-feed-lease.ts +56 -0
- package/server/service/reference/live-ingest.ts +94 -0
- package/server/service/reference/reference-adapter.ts +327 -5
- package/server/service/reference/reference-fill-run.ts +165 -0
- package/server/service/reference/reference-fill.ts +223 -0
- package/server/service/reference/reference-hook.ts +162 -56
- package/server/service/reference/reference-link-groups.ts +21 -0
- package/server/service/reference/reference-links.ts +77 -0
- package/server/service/reference/reference-live.ts +30 -5
- package/server/service/reference/reference-master.ts +90 -5
- package/server/service/reference/reference-resolver.ts +77 -2
- package/server/service/twin-event/twin-event-keys.ts +37 -4
- package/server/service/twin-event/twin-event.ts +18 -1
- package/server/service/twin-model/choose-recipe.ts +31 -0
- package/server/service/twin-model/external-resolver.ts +161 -0
- package/server/service/twin-model/project-structure.ts +16 -3
- package/server/service/twin-model/twin-equipment.ts +22 -0
- package/server/service/twin-model/twin-lineage-query.ts +82 -1
- package/server/service/twin-model/twin-model-item-query.ts +95 -4
- package/server/service/twin-model/twin-model-tree-query.ts +1 -1
- package/server/service/twin-space/twin-space.ts +39 -0
- package/server/service/twin-subject/event-subjects.ts +8 -1
- package/shared/entity-delta.ts +1 -1
- package/test/ack-shape-alignment.test.ts +97 -0
- package/test/actuation-approval-door.test.ts +421 -0
- package/test/actuation-dispatch.test.ts +16 -3
- package/test/actuation-effect.test.ts +162 -0
- package/test/actuation-outcome.test.ts +152 -0
- package/test/actuation-rule-runner.test.ts +281 -0
- package/test/actuation-rule.test.ts +266 -0
- package/test/actuation-seam.test.ts +249 -0
- package/test/actuation-settle.test.ts +164 -0
- package/test/actuation-target.test.ts +145 -0
- package/test/canonical-ingest-vocabularies.test.ts +49 -2
- package/test/capability-mapping.test.ts +1 -1
- package/test/choose-recipe.test.ts +117 -0
- package/test/command-store-writes-columns.test.ts +126 -0
- package/test/connection-portability-doors.test.ts +87 -0
- package/test/connector-capability-declaration.test.ts +54 -4
- package/test/declaration-reaches-model.test.ts +43 -0
- package/test/dedupe-transformation.test.ts +112 -0
- package/test/entity-delta.test.ts +1 -1
- package/test/event-subjects.test.ts +2 -1
- package/test/external-resolver.test.ts +171 -0
- package/test/fill-dedupe.test.ts +59 -0
- package/test/fill-loop.test.ts +35 -0
- package/test/first-envelope.test.ts +153 -0
- package/test/fold-shift-dst.test.ts +110 -0
- package/test/home-in-area.test.ts +90 -0
- package/test/hook-rejected-shape.test.ts +86 -0
- package/test/hook-response-sum.test.ts +193 -0
- package/test/hook-sequence.test.ts +1 -1
- package/test/ingest-dedupe.test.ts +56 -0
- package/test/ingest-expected-quiet.test.ts +111 -0
- package/test/ingest-idempotent.test.ts +79 -0
- package/test/ingest-reconcile-four-ways.test.ts +97 -0
- package/test/ingest-rules-header.test.ts +100 -0
- package/test/ingest-window-rolls-unobserved.test.ts +72 -0
- package/test/intake-mapping-parity.test.ts +175 -0
- package/test/live-cadence.test.ts +170 -0
- package/test/live-feed-lease.test.ts +63 -0
- package/test/live-mirror-parity.test.ts +1 -1
- package/test/oee-accumulator.test.ts +3 -3
- package/test/oee-site-calendar.test.ts +146 -0
- package/test/order-and-transaction-split.test.ts +107 -0
- package/test/reference-fill.test.ts +235 -0
- package/test/reference-hook.test.ts +1 -1
- package/test/reference-links.test.ts +56 -0
- package/test/retry-plan.test.ts +139 -0
- package/test/sweep-dispatch.test.ts +191 -0
- package/test/twin-event-keys.test.ts +20 -6
- package/test/twin-model-item-db.test.ts +6 -6
- package/test/twin-model-tree-db.test.ts +6 -6
- package/tsconfig.shared.tsbuildinfo +1 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -55,6 +55,11 @@ tslib_1.__decorate([
|
|
|
55
55
|
(0, type_graphql_1.Field)({ description: 'Who proposed it: operator, rule, ai or schedule. A person weighs this when approving.' }),
|
|
56
56
|
tslib_1.__metadata("design:type", String)
|
|
57
57
|
], TwinCommandRecord.prototype, "origin", void 0);
|
|
58
|
+
tslib_1.__decorate([
|
|
59
|
+
(0, typeorm_1.Column)({ nullable: true }),
|
|
60
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'The person who proposed it. Absent when a rule or model proposed it.' }),
|
|
61
|
+
tslib_1.__metadata("design:type", String)
|
|
62
|
+
], TwinCommandRecord.prototype, "proposedBy", void 0);
|
|
58
63
|
tslib_1.__decorate([
|
|
59
64
|
(0, typeorm_1.Column)({ nullable: true }),
|
|
60
65
|
(0, type_graphql_1.Field)({ nullable: true, description: 'Whether the effect can be undone. Absent means it cannot, and a person must approve.' }),
|
|
@@ -65,6 +70,21 @@ tslib_1.__decorate([
|
|
|
65
70
|
(0, type_graphql_1.Field)({ description: 'proposed · approved · rejected · dispatched · acked · failed.' }),
|
|
66
71
|
tslib_1.__metadata("design:type", String)
|
|
67
72
|
], TwinCommandRecord.prototype, "state", void 0);
|
|
73
|
+
tslib_1.__decorate([
|
|
74
|
+
(0, typeorm_1.Column)({ nullable: true }),
|
|
75
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'The observation that caused a rule to propose this. Empty when a person proposed it.' }),
|
|
76
|
+
tslib_1.__metadata("design:type", String)
|
|
77
|
+
], TwinCommandRecord.prototype, "causeRef", void 0);
|
|
78
|
+
tslib_1.__decorate([
|
|
79
|
+
(0, typeorm_1.Column)({ nullable: true }),
|
|
80
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'The rule that proposed this, when a rule did.' }),
|
|
81
|
+
tslib_1.__metadata("design:type", String)
|
|
82
|
+
], TwinCommandRecord.prototype, "ruleRef", void 0);
|
|
83
|
+
tslib_1.__decorate([
|
|
84
|
+
(0, typeorm_1.Column)({ nullable: true }),
|
|
85
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'Approval record this command waits on. While set, decisions come only from there.' }),
|
|
86
|
+
tslib_1.__metadata("design:type", String)
|
|
87
|
+
], TwinCommandRecord.prototype, "approvalRef", void 0);
|
|
68
88
|
tslib_1.__decorate([
|
|
69
89
|
(0, typeorm_1.Column)({ nullable: true }),
|
|
70
90
|
(0, type_graphql_1.Field)({ nullable: true, description: 'Who approved or rejected it.' }),
|
|
@@ -85,6 +105,46 @@ tslib_1.__decorate([
|
|
|
85
105
|
(0, type_graphql_1.Field)({ nullable: true, description: 'Reference the adapter returned. Without it a reversal cannot name what to undo.' }),
|
|
86
106
|
tslib_1.__metadata("design:type", String)
|
|
87
107
|
], TwinCommandRecord.prototype, "dispatchRef", void 0);
|
|
108
|
+
tslib_1.__decorate([
|
|
109
|
+
(0, typeorm_1.Column)({ nullable: true }),
|
|
110
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'What the source added while accepting — not a failure, not a completion. Kept verbatim.' }),
|
|
111
|
+
tslib_1.__metadata("design:type", String)
|
|
112
|
+
], TwinCommandRecord.prototype, "dispatchNote", void 0);
|
|
113
|
+
tslib_1.__decorate([
|
|
114
|
+
(0, typeorm_1.Column)({ nullable: true }),
|
|
115
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'Status the source reported for this instruction, kept verbatim. The kernel does not interpret it.' }),
|
|
116
|
+
tslib_1.__metadata("design:type", String)
|
|
117
|
+
], TwinCommandRecord.prototype, "effectStatus", void 0);
|
|
118
|
+
tslib_1.__decorate([
|
|
119
|
+
(0, typeorm_1.Column)({ nullable: true }),
|
|
120
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'When that report was observed. Empty means no report has been correlated yet.' }),
|
|
121
|
+
tslib_1.__metadata("design:type", String)
|
|
122
|
+
], TwinCommandRecord.prototype, "effectAt", void 0);
|
|
123
|
+
tslib_1.__decorate([
|
|
124
|
+
(0, typeorm_1.Column)({ type: 'int', nullable: true }),
|
|
125
|
+
(0, type_graphql_1.Field)(type => type_graphql_1.Int, { nullable: true, description: 'How many reports have been correlated to this instruction.' }),
|
|
126
|
+
tslib_1.__metadata("design:type", Number)
|
|
127
|
+
], TwinCommandRecord.prototype, "effectSeen", void 0);
|
|
128
|
+
tslib_1.__decorate([
|
|
129
|
+
(0, typeorm_1.Column)({ nullable: true }),
|
|
130
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'The next action the source expects, as one line. The cause is dispatchNote.' }),
|
|
131
|
+
tslib_1.__metadata("design:type", String)
|
|
132
|
+
], TwinCommandRecord.prototype, "dispatchNoteNext", void 0);
|
|
133
|
+
tslib_1.__decorate([
|
|
134
|
+
(0, typeorm_1.Column)({ nullable: true }),
|
|
135
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'Whether retrying can work: again · after-fix · never. Empty means the adapter did not say.' }),
|
|
136
|
+
tslib_1.__metadata("design:type", String)
|
|
137
|
+
], TwinCommandRecord.prototype, "retry", void 0);
|
|
138
|
+
tslib_1.__decorate([
|
|
139
|
+
(0, typeorm_1.Column)({ type: 'int', nullable: true }),
|
|
140
|
+
(0, type_graphql_1.Field)(type => type_graphql_1.Int, { nullable: true, description: 'How many dispatch attempts have been made. The first attempt is 1.' }),
|
|
141
|
+
tslib_1.__metadata("design:type", Number)
|
|
142
|
+
], TwinCommandRecord.prototype, "attempts", void 0);
|
|
143
|
+
tslib_1.__decorate([
|
|
144
|
+
(0, typeorm_1.Column)({ nullable: true }),
|
|
145
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'When to try dispatching again (ISO 8601). Empty means the next cycle.' }),
|
|
146
|
+
tslib_1.__metadata("design:type", String)
|
|
147
|
+
], TwinCommandRecord.prototype, "nextAttemptAt", void 0);
|
|
88
148
|
tslib_1.__decorate([
|
|
89
149
|
(0, typeorm_1.Column)({ nullable: true }),
|
|
90
150
|
(0, type_graphql_1.Field)({ nullable: true, description: 'Why the dispatch failed. The server sentence, kept as it came.' }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"twin-command.js","sourceRoot":"","sources":["../../../server/service/actuation/twin-command.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * **현장에 내리는 조치 하나** — 제안 · 승인 · 넘김 · 응답이 한 행에 남는다.\n *\n * ── 왜 표인가 (2026-08-31) ───────────────────────────────────────────────\n * 승인이 프로세스 안에만 있으면 트윈이 재기동한 순간 승인 대기 줄이 사라진다. 그때 사람은 **자기가\n * 승인한 것이 실행됐는지 알 수 없다.** 조치는 되돌리기 어려운 것이므로 그 모름이 그대로 사고가 된다.\n *\n * 그리고 이 행은 **누가 언제 승인했나**의 기록이기도 하다. 사고 뒤에 그것을 물을 자리가 여기다.\n *\n * ── 판정은 여기 없다 ─────────────────────────────────────────────────────\n * 어느 상태에서 어느 사건이 허용되는지는 계약이 든다(`@operato/ops-contract` §`nextCommandState`).\n * 이 파일은 그 값을 담기만 한다 — 판정이 두 벌이 되면 화면과 서버가 다른 답을 낸다.\n */\nimport { Column, CreateDateColumn, Entity, Index, ManyToOne, PrimaryGeneratedColumn, RelationId, UpdateDateColumn } from 'typeorm'\nimport { Field, ID, ObjectType } from 'type-graphql'\n\nimport { Domain, ScalarObject } from '@things-factory/shell'\n\n@Entity()\n/* 대기 줄을 여는 질의가 이 세 칸으로 묻는다 — 어느 테넌트의 · 어느 트윈의 · 어느 상태인가. */\n@Index('ix_twin_command_0', (e: TwinCommandRecord) => [e.domain, e.instanceId, e.state])\n@ObjectType({ description: 'One actuation command proposed against a twin, with its approval trail.' })\nexport class TwinCommandRecord {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { description: 'Unique identifier of the command.' })\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { nullable: true, description: 'Tenant this command belongs to.' })\n domain?: Domain\n\n @RelationId((e: TwinCommandRecord) => e.domain)\n domainId?: string\n\n @Column()\n @Field({ description: 'Twin instance this command acts on.' })\n instanceId: string\n\n @Column()\n @Field({ description: 'Command type in twin vocabulary. Translating it into a legacy call is the adapter job.' })\n type: string\n\n @Column({ type: 'simple-json', nullable: true })\n @Field(type => ScalarObject, { nullable: true, description: 'Command payload in twin vocabulary.' })\n payload?: any\n\n @Column()\n @Field({ description: 'Who proposed it: operator, rule, ai or schedule. A person weighs this when approving.' })\n origin: string\n\n /**\n * **되돌릴 수 있는가** — 게이트가 걸리는 기준이다.\n *\n * 비어 있으면 「되돌릴 수 없다」로 읽는다(§`requiresApproval`). 모름을 되돌릴 수 있다로 읽으면 그\n * 커맨드가 승인 없이 나간다.\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Whether the effect can be undone. Absent means it cannot, and a person must approve.' })\n reversible?: boolean\n\n @Column()\n @Field({ description: 'proposed · approved · rejected · dispatched · acked · failed.' })\n state: string\n\n /* ── 승인의 기록 ─────────────────────────────────────────────────────────\n * 한 칸에 JSON 으로 담지 않고 세 칸으로 나눈다 — 「누가 승인했나」로 묻는 질의가 실제로 온다.\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Who approved or rejected it.' })\n decidedBy?: string\n\n /*\n * ISO 문자열로 든다 — `datetime` 은 드라이버 방언이라 다섯 드라이버를 그대로 지나지 못하고,\n * sqlite 는 그것을 검증하지 않아 개발에서 잡히지 않는다. 이 저장소의 다른 시각 칸도 그 모양이다\n * (§`TwinReference.lastSyncedAt`). 계약의 `CommandApproval.at` 도 ISO 문자열이라 옮길 것이 없다.\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'When it was approved or rejected (ISO 8601).' })\n decidedAt?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Why — free text the approver wrote.' })\n decisionNote?: string\n\n /**\n * 어댑터가 돌려준 것 — 저쪽에서 무엇이 되었는지 되짚을 수 있어야 한다.\n *\n * 이것이 없으면 되돌릴 때 **무엇을 되돌릴지 말할 수 없다.**\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Reference the adapter returned. Without it a reversal cannot name what to undo.' })\n dispatchRef?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Why the dispatch failed. The server sentence, kept as it came.' })\n error?: string\n\n @CreateDateColumn()\n @Field({ nullable: true, description: 'When the command was proposed.' })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true, description: 'When the row last changed.' })\n updatedAt?: Date\n}\n"]}
|
|
1
|
+
{"version":3,"file":"twin-command.js","sourceRoot":"","sources":["../../../server/service/actuation/twin-command.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * **현장에 내리는 조치 하나** — 제안 · 승인 · 넘김 · 응답이 한 행에 남는다.\n *\n * ── 왜 표인가 (2026-08-31) ───────────────────────────────────────────────\n * 승인이 프로세스 안에만 있으면 트윈이 재기동한 순간 승인 대기 줄이 사라진다. 그때 사람은 **자기가\n * 승인한 것이 실행됐는지 알 수 없다.** 조치는 되돌리기 어려운 것이므로 그 모름이 그대로 사고가 된다.\n *\n * 그리고 이 행은 **누가 언제 승인했나**의 기록이기도 하다. 사고 뒤에 그것을 물을 자리가 여기다.\n *\n * ── 판정은 여기 없다 ─────────────────────────────────────────────────────\n * 어느 상태에서 어느 사건이 허용되는지는 계약이 든다(`@operato/ops-contract` §`nextCommandState`).\n * 이 파일은 그 값을 담기만 한다 — 판정이 두 벌이 되면 화면과 서버가 다른 답을 낸다.\n */\nimport { Column, CreateDateColumn, Entity, Index, ManyToOne, PrimaryGeneratedColumn, RelationId, UpdateDateColumn } from 'typeorm'\nimport { Field, ID, Int, ObjectType } from 'type-graphql'\n\nimport { Domain, ScalarObject } from '@things-factory/shell'\n\n@Entity()\n/* 대기 줄을 여는 질의가 이 세 칸으로 묻는다 — 어느 테넌트의 · 어느 트윈의 · 어느 상태인가. */\n@Index('ix_twin_command_0', (e: TwinCommandRecord) => [e.domain, e.instanceId, e.state])\n@ObjectType({ description: 'One actuation command proposed against a twin, with its approval trail.' })\nexport class TwinCommandRecord {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { description: 'Unique identifier of the command.' })\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { nullable: true, description: 'Tenant this command belongs to.' })\n domain?: Domain\n\n @RelationId((e: TwinCommandRecord) => e.domain)\n domainId?: string\n\n @Column()\n @Field({ description: 'Twin instance this command acts on.' })\n instanceId: string\n\n @Column()\n @Field({ description: 'Command type in twin vocabulary. Translating it into a legacy call is the adapter job.' })\n type: string\n\n @Column({ type: 'simple-json', nullable: true })\n @Field(type => ScalarObject, { nullable: true, description: 'Command payload in twin vocabulary.' })\n payload?: any\n\n @Column()\n @Field({ description: 'Who proposed it: operator, rule, ai or schedule. A person weighs this when approving.' })\n origin: string\n\n /**\n * **낸 사람** — 종류가 아니라 사람이다.\n *\n * `origin` 은 「사람이 냈나 · 규칙이 냈나」만 말한다. 그것만으로는 **낸 사람이 자기 것을 승인하는 것**을\n * 막을 수 없고, 그러면 게이트가 시늉이 된다. 되돌릴 수 없는 조치에 두 사람이 필요하다는 것이 이 표의\n * 존재 이유다.\n *\n * 규칙·AI 가 낸 것은 비어 있다 — 사람이 아니다.\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'The person who proposed it. Absent when a rule or model proposed it.' })\n proposedBy?: string\n\n /**\n * **되돌릴 수 있는가** — 게이트가 걸리는 기준이다.\n *\n * 비어 있으면 「되돌릴 수 없다」로 읽는다(§`requiresApproval`). 모름을 되돌릴 수 있다로 읽으면 그\n * 커맨드가 승인 없이 나간다.\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Whether the effect can be undone. Absent means it cannot, and a person must approve.' })\n reversible?: boolean\n\n @Column()\n @Field({ description: 'proposed · approved · rejected · dispatched · acked · failed.' })\n state: string\n\n /**\n * **무엇 때문에 냈나** — 규칙이 낸 조치가 가리키는 관측의 식별자(`Attention.id`).\n *\n * 두 가지를 한다.\n *\n * 같은 조건을 두 번 올리지 않는다 결정을 기다리는 커맨드의 이 값을 모아 판정에 넘긴다\n * 승인하는 사람이 근거를 본다 「무슨 일이 있어서 이것이 올라왔나」\n *\n * 앞엣것이 이 칸의 존재 이유다. 프로세스 기억으로 중복을 막으면 **재기동이 곧 중복**이 된다 — 다시 선\n * 트윈이 같은 조건을 처음 보는 것으로 읽고 대기 줄에 같은 지시를 또 올린다.\n *\n * 사람이 낸 것은 비어 있다. 사람은 자기가 왜 내는지 알고 있고, 그 이유를 값으로 요구하면 아무 글자나 적힌다.\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'The observation that caused a rule to propose this. Empty when a person proposed it.' })\n causeRef?: string\n\n /** 어느 규칙이 냈나 — 규칙을 끄거나 고칠 때 그 규칙이 무엇을 냈는지 되짚을 자리. */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'The rule that proposed this, when a rule did.' })\n ruleRef?: string\n\n /**\n * **결재선에 올라간 자리** — 업무 목록(`@things-factory/worklist`)의 액티비티 인스턴스 식별자.\n *\n * 이 값이 있으면 승인·거절은 **그 목록에서만** 한다. 여기 있는 직접 결정 뮤테이션은 막힌다 — 문이 두\n * 벌이면 결재선에는 대기로 남아 있는데 조치는 이미 나간 상태가 만들어진다.\n *\n * 비어 있으면 결재선을 쓰지 않는 배포다. 그때는 직접 결정이 유일한 문이다.\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Approval record this command waits on. While set, decisions come only from there.' })\n approvalRef?: string\n\n /* ── 승인의 기록 ─────────────────────────────────────────────────────────\n * 한 칸에 JSON 으로 담지 않고 세 칸으로 나눈다 — 「누가 승인했나」로 묻는 질의가 실제로 온다.\n *\n * 결재선을 지난 경우에도 여기에 적는다. 판정의 온전한 기록(위임·상신·회차)은 결재선이 들고, 이 세 칸은\n * **이 커맨드에 대한 마지막 판정**이다 — 커맨드 목록이 결재선을 조회하지 않고도 「누가 언제」를 보여야 한다.\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Who approved or rejected it.' })\n decidedBy?: string\n\n /*\n * ISO 문자열로 든다 — `datetime` 은 드라이버 방언이라 다섯 드라이버를 그대로 지나지 못하고,\n * sqlite 는 그것을 검증하지 않아 개발에서 잡히지 않는다. 이 저장소의 다른 시각 칸도 그 모양이다\n * (§`TwinReference.lastSyncedAt`). 계약의 `CommandApproval.at` 도 ISO 문자열이라 옮길 것이 없다.\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'When it was approved or rejected (ISO 8601).' })\n decidedAt?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Why — free text the approver wrote.' })\n decisionNote?: string\n\n /**\n * 어댑터가 돌려준 것 — 저쪽에서 무엇이 되었는지 되짚을 수 있어야 한다.\n *\n * 이것이 없으면 되돌릴 때 **무엇을 되돌릴지 말할 수 없다.**\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Reference the adapter returned. Without it a reversal cannot name what to undo.' })\n dispatchRef?: string\n\n /*\n * ── 저쪽에서 실제로 무엇이 되었나 ─────────────────────────────────────────\n * 상태(`state`)와 다른 축이다. `acked` 는 「저쪽이 답했다」로 끝나고, 이 셋은 그 뒤에 관측으로\n * 돌아온 사실이다. 되돌아오는 관측이 없는 커넥터의 조치는 이 셋이 영원히 빈다 — 그것이 정상이고,\n * 빈 것은 「효과가 없었다」가 아니라 **「모른다」**다.\n */\n\n /**\n * 받아들였으나 저쪽이 덧붙인 말 — **실패가 아니고 「끝났다」도 아니다.**\n *\n * MES 가 지시를 접수해 놓고 지시서를 못 붙이면 그 사실이 여기 온다. `error` 와 다른 칸이어야 한다 —\n * `error` 에 적으면 실패로 읽혀 커맨드가 `failed` 로 앉는다.\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'What the source added while accepting — not a failure, not a completion. Kept verbatim.' })\n dispatchNote?: string\n\n /** 저쪽이 보고한 상태 — **해석하지 않고 그대로.** 어느 값이 「나갔다」인지는 현장이 안다. */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Status the source reported for this instruction, kept verbatim. The kernel does not interpret it.' })\n effectStatus?: string\n\n /** 그 보고를 관측한 시각. 컬럼 타입을 적지 않는다 — 드라이버 방언이라 개발에서 안 잡힌다. */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'When that report was observed. Empty means no report has been correlated yet.' })\n effectAt?: string\n\n /** 몇 번 보고됐나 — 한 번도 안 바뀐 것과 다섯 번 바뀐 것은 다른 일이다. */\n @Column({ type: 'int', nullable: true })\n @Field(type => Int, { nullable: true, description: 'How many reports have been correlated to this instruction.' })\n effectSeen?: number\n\n /**\n * 저쪽이 받으면서 덧붙인 말 중 **다음에 할 일** 한 줄 — 원인은 `dispatchNote` 다.\n *\n * 왜 둘로 나누나: 읽는 사람은 「그래서 내가 뭘 해야 하나」를 먼저 찾는다(UX 레인). 한 문장으로\n * 주면 화면이 자르게 되고, 자르는 규칙이 화면마다 생긴다. 어댑터가 이미 둘로 알고 있으니\n * 붙이지 않고 그대로 받는다.\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'The next action the source expects, as one line. The cause is dispatchNote.' })\n dispatchNoteNext?: string\n\n /**\n * **다시 해서 될 일인가** — 어댑터가 실패마다 답한다(§`CommandAck.retry`).\n *\n * again 그대로 다시 해 볼 만하다 — 못 닿았거나 저쪽이 잠깐 흔들렸다\n * after-fix 사람이 고친 뒤 그대로 나간다 — 설정 문제\n * never 이 조치로는 영원히 안 된다 — 지시 내용이 틀렸다\n *\n * 가르는 자리는 「조치를 다시 낼 필요가 있나」다. 설정이 틀린 것은 조치가 멀쩡하므로 `after-fix`,\n * 지시 내용이 틀린 것은 그 조치가 영원히 틀렸으므로 `never` 다(인티그레이션 레인 판단).\n *\n * **비어 있으면 `never` 로 읽지 않는다.** 모르는 것과 영원히 안 되는 것은 다르다 — 선언하지 않은\n * 커넥터의 실패를 `never` 로 접으면 고칠 수 있는 것을 사람이 포기한다.\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Whether retrying can work: again · after-fix · never. Empty means the adapter did not say.' })\n retry?: string\n\n /** 몇 번 넘겨 봤나 — 목록의 「몇 번 해 봤나」가 이 값이다. 첫 시도가 1이다. */\n @Column({ type: 'int', nullable: true })\n @Field(type => Int, { nullable: true, description: 'How many dispatch attempts have been made. The first attempt is 1.' })\n attempts?: number\n\n /**\n * **다음에 넘겨 볼 시각**(ISO) — 물러섬을 표로 든다.\n *\n * 메모리에 두지 않는 이유: 재기동하면 사라지고, 그러면 물러서던 것이 전부 즉시 재시도가 된다.\n * 표에 두면 일꾼이 여러 대여도 같은 답을 본다.\n *\n * 비어 있으면 **다음 주기에 바로** 본다(한 번도 실패하지 않은 것).\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'When to try dispatching again (ISO 8601). Empty means the next cycle.' })\n nextAttemptAt?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Why the dispatch failed. The server sentence, kept as it came.' })\n error?: string\n\n @CreateDateColumn()\n @Field({ nullable: true, description: 'When the command was proposed.' })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true, description: 'When the row last changed.' })\n updatedAt?: Date\n}\n"]}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** 승인 화면이 읽는 한 줄 — 커널 낱말 그대로. 사람의 말은 표현계층이 만든다. */
|
|
2
|
+
export interface TwinNowAttention {
|
|
3
|
+
id: string;
|
|
4
|
+
kind: string;
|
|
5
|
+
severity: string;
|
|
6
|
+
anchor?: Record<string, string | undefined>;
|
|
7
|
+
params?: Record<string, string | number>;
|
|
8
|
+
timeStamp?: string;
|
|
9
|
+
/** 커널이 권하는 방향 — 실행 가능한 것과 권고가 섞여 있다(`command` 가 있으면 자극이다). */
|
|
10
|
+
recommendedActions?: {
|
|
11
|
+
code: string;
|
|
12
|
+
command?: string;
|
|
13
|
+
args?: unknown;
|
|
14
|
+
}[];
|
|
15
|
+
/** 이 조치가 이것 때문에 올라왔나 — 규칙이 낸 것만 표시된다. */
|
|
16
|
+
isCause?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface TwinNow {
|
|
19
|
+
/** 심각한 순서로 몇 개. 전부가 아니다. */
|
|
20
|
+
attentions: TwinNowAttention[];
|
|
21
|
+
/** 지금 서 있는 것의 전체 수 — **잘랐으면 몇을 잘랐는지 알 수 있게.** */
|
|
22
|
+
total: number;
|
|
23
|
+
/** 이 조치를 낸 근거가 아직 서 있나 — 규칙이 낸 것에만 값이 있다. */
|
|
24
|
+
causeStanding?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* 트윈의 상태를 못 읽었으면 그 이유.
|
|
27
|
+
*
|
|
28
|
+
* 빈 목록으로 답하지 않는다 — 「지금 아무 문제 없다」와 「트윈이 멈춰 있다」는 다른 사실이고,
|
|
29
|
+
* 승인하는 사람에게 그 차이가 크다.
|
|
30
|
+
*/
|
|
31
|
+
unavailable?: string;
|
|
32
|
+
}
|
|
33
|
+
/** 승인 화면에 몇 개까지 싣나 — 사람이 읽는 수. 스물여섯 줄은 아무도 안 읽는다. */
|
|
34
|
+
export declare const TWIN_NOW_LIMIT = 5;
|
|
35
|
+
/**
|
|
36
|
+
* 이 트윈이 지금 무엇을 보고 있나 — 결재선 입력에 실어 보낼 모양으로.
|
|
37
|
+
*
|
|
38
|
+
* `causeRef` 를 주면 그것이 아직 서 있는지도 함께 답한다. 승인하는 사람이 물어야 하는 것 중 가장 중요한
|
|
39
|
+
* 것이 「지금도 그런가」이고, 조건이 사라진 뒤 승인하면 필요 없는 것을 만든다.
|
|
40
|
+
*/
|
|
41
|
+
export declare function twinNow(domainId: string, instanceId: string, causeRef?: string, limit?: number): TwinNow;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TWIN_NOW_LIMIT = void 0;
|
|
4
|
+
exports.twinNow = twinNow;
|
|
5
|
+
/*
|
|
6
|
+
* **승인하는 사람에게 트윈이 지금 보고 있는 것을 함께 준다.**
|
|
7
|
+
*
|
|
8
|
+
* ── 왜 필요한가 (2026-09-01, 사용자 지적) ────────────────────────────────
|
|
9
|
+
* 사용자가 물었다 — 「보통 AI 는 분명하지 않으면 되묻고, 잘못된 명령이면 확인이나 역제안을 하는데
|
|
10
|
+
* 왜 이 기능에는 그것이 없나.」
|
|
11
|
+
*
|
|
12
|
+
* 확인해 보니 대화하는 AI 는 되묻는다(`twin-ai` §assistant 프롬프트). 그런데 **현장에 지시를 내는
|
|
13
|
+
* 경로는 아무것도 묻지 않는다.** 사람이 「볼트 30개 추가」를 올리면 그대로 결재선에 올라간다. 트윈이
|
|
14
|
+
* 도금 대기가 여섯 배인 것을 알고 있어도 그 말을 할 자리가 없다.
|
|
15
|
+
*
|
|
16
|
+
* 말하는 쪽은 안전한 경로이고, 말 못 하는 쪽이 공장을 움직인다.
|
|
17
|
+
*
|
|
18
|
+
* ── 판정하지 않는다 ──────────────────────────────────────────────────────
|
|
19
|
+
* 트윈이 「이 지시는 틀렸다」고 말하지 않는다. 무엇이 무엇과 어긋나는지 판정하려면 그 규칙을 선언해야
|
|
20
|
+
* 하고, 선언 없이 판정하면 지어낸 규칙이 사람의 지시를 막는다.
|
|
21
|
+
*
|
|
22
|
+
* **트윈은 지금 보이는 것을 내놓고, 승인하는 사람이 판단한다.**
|
|
23
|
+
*
|
|
24
|
+
* ── 이름을 정확히 쓴다 ───────────────────────────────────────────────────
|
|
25
|
+
* 이 값의 이름은 `twinNow` 다. `related` 나 `relevant` 가 아니다 — **이 지시와 관련 있다고 판정한 것이
|
|
26
|
+
* 아니고**, 이 트윈에서 지금 서 있는 것 전부 중 심각한 순서로 몇 개다. 이름이 관련성을 주장하면 승인자가
|
|
27
|
+
* 「트윈이 이 지시 때문에 이것을 골랐다」로 읽는다.
|
|
28
|
+
*/
|
|
29
|
+
const attention_digest_js_1 = require("../../engine/attention-digest.js");
|
|
30
|
+
const twin_engine_js_1 = require("../../engine/twin-engine.js");
|
|
31
|
+
/** 승인 화면에 몇 개까지 싣나 — 사람이 읽는 수. 스물여섯 줄은 아무도 안 읽는다. */
|
|
32
|
+
exports.TWIN_NOW_LIMIT = 5;
|
|
33
|
+
/**
|
|
34
|
+
* 이 트윈이 지금 무엇을 보고 있나 — 결재선 입력에 실어 보낼 모양으로.
|
|
35
|
+
*
|
|
36
|
+
* `causeRef` 를 주면 그것이 아직 서 있는지도 함께 답한다. 승인하는 사람이 물어야 하는 것 중 가장 중요한
|
|
37
|
+
* 것이 「지금도 그런가」이고, 조건이 사라진 뒤 승인하면 필요 없는 것을 만든다.
|
|
38
|
+
*/
|
|
39
|
+
function twinNow(domainId, instanceId, causeRef, limit = exports.TWIN_NOW_LIMIT) {
|
|
40
|
+
if (!twin_engine_js_1.TwinEngine.owns(domainId, instanceId)) {
|
|
41
|
+
return { attentions: [], total: 0, unavailable: '이 서버가 이 트윈을 들고 있지 않다 — 멈춰 있거나 다른 서버가 돌고 있다' };
|
|
42
|
+
}
|
|
43
|
+
const kernel = twin_engine_js_1.TwinEngine.kernel(domainId, instanceId);
|
|
44
|
+
const snap = kernel?.getSnapshot?.() ?? twin_engine_js_1.TwinEngine.snapshot(domainId, instanceId);
|
|
45
|
+
if (!snap) {
|
|
46
|
+
return { attentions: [], total: 0, unavailable: '이 트윈의 상태를 읽을 수 없다 — 아직 서지 않았거나 멈췄다' };
|
|
47
|
+
}
|
|
48
|
+
const all = (snap.attentions ?? []);
|
|
49
|
+
/* 자리 색은 쓰지 않는다 — 승인 화면은 지도가 아니다. 자른 뒤의 수는 반드시 함께 낸다. */
|
|
50
|
+
const digest = (0, attention_digest_js_1.digestAttentions)(all, limit);
|
|
51
|
+
const attentions = digest.attentions.map(a => ({
|
|
52
|
+
...a,
|
|
53
|
+
...(causeRef && a.id === causeRef ? { isCause: true } : {})
|
|
54
|
+
}));
|
|
55
|
+
return {
|
|
56
|
+
attentions,
|
|
57
|
+
total: digest.attentionTotal,
|
|
58
|
+
/*
|
|
59
|
+
* 근거가 서 있나 — **자른 목록이 아니라 전량에서 찾는다.** 상위 다섯에 안 들었다고 사라진 것이
|
|
60
|
+
* 아니다. 잘린 목록에서 찾으면 심각한 것이 여럿일 때 「사라졌다」로 답한다.
|
|
61
|
+
*/
|
|
62
|
+
...(causeRef ? { causeStanding: all.some(a => a.id === causeRef) } : {})
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=twin-now.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"twin-now.js","sourceRoot":"","sources":["../../../server/service/actuation/twin-now.ts"],"names":[],"mappings":";;;AAkEA,0BA8BC;AAhGD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,0EAAmE;AACnE,gEAAwD;AAgCxD,qDAAqD;AACxC,QAAA,cAAc,GAAG,CAAC,CAAA;AAE/B;;;;;GAKG;AACH,SAAgB,OAAO,CAAC,QAAgB,EAAE,UAAkB,EAAE,QAAiB,EAAE,KAAK,GAAG,sBAAc;IACrG,IAAI,CAAC,2BAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,EAAE,CAAC;QAC3C,OAAO,EAAE,UAAU,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,WAAW,EAAE,4CAA4C,EAAE,CAAA;IAChG,CAAC;IAED,MAAM,MAAM,GAAG,2BAAU,CAAC,MAAM,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAA;IACtD,MAAM,IAAI,GAAG,MAAM,EAAE,WAAW,EAAE,EAAE,IAAI,2BAAU,CAAC,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAA;IACjF,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,EAAE,UAAU,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,WAAW,EAAE,oCAAoC,EAAE,CAAA;IACxF,CAAC;IAED,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,CAAuB,CAAA;IAEzD,wDAAwD;IACxD,MAAM,MAAM,GAAG,IAAA,sCAAgB,EAAC,GAAU,EAAE,KAAK,CAAC,CAAA;IAElD,MAAM,UAAU,GAAI,MAAM,CAAC,UAAiC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACrE,GAAG,CAAC;QACJ,GAAG,CAAC,QAAQ,IAAI,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC5D,CAAC,CAAC,CAAA;IAEH,OAAO;QACL,UAAU;QACV,KAAK,EAAE,MAAM,CAAC,cAAc;QAC5B;;;WAGG;QACH,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACzE,CAAA;AACH,CAAC","sourcesContent":["/*\n * **승인하는 사람에게 트윈이 지금 보고 있는 것을 함께 준다.**\n *\n * ── 왜 필요한가 (2026-09-01, 사용자 지적) ────────────────────────────────\n * 사용자가 물었다 — 「보통 AI 는 분명하지 않으면 되묻고, 잘못된 명령이면 확인이나 역제안을 하는데\n * 왜 이 기능에는 그것이 없나.」\n *\n * 확인해 보니 대화하는 AI 는 되묻는다(`twin-ai` §assistant 프롬프트). 그런데 **현장에 지시를 내는\n * 경로는 아무것도 묻지 않는다.** 사람이 「볼트 30개 추가」를 올리면 그대로 결재선에 올라간다. 트윈이\n * 도금 대기가 여섯 배인 것을 알고 있어도 그 말을 할 자리가 없다.\n *\n * 말하는 쪽은 안전한 경로이고, 말 못 하는 쪽이 공장을 움직인다.\n *\n * ── 판정하지 않는다 ──────────────────────────────────────────────────────\n * 트윈이 「이 지시는 틀렸다」고 말하지 않는다. 무엇이 무엇과 어긋나는지 판정하려면 그 규칙을 선언해야\n * 하고, 선언 없이 판정하면 지어낸 규칙이 사람의 지시를 막는다.\n *\n * **트윈은 지금 보이는 것을 내놓고, 승인하는 사람이 판단한다.**\n *\n * ── 이름을 정확히 쓴다 ───────────────────────────────────────────────────\n * 이 값의 이름은 `twinNow` 다. `related` 나 `relevant` 가 아니다 — **이 지시와 관련 있다고 판정한 것이\n * 아니고**, 이 트윈에서 지금 서 있는 것 전부 중 심각한 순서로 몇 개다. 이름이 관련성을 주장하면 승인자가\n * 「트윈이 이 지시 때문에 이것을 골랐다」로 읽는다.\n */\nimport { digestAttentions } from '../../engine/attention-digest.js'\nimport { TwinEngine } from '../../engine/twin-engine.js'\n\n/** 승인 화면이 읽는 한 줄 — 커널 낱말 그대로. 사람의 말은 표현계층이 만든다. */\nexport interface TwinNowAttention {\n id: string\n kind: string\n severity: string\n anchor?: Record<string, string | undefined>\n params?: Record<string, string | number>\n timeStamp?: string\n /** 커널이 권하는 방향 — 실행 가능한 것과 권고가 섞여 있다(`command` 가 있으면 자극이다). */\n recommendedActions?: { code: string; command?: string; args?: unknown }[]\n /** 이 조치가 이것 때문에 올라왔나 — 규칙이 낸 것만 표시된다. */\n isCause?: boolean\n}\n\nexport interface TwinNow {\n /** 심각한 순서로 몇 개. 전부가 아니다. */\n attentions: TwinNowAttention[]\n /** 지금 서 있는 것의 전체 수 — **잘랐으면 몇을 잘랐는지 알 수 있게.** */\n total: number\n /** 이 조치를 낸 근거가 아직 서 있나 — 규칙이 낸 것에만 값이 있다. */\n causeStanding?: boolean\n /**\n * 트윈의 상태를 못 읽었으면 그 이유.\n *\n * 빈 목록으로 답하지 않는다 — 「지금 아무 문제 없다」와 「트윈이 멈춰 있다」는 다른 사실이고,\n * 승인하는 사람에게 그 차이가 크다.\n */\n unavailable?: string\n}\n\n/** 승인 화면에 몇 개까지 싣나 — 사람이 읽는 수. 스물여섯 줄은 아무도 안 읽는다. */\nexport const TWIN_NOW_LIMIT = 5\n\n/**\n * 이 트윈이 지금 무엇을 보고 있나 — 결재선 입력에 실어 보낼 모양으로.\n *\n * `causeRef` 를 주면 그것이 아직 서 있는지도 함께 답한다. 승인하는 사람이 물어야 하는 것 중 가장 중요한\n * 것이 「지금도 그런가」이고, 조건이 사라진 뒤 승인하면 필요 없는 것을 만든다.\n */\nexport function twinNow(domainId: string, instanceId: string, causeRef?: string, limit = TWIN_NOW_LIMIT): TwinNow {\n if (!TwinEngine.owns(domainId, instanceId)) {\n return { attentions: [], total: 0, unavailable: '이 서버가 이 트윈을 들고 있지 않다 — 멈춰 있거나 다른 서버가 돌고 있다' }\n }\n\n const kernel = TwinEngine.kernel(domainId, instanceId)\n const snap = kernel?.getSnapshot?.() ?? TwinEngine.snapshot(domainId, instanceId)\n if (!snap) {\n return { attentions: [], total: 0, unavailable: '이 트윈의 상태를 읽을 수 없다 — 아직 서지 않았거나 멈췄다' }\n }\n\n const all = (snap.attentions ?? []) as TwinNowAttention[]\n\n /* 자리 색은 쓰지 않는다 — 승인 화면은 지도가 아니다. 자른 뒤의 수는 반드시 함께 낸다. */\n const digest = digestAttentions(all as any, limit)\n\n const attentions = (digest.attentions as TwinNowAttention[]).map(a => ({\n ...a,\n ...(causeRef && a.id === causeRef ? { isCause: true } : {})\n }))\n\n return {\n attentions,\n total: digest.attentionTotal,\n /*\n * 근거가 서 있나 — **자른 목록이 아니라 전량에서 찾는다.** 상위 다섯에 안 들었다고 사라진 것이\n * 아니다. 잘린 목록에서 찾으면 심각한 것이 여럿일 때 「사라졌다」로 답한다.\n */\n ...(causeRef ? { causeStanding: all.some(a => a.id === causeRef) } : {})\n }\n}\n"]}
|
|
@@ -11,16 +11,19 @@ export * from './twin-state/twin-state-subscription.js';
|
|
|
11
11
|
export * from './reference/reference-master.js';
|
|
12
12
|
export * from './reference/reference-registry.js';
|
|
13
13
|
export * from './reference/reference-adapter.js';
|
|
14
|
+
export * from './reference/live-cadence.js';
|
|
14
15
|
export * from './reference/reference-live.js';
|
|
15
16
|
export * from './reference/twin-reference.js';
|
|
17
|
+
export * from './reference/connection-portability.js';
|
|
16
18
|
export * from './reference/template-registry.js';
|
|
17
19
|
export * from './reference/reference-probe.js';
|
|
18
20
|
export * from './reference/reference-assessment.js';
|
|
19
21
|
export * from './twin-lifecycle/domain-catalog.js';
|
|
20
|
-
export
|
|
22
|
+
export * from './actuation/index.js';
|
|
23
|
+
export declare const entities: (typeof import("./twin-event/twin-event.js").TwinEvent | typeof import("./twin-subject/twin-subject-event.js").TwinSubjectEvent | typeof import("./twin-instance/twin-instance.js").TwinInstance | typeof import("./reference/twin-reference.js").TwinReference | typeof import("./twin-structure/twin-structure.js").TwinStructure | typeof import("./twin-space/twin-space.js").TwinSpace | typeof import("./twin-space/twin-space-representation.js").TwinSpaceRepresentation | typeof import("./twin-space/twin-space-area.js").TwinSpaceArea | typeof import("./twin-space/twin-area.js").TwinArea | typeof import("./twin-model/twin-location.js").TwinLocation | typeof import("./twin-model/twin-equipment.js").TwinEquipment | typeof import("./twin-model/twin-operation.js").TwinOperation | typeof import("./twin-target/twin-target.js").TwinTarget | typeof import("./twin-audit/twin-audit-event.js").TwinAuditEvent | typeof import("./twin-ingest-window/twin-ingest-window.js").TwinIngestWindow | typeof import("./actuation/twin-command.js").TwinCommandRecord | typeof import("./actuation/actuation-rule.js").TwinActuationRule)[];
|
|
21
24
|
export declare const subscribers: any[];
|
|
22
25
|
export declare const schema: {
|
|
23
26
|
typeDefs: {};
|
|
24
|
-
resolverClasses: (typeof import("./twin-state/twin-state-subscription.js").TwinStateSubscription | typeof import("./twin-subject/twin-subject-query.js").TwinSubjectQuery | typeof import("./twin-subject/twin-subject-query.js").TwinSubjectMutation | typeof import("./twin-audit/twin-audit-query.js").TwinAuditQuery | typeof import("./twin-space/twin-space-resolver.js").TwinSpaceResolver | typeof import("./twin-target/twin-target-resolver.js").TwinTargetResolver | typeof import("./reference/reference-resolver.js").TwinReferenceResolver | typeof import("./reference/reference-progress-subscription.js").TwinReferenceProgressSubscription | typeof import("./twin-control/twin-control-mutation.js").TwinControlMutation | typeof import("./twin-model/twin-model-query.js").TwinModelQuery | typeof import("./twin-model/twin-model-item-query.js").TwinModelItemQuery | typeof import("./twin-model/twin-model-tree-query.js").TwinModelTreeQuery | typeof import("./twin-model/twin-model-mutation.js").TwinModelMutation | typeof import("./twin-model/twin-lineage-query.js").TwinLineageQuery | typeof import("./twin-ingest-window/twin-ingest-window-query.js").TwinIngestWindowQuery | typeof import("./twin-lifecycle/twin-lifecycle-mutation.js").TwinLifecycleMutation | typeof import("./twin-journal/twin-journal-query.js").TwinJournalQuery | typeof import("./twin-forecast/twin-forecast-query.js").TwinForecastQuery | typeof import("./twin-attention/twin-attention-query.js").TwinAttentionQuery | typeof import("./twin-metrics/twin-metrics-query.js").TwinMetricsQuery | typeof import("./twin-readiness/twin-readiness-query.js").TwinReadinessQuery | typeof import("./twin-backfill/twin-backfill-resolver.js").TwinBackfillResolver)[];
|
|
27
|
+
resolverClasses: (typeof import("./twin-state/twin-state-subscription.js").TwinStateSubscription | typeof import("./actuation/actuation-rule-resolver.js").TwinActuationRuleResolver | typeof import("./actuation/twin-command-resolver.js").TwinCommandResolver | typeof import("./twin-subject/twin-subject-query.js").TwinSubjectQuery | typeof import("./twin-subject/twin-subject-query.js").TwinSubjectMutation | typeof import("./twin-audit/twin-audit-query.js").TwinAuditQuery | typeof import("./twin-space/twin-space-resolver.js").TwinSpaceResolver | typeof import("./twin-target/twin-target-resolver.js").TwinTargetResolver | typeof import("./reference/connection-portability-resolver.js").ConnectionPortabilityResolver | typeof import("./reference/reference-resolver.js").TwinReferenceResolver | typeof import("./reference/reference-progress-subscription.js").TwinReferenceProgressSubscription | typeof import("./twin-control/twin-control-mutation.js").TwinControlMutation | typeof import("./twin-model/twin-model-query.js").TwinModelQuery | typeof import("./twin-model/twin-model-item-query.js").TwinModelItemQuery | typeof import("./twin-model/twin-model-tree-query.js").TwinModelTreeQuery | typeof import("./twin-model/twin-model-mutation.js").TwinModelMutation | typeof import("./twin-model/twin-lineage-query.js").TwinLineageQuery | typeof import("./twin-ingest-window/twin-ingest-window-query.js").TwinIngestWindowQuery | typeof import("./twin-lifecycle/twin-lifecycle-mutation.js").TwinLifecycleMutation | typeof import("./twin-journal/twin-journal-query.js").TwinJournalQuery | typeof import("./twin-forecast/twin-forecast-query.js").TwinForecastQuery | typeof import("./twin-attention/twin-attention-query.js").TwinAttentionQuery | typeof import("./twin-metrics/twin-metrics-query.js").TwinMetricsQuery | typeof import("./twin-readiness/twin-readiness-query.js").TwinReadinessQuery | typeof import("./twin-backfill/twin-backfill-resolver.js").TwinBackfillResolver)[];
|
|
25
28
|
directives: {};
|
|
26
29
|
};
|
|
@@ -16,8 +16,10 @@ tslib_1.__exportStar(require("./twin-state/twin-state-subscription.js"), exports
|
|
|
16
16
|
tslib_1.__exportStar(require("./reference/reference-master.js"), exports);
|
|
17
17
|
tslib_1.__exportStar(require("./reference/reference-registry.js"), exports);
|
|
18
18
|
tslib_1.__exportStar(require("./reference/reference-adapter.js"), exports);
|
|
19
|
+
tslib_1.__exportStar(require("./reference/live-cadence.js"), exports);
|
|
19
20
|
tslib_1.__exportStar(require("./reference/reference-live.js"), exports);
|
|
20
21
|
tslib_1.__exportStar(require("./reference/twin-reference.js"), exports);
|
|
22
|
+
tslib_1.__exportStar(require("./reference/connection-portability.js"), exports);
|
|
21
23
|
tslib_1.__exportStar(require("./reference/template-registry.js"), exports);
|
|
22
24
|
/* 커넥터 점검 하네스 — 실 원본에 붙여 무엇이 채워지고 무엇이 비는지 센다(앱을 띄우지 않는다). */
|
|
23
25
|
tslib_1.__exportStar(require("./reference/reference-probe.js"), exports);
|
|
@@ -25,6 +27,11 @@ tslib_1.__exportStar(require("./reference/reference-probe.js"), exports);
|
|
|
25
27
|
tslib_1.__exportStar(require("./reference/reference-assessment.js"), exports);
|
|
26
28
|
/* 도메인 어휘 카탈로그 조합(커널 SSOT 노출) — 서버측 소비자(board-ai 도구 등)가 같은 형태를 쓰도록. */
|
|
27
29
|
tslib_1.__exportStar(require("./twin-lifecycle/domain-catalog.js"), exports);
|
|
30
|
+
/*
|
|
31
|
+
* 조치 — 앱이 결재선을 채우고(§`setApprovalGateway`) 판정을 옮긴다(§`settleByApproval`). 이 줄이 없으면
|
|
32
|
+
* 자리는 있는데 앱이 닿을 길이 없다.
|
|
33
|
+
*/
|
|
34
|
+
tslib_1.__exportStar(require("./actuation/index.js"), exports);
|
|
28
35
|
/* IMPORT ENTITIES AND RESOLVERS */
|
|
29
36
|
const index_js_1 = require("./twin-event/index.js");
|
|
30
37
|
const index_js_2 = require("./twin-subject/index.js");
|
|
@@ -84,6 +91,7 @@ exports.schema = {
|
|
|
84
91
|
...index_js_6.resolvers,
|
|
85
92
|
...index_js_7.resolvers,
|
|
86
93
|
...index_js_8.resolvers,
|
|
94
|
+
...index_js_9.resolvers,
|
|
87
95
|
...index_js_2.resolvers
|
|
88
96
|
],
|
|
89
97
|
directives: {}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/service/index.ts"],"names":[],"mappings":";;;;AAAA,yBAAyB;AACzB,qEAA0C;AAC1C,2EAAgD;AAChD,qFAA0D;AAC1D,2EAAgD;AAChD,qEAA0C;AAC1C,oFAAyD;AACzD,0EAA+C;AAC/C,uEAA4C;AAC5C,6EAAkD;AAClD,kFAAuD;AACvD,0EAA+C;AAC/C,4EAAiD;AACjD,2EAAgD;AAChD,wEAA6C;AAC7C,wEAA6C;AAC7C,2EAAgD;AAChD,4DAA4D;AAC5D,yEAA8C;AAC9C,iEAAiE;AACjE,8EAAmD;AACnD,sEAAsE;AACtE,6EAAkD;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/service/index.ts"],"names":[],"mappings":";;;;AAAA,yBAAyB;AACzB,qEAA0C;AAC1C,2EAAgD;AAChD,qFAA0D;AAC1D,2EAAgD;AAChD,qEAA0C;AAC1C,oFAAyD;AACzD,0EAA+C;AAC/C,uEAA4C;AAC5C,6EAAkD;AAClD,kFAAuD;AACvD,0EAA+C;AAC/C,4EAAiD;AACjD,2EAAgD;AAChD,sEAA2C;AAC3C,wEAA6C;AAC7C,wEAA6C;AAC7C,gFAAqD;AACrD,2EAAgD;AAChD,4DAA4D;AAC5D,yEAA8C;AAC9C,iEAAiE;AACjE,8EAAmD;AACnD,sEAAsE;AACtE,6EAAkD;AAClD;;;GAGG;AACH,+DAAoC;AAEpC,mCAAmC;AACnC,oDAAqE;AACrE,sDAA4G;AAC5G,oDAAsG;AACtG,uDAA2E;AAC3E,wDAA6E;AAC7E,oDAAsG;AACtG,qDAAyG;AACzG,mDAA6G;AAC7G,2EAA2E;AAC3E,mDAA6G;AAC7G,qDAAuE;AACvE,uDAA2E;AAC3E,qDAAsG;AACtG,mEAAmE;AACnE,6DAA4H;AAC5H,yDAA+E;AAC/E,uDAA2E;AAC3E,wDAA6E;AAC7E,yDAA+E;AAC/E,uDAA2E;AAC3E,yDAA+E;AAC/E,qDAAqD;AACrD,wDAA6E;AAEhE,QAAA,QAAQ,GAAG;IACtB,cAAc;IACd,GAAG,mBAAiB;IACpB,GAAG,mBAAmB;IACtB,GAAG,oBAAwB;IAC3B,GAAG,mBAAiB;IACpB,GAAG,mBAAoB;IACvB,GAAG,mBAAqB;IACxB,GAAG,mBAAiB;IACpB,GAAG,mBAAkB;IACrB,GAAG,mBAAqB;IACxB,GAAG,mBAAqB;IACxB,4DAA4D;IAC5D,GAAG,oBAAiB;CACrB,CAAA;AAEY,QAAA,WAAW,GAAG,EAAE,CAAA;AAEhB,QAAA,MAAM,GAAG;IACpB,QAAQ,EAAE,EAAE;IACZ,eAAe,EAAE;QACf,sBAAsB;QACtB,GAAG,qBAAkB;QACrB,GAAG,qBAAoB;QACvB,GAAG,qBAAsB;QACzB,GAAG,qBAAoB;QACvB,GAAG,oBAAkB;QACrB,GAAG,qBAAqB;QACxB,GAAG,qBAAsB;QACzB,GAAG,qBAAoB;QACvB,GAAG,qBAAsB;QACzB,GAAG,qBAAqB;QACxB,GAAG,qBAAyB;QAC5B,GAAG,qBAAkB;QACrB,GAAG,oBAAkB;QACrB,GAAG,oBAAmB;QACtB,GAAG,oBAAsB;QACzB,GAAG,oBAAsB;QACzB,GAAG,oBAAoB;KACxB;IACD,UAAU,EAAE,EAAE;CACf,CAAA","sourcesContent":["/* EXPORT ENTITY TYPES */\nexport * from './twin-event/twin-event.js'\nexport * from './twin-audit/twin-audit-event.js'\nexport * from './twin-ingest-window/twin-ingest-window.js'\nexport * from './twin-instance/twin-instance.js'\nexport * from './twin-space/twin-space.js'\nexport * from './twin-space/twin-space-representation.js'\nexport * from './twin-space/twin-space-area.js'\nexport * from './twin-target/twin-target.js'\nexport * from './twin-structure/twin-structure.js'\nexport * from './twin-state/twin-state-subscription.js'\nexport * from './reference/reference-master.js'\nexport * from './reference/reference-registry.js'\nexport * from './reference/reference-adapter.js'\nexport * from './reference/live-cadence.js'\nexport * from './reference/reference-live.js'\nexport * from './reference/twin-reference.js'\nexport * from './reference/connection-portability.js'\nexport * from './reference/template-registry.js'\n/* 커넥터 점검 하네스 — 실 원본에 붙여 무엇이 채워지고 무엇이 비는지 센다(앱을 띄우지 않는다). */\nexport * from './reference/reference-probe.js'\n/* 읽은 뒤의 총평(사실 층) — 화면이 표로 그리고, 더 알고 싶을 때 AI 가 이 사실만 근거로 서술한다. */\nexport * from './reference/reference-assessment.js'\n/* 도메인 어휘 카탈로그 조합(커널 SSOT 노출) — 서버측 소비자(board-ai 도구 등)가 같은 형태를 쓰도록. */\nexport * from './twin-lifecycle/domain-catalog.js'\n/*\n * 조치 — 앱이 결재선을 채우고(§`setApprovalGateway`) 판정을 옮긴다(§`settleByApproval`). 이 줄이 없으면\n * 자리는 있는데 앱이 닿을 길이 없다.\n */\nexport * from './actuation/index.js'\n\n/* IMPORT ENTITIES AND RESOLVERS */\nimport { entities as TwinEventEntities } from './twin-event/index.js'\nimport { entities as TwinSubjectEntities, resolvers as TwinSubjectResolvers } from './twin-subject/index.js'\nimport { entities as TwinAuditEntities, resolvers as TwinAuditResolvers } from './twin-audit/index.js'\nimport { entities as TwinInstanceEntities } from './twin-instance/index.js'\nimport { entities as TwinStructureEntities } from './twin-structure/index.js'\nimport { entities as TwinSpaceEntities, resolvers as TwinSpaceResolvers } from './twin-space/index.js'\nimport { entities as TwinTargetEntities, resolvers as TwinTargetResolvers } from './twin-target/index.js'\nimport { entities as TwinReferenceEntities, resolvers as TwinReferenceResolvers } from './reference/index.js'\n/* 현장에 내리는 조치 — 승인이 재기동을 넘어 살아야 하므로 표로 든다(§`actuation/twin-command.ts`). */\nimport { entities as TwinActuationEntities, resolvers as TwinActuationResolvers } from './actuation/index.js'\nimport { resolvers as TwinStateResolvers } from './twin-state/index.js'\nimport { resolvers as TwinControlResolvers } from './twin-control/index.js'\nimport { entities as TwinModelEntities, resolvers as TwinModelResolvers } from './twin-model/index.js'\n/* 인제스트 창 — 동기화 추이를 재기동 뒤에도 이어 보기 위한 행(리졸버 없음: 메모리 장부가 조회를 답한다). */\nimport { entities as TwinIngestWindowEntities, resolvers as TwinIngestWindowResolvers } from './twin-ingest-window/index.js'\nimport { resolvers as TwinLifecycleResolvers } from './twin-lifecycle/index.js'\nimport { resolvers as TwinJournalResolvers } from './twin-journal/index.js'\nimport { resolvers as TwinForecastResolvers } from './twin-forecast/index.js'\nimport { resolvers as TwinAttentionResolvers } from './twin-attention/index.js'\nimport { resolvers as TwinMetricsResolvers } from './twin-metrics/index.js'\nimport { resolvers as TwinReadinessResolvers } from './twin-readiness/index.js'\n/* 지난 기록 채우기 — 저널에만 쓰는 문. 라이브 유입과 판단이 달라 문을 따로 둔다. */\nimport { resolvers as TwinBackfillResolvers } from './twin-backfill/index.js'\n\nexport const entities = [\n /* ENTITIES */\n ...TwinEventEntities,\n ...TwinSubjectEntities,\n ...TwinIngestWindowEntities,\n ...TwinAuditEntities,\n ...TwinInstanceEntities,\n ...TwinStructureEntities,\n ...TwinSpaceEntities,\n ...TwinTargetEntities,\n ...TwinReferenceEntities,\n ...TwinActuationEntities,\n /* 구조 투영 — model JSON 에서 풀어낸 표준 엔티티(ADR-0032). 원본에서 온 캐시. */\n ...TwinModelEntities\n]\n\nexport const subscribers = []\n\nexport const schema = {\n typeDefs: {},\n resolverClasses: [\n /* RESOLVER CLASSES */\n ...TwinStateResolvers,\n ...TwinControlResolvers,\n ...TwinLifecycleResolvers,\n ...TwinJournalResolvers,\n ...TwinAuditResolvers,\n ...TwinForecastResolvers,\n ...TwinAttentionResolvers,\n ...TwinMetricsResolvers,\n ...TwinReadinessResolvers,\n ...TwinBackfillResolvers,\n ...TwinIngestWindowResolvers,\n ...TwinModelResolvers,\n ...TwinSpaceResolvers,\n ...TwinTargetResolvers,\n ...TwinReferenceResolvers,\n ...TwinActuationResolvers,\n ...TwinSubjectResolvers\n ],\n directives: {}\n}\n"]}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { ApprovedCommand } from '@operato/ops-contract';
|
|
2
|
+
import { type ActuationReadiness } from './reference-adapter.js';
|
|
3
|
+
import { type ActuationTarget } from './actuation-target.js';
|
|
4
|
+
/** 어댑터가 답하는 모양 그대로 — 디스패처가 이 값을 커맨드 행에 적는다. */
|
|
5
|
+
/**
|
|
6
|
+
* 조치를 낸 결과 — **어댑터가 답한 것을 좁히지 않고 그대로 나른다.**
|
|
7
|
+
*
|
|
8
|
+
* ── 왜 넓혔나 (2026-09-04, 인티그레이션 레인이 찾음) ────────────────────────
|
|
9
|
+
* 이 타입이 `ok`·`ref`·`error` 셋만 들고 있었다. 그래서 `routeActuation` 이 어댑터의 답을
|
|
10
|
+
* 그대로 되돌리는데 **타입은 `note`·`noteNext`·`retry` 를 버린다고 말했다.**
|
|
11
|
+
*
|
|
12
|
+
* 값은 흘렀다 — JS 객체가 키를 안 버리므로. 어제 `dispatch_note` 에 실제로 앉았다. 그런데
|
|
13
|
+
* **커넥터를 만드는 사람이 그 칸이 있다는 것을 알 방법이 없었다.** 우리가 어제 하루 종일
|
|
14
|
+
* 「자리는 있고 길이 없다」로 부른 것의 타입 판이다.
|
|
15
|
+
*
|
|
16
|
+
* 좁히지 않는다: 세 칸을 다 실어 `CommandAck` 와 같은 모양으로 둔다. 커널이 어느 칸을 어떻게
|
|
17
|
+
* 쓰는지는 `dispatch` 가 정하고, 이 자리는 나르기만 한다.
|
|
18
|
+
*/
|
|
19
|
+
export interface ActuationAck {
|
|
20
|
+
ok: boolean;
|
|
21
|
+
/** 저쪽 문서·작업 식별자. 없으면 무엇을 되돌릴지 지목할 수 없다. */
|
|
22
|
+
ref?: string;
|
|
23
|
+
/** 받아들였으나 저쪽이 덧붙인 말 — **실패가 아니다.** 원인 쪽 문장. */
|
|
24
|
+
note?: string;
|
|
25
|
+
/** 그 말 중 다음에 할 일 한 줄 — 붙여 보내지 않는다. */
|
|
26
|
+
noteNext?: string;
|
|
27
|
+
error?: string;
|
|
28
|
+
/** 다시 해서 될 일인가 — 실패에만. 말하지 않으면 일꾼이 집지 않는다. */
|
|
29
|
+
retry?: 'again' | 'after-fix' | 'never';
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* 이 트윈의 원본에 승인된 조치를 보낸다.
|
|
33
|
+
*
|
|
34
|
+
* **거절할 때는 무엇이 없어서인지 말한다.** 「보내지 못했습니다」만 답하면 사람이 연결 설정을 열어야
|
|
35
|
+
* 하는지, 커넥터를 고쳐야 하는지, 트윈을 다시 만들어야 하는지 알 수 없다.
|
|
36
|
+
*/
|
|
37
|
+
export declare function routeActuation(domainId: string, instanceId: string, command: ApprovedCommand): Promise<ActuationAck>;
|
|
38
|
+
/**
|
|
39
|
+
* 이 트윈이 조치를 받을 수 있나 — **넘기기 전에 화면이 먼저 알 수 있게.**
|
|
40
|
+
*
|
|
41
|
+
* 승인 화면이 「넘기기」를 보이기 전에 이것을 묻는다. 누를 수 없는 것을 보여 주고 눌렀을 때 거절하는
|
|
42
|
+
* 것보다, 왜 못 넘기는지 먼저 말하는 것이 낫다.
|
|
43
|
+
*/
|
|
44
|
+
export declare function actuationTargetOf(domainId: string, instanceId: string): Promise<ActuationTarget>;
|
|
45
|
+
/**
|
|
46
|
+
* **저쪽이 받을 준비가 됐나** — 넘기기 전에 묻는다. 화면이 사람을 헛걸음시키지 않게.
|
|
47
|
+
*
|
|
48
|
+
* ── 왜 `actuationTargetOf` 와 따로인가 ──────────────────────────────────────
|
|
49
|
+
* 그쪽은 **망을 타지 않는다.** 화면이 목록을 그릴 때마다 부를 수 있어야 하므로 그래야 한다.
|
|
50
|
+
* 이 함수는 커넥터가 저쪽에 물을 수 있고, 그래서 부르는 시점이 다르다 — 사람이 「넘기기」를
|
|
51
|
+
* 열었을 때다.
|
|
52
|
+
*
|
|
53
|
+
* ── 답이 문이 아니다 ────────────────────────────────────────────────────────
|
|
54
|
+
* **이 답으로 넘김을 막지 않는다.** 막으면 점검이 틀렸거나 잠깐 못 닿은 사이에 성공할 수 있었던
|
|
55
|
+
* 조치가 못 나간다. 화면이 미리 말하는 데까지가 이 함수의 일이다.
|
|
56
|
+
*
|
|
57
|
+
* 커넥터가 이 얼굴을 안 만들었으면 **「모른다」로 답한다** — 「준비됐다」가 아니다. 없는 확인을
|
|
58
|
+
* 통과로 읽으면 이 함수가 있으나 없으나 같아진다.
|
|
59
|
+
*/
|
|
60
|
+
export declare function actuationReadinessOf(domainId: string, instanceId: string): Promise<ActuationReadiness>;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.routeActuation = routeActuation;
|
|
4
|
+
exports.actuationTargetOf = actuationTargetOf;
|
|
5
|
+
exports.actuationReadinessOf = actuationReadinessOf;
|
|
6
|
+
/*
|
|
7
|
+
* **승인된 조치가 원본으로 나가는 길** — 판단은 `actuation-target.ts`, 여기는 배선.
|
|
8
|
+
*
|
|
9
|
+
* ── 무엇이 끊겨 있었나 (2026-09-02) ────────────────────────────────────────────
|
|
10
|
+
* MES 커넥터는 `actuate` 를 다 만들어 두었다. 서명·접수 판정·초안 상태까지 있었다. 그런데 넘기는
|
|
11
|
+
* 리졸버가 **어댑터를 찾지도 않고** 「구현이 없습니다」라고 답했다. 그 문장을 사실로 읽어서 설계
|
|
12
|
+
* 문서에도, 다른 레인에도 그렇게 알렸다 — 둘 다 틀렸다.
|
|
13
|
+
*
|
|
14
|
+
* 자리는 있고 길이 없는 상태다. 이 저장소에서 가장 자주 나는 결함 부류이고, 조치 경로에서 나면
|
|
15
|
+
* 결과가 하나다 — **승인은 쌓이는데 현장에는 아무것도 가지 않는다.**
|
|
16
|
+
*
|
|
17
|
+
* ── 왜 구동과 따로 두나 ───────────────────────────────────────────────────────
|
|
18
|
+
* 모양이 같아서 한 함수로 묶고 싶어진다. 묶지 않는다.
|
|
19
|
+
*
|
|
20
|
+
* 구동은 시뮬레이터를 움직인다 틀리면 시늉이 어긋난다
|
|
21
|
+
* 조치는 현장에 작업지시를 낸다 틀리면 만들지 않아도 될 것을 만든다
|
|
22
|
+
*/
|
|
23
|
+
const shell_1 = require("@things-factory/shell");
|
|
24
|
+
const twin_instance_js_1 = require("../twin-instance/twin-instance.js");
|
|
25
|
+
const twin_reference_js_1 = require("./twin-reference.js");
|
|
26
|
+
const reference_adapter_js_1 = require("./reference-adapter.js");
|
|
27
|
+
const actuation_target_js_1 = require("./actuation-target.js");
|
|
28
|
+
/**
|
|
29
|
+
* 저장소를 **한 번만** 읽고, 판정과 함께 부를 것까지 돌려준다.
|
|
30
|
+
*
|
|
31
|
+
* 판정한 뒤 보낼 때 다시 읽으면 그 사이에 연결 설정이 바뀔 수 있다 — 판정한 커넥터와 실제로 부르는
|
|
32
|
+
* 커넥터가 달라진다. 그리고 같은 행을 두 번 읽을 이유가 없다.
|
|
33
|
+
*/
|
|
34
|
+
async function resolve(domainId, instanceId) {
|
|
35
|
+
const inst = await (0, shell_1.getRepository)(twin_instance_js_1.TwinInstance).findOne({ where: { domain: { id: domainId }, instanceId } });
|
|
36
|
+
const ref = inst
|
|
37
|
+
? await (0, shell_1.getRepository)(twin_reference_js_1.TwinReference).findOne({ where: { domain: { id: domainId }, source: instanceId } })
|
|
38
|
+
: null;
|
|
39
|
+
const adapter = ref ? (0, reference_adapter_js_1.getAdapter)(ref.adapterType) : undefined;
|
|
40
|
+
const target = (0, actuation_target_js_1.actuationTarget)({
|
|
41
|
+
instanceId,
|
|
42
|
+
twinExists: !!inst,
|
|
43
|
+
siteId: inst?.origin?.siteId,
|
|
44
|
+
...(ref ? { ref: { source: ref.source, adapterType: ref.adapterType } } : {}),
|
|
45
|
+
adapterRegistered: !!adapter,
|
|
46
|
+
capabilities: (0, reference_adapter_js_1.capabilitiesOf)(adapter),
|
|
47
|
+
declaresActuate: !!adapter?.capabilities?.includes('actuate'),
|
|
48
|
+
hasActuateImpl: typeof adapter?.actuate === 'function'
|
|
49
|
+
});
|
|
50
|
+
return { target, ref, adapter };
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* 이 트윈의 원본에 승인된 조치를 보낸다.
|
|
54
|
+
*
|
|
55
|
+
* **거절할 때는 무엇이 없어서인지 말한다.** 「보내지 못했습니다」만 답하면 사람이 연결 설정을 열어야
|
|
56
|
+
* 하는지, 커넥터를 고쳐야 하는지, 트윈을 다시 만들어야 하는지 알 수 없다.
|
|
57
|
+
*/
|
|
58
|
+
async function routeActuation(domainId, instanceId, command) {
|
|
59
|
+
const { target, ref, adapter } = await resolve(domainId, instanceId);
|
|
60
|
+
if (!(0, actuation_target_js_1.isActuationAllowed)(target))
|
|
61
|
+
return { ok: false, error: `${target.detail}. ${actuation_target_js_1.APPROVAL_STANDS}` };
|
|
62
|
+
/*
|
|
63
|
+
* 여기까지 왔으면 판정이 그 둘의 존재를 이미 확인했다(§`actuationTarget`). 그래도 조용히 지나가지
|
|
64
|
+
* 않는다 — 판정과 배선이 어긋나면 그것이 결함이고, `!` 로 덮으면 그 결함이 「아무 일도 일어나지
|
|
65
|
+
* 않았다」로 나타난다.
|
|
66
|
+
*/
|
|
67
|
+
if (!adapter?.actuate) {
|
|
68
|
+
return { ok: false, error: `커넥터를 찾았는데 조치를 보낼 자리가 없습니다 (${target.adapterType}). ${actuation_target_js_1.APPROVAL_STANDS}` };
|
|
69
|
+
}
|
|
70
|
+
const site = { siteId: target.siteId };
|
|
71
|
+
return adapter.actuate(ref?.connectionConfig ?? {}, site, command);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* 이 트윈이 조치를 받을 수 있나 — **넘기기 전에 화면이 먼저 알 수 있게.**
|
|
75
|
+
*
|
|
76
|
+
* 승인 화면이 「넘기기」를 보이기 전에 이것을 묻는다. 누를 수 없는 것을 보여 주고 눌렀을 때 거절하는
|
|
77
|
+
* 것보다, 왜 못 넘기는지 먼저 말하는 것이 낫다.
|
|
78
|
+
*/
|
|
79
|
+
async function actuationTargetOf(domainId, instanceId) {
|
|
80
|
+
return (await resolve(domainId, instanceId)).target;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* **저쪽이 받을 준비가 됐나** — 넘기기 전에 묻는다. 화면이 사람을 헛걸음시키지 않게.
|
|
84
|
+
*
|
|
85
|
+
* ── 왜 `actuationTargetOf` 와 따로인가 ──────────────────────────────────────
|
|
86
|
+
* 그쪽은 **망을 타지 않는다.** 화면이 목록을 그릴 때마다 부를 수 있어야 하므로 그래야 한다.
|
|
87
|
+
* 이 함수는 커넥터가 저쪽에 물을 수 있고, 그래서 부르는 시점이 다르다 — 사람이 「넘기기」를
|
|
88
|
+
* 열었을 때다.
|
|
89
|
+
*
|
|
90
|
+
* ── 답이 문이 아니다 ────────────────────────────────────────────────────────
|
|
91
|
+
* **이 답으로 넘김을 막지 않는다.** 막으면 점검이 틀렸거나 잠깐 못 닿은 사이에 성공할 수 있었던
|
|
92
|
+
* 조치가 못 나간다. 화면이 미리 말하는 데까지가 이 함수의 일이다.
|
|
93
|
+
*
|
|
94
|
+
* 커넥터가 이 얼굴을 안 만들었으면 **「모른다」로 답한다** — 「준비됐다」가 아니다. 없는 확인을
|
|
95
|
+
* 통과로 읽으면 이 함수가 있으나 없으나 같아진다.
|
|
96
|
+
*/
|
|
97
|
+
async function actuationReadinessOf(domainId, instanceId) {
|
|
98
|
+
const { target, ref, adapter } = await resolve(domainId, instanceId);
|
|
99
|
+
/* 보낼 대상 자체가 없으면 저쪽에 물을 것도 없다 — 그 사실을 그대로 옮긴다. */
|
|
100
|
+
if (!(0, actuation_target_js_1.isActuationAllowed)(target)) {
|
|
101
|
+
return { checked: 'local', ready: false, reason: target.detail, code: target.code };
|
|
102
|
+
}
|
|
103
|
+
if (!adapter?.actuationReadiness) {
|
|
104
|
+
/* 확인할 길이 없다. 「준비됐다」로 답하지 않는다 — 그러면 화면이 없는 확인을 통과로 읽는다. */
|
|
105
|
+
return {
|
|
106
|
+
checked: 'local',
|
|
107
|
+
reason: `커넥터 "${target.adapterType}" 는 저쪽이 받을 준비가 됐는지 알려 주지 않습니다 — 넘겨 보고 알게 됩니다`,
|
|
108
|
+
code: 'not-checkable'
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
return await adapter.actuationReadiness(ref?.connectionConfig ?? {}, { siteId: target.siteId });
|
|
113
|
+
}
|
|
114
|
+
catch (e) {
|
|
115
|
+
/*
|
|
116
|
+
* 점검이 터진 것을 「저쪽이 죽었다」로 옮기지 않는다 — 우리 점검의 실패다. 그리고 던지지 않는다:
|
|
117
|
+
* 이 함수 때문에 승인 화면이 안 열리면, 알려 주려던 것보다 큰 것을 잃는다.
|
|
118
|
+
*/
|
|
119
|
+
return {
|
|
120
|
+
checked: 'local',
|
|
121
|
+
reason: `준비 상태를 확인하다 실패했습니다 — ${String(e?.message ?? e).slice(0, 160)}`,
|
|
122
|
+
code: 'check-failed'
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=actuation-routing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actuation-routing.js","sourceRoot":"","sources":["../../../server/service/reference/actuation-routing.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * **승인된 조치가 원본으로 나가는 길** — 판단은 `actuation-target.ts`, 여기는 배선.\n *\n * ── 무엇이 끊겨 있었나 (2026-09-02) ────────────────────────────────────────────\n * MES 커넥터는 `actuate` 를 다 만들어 두었다. 서명·접수 판정·초안 상태까지 있었다. 그런데 넘기는\n * 리졸버가 **어댑터를 찾지도 않고** 「구현이 없습니다」라고 답했다. 그 문장을 사실로 읽어서 설계\n * 문서에도, 다른 레인에도 그렇게 알렸다 — 둘 다 틀렸다.\n *\n * 자리는 있고 길이 없는 상태다. 이 저장소에서 가장 자주 나는 결함 부류이고, 조치 경로에서 나면\n * 결과가 하나다 — **승인은 쌓이는데 현장에는 아무것도 가지 않는다.**\n *\n * ── 왜 구동과 따로 두나 ───────────────────────────────────────────────────────\n * 모양이 같아서 한 함수로 묶고 싶어진다. 묶지 않는다.\n *\n * 구동은 시뮬레이터를 움직인다 틀리면 시늉이 어긋난다\n * 조치는 현장에 작업지시를 낸다 틀리면 만들지 않아도 될 것을 만든다\n */\nimport { getRepository } from '@things-factory/shell'\n\nimport type { ApprovedCommand } from '@operato/ops-contract'\n\nimport { TwinInstance } from '../twin-instance/twin-instance.js'\n\nimport { TwinReference } from './twin-reference.js'\nimport { capabilitiesOf, getAdapter, type ActuationReadiness, type SiteDescriptor } from './reference-adapter.js'\nimport { APPROVAL_STANDS, actuationTarget, isActuationAllowed, type ActuationTarget } from './actuation-target.js'\n\n/** 어댑터가 답하는 모양 그대로 — 디스패처가 이 값을 커맨드 행에 적는다. */\n/**\n * 조치를 낸 결과 — **어댑터가 답한 것을 좁히지 않고 그대로 나른다.**\n *\n * ── 왜 넓혔나 (2026-09-04, 인티그레이션 레인이 찾음) ────────────────────────\n * 이 타입이 `ok`·`ref`·`error` 셋만 들고 있었다. 그래서 `routeActuation` 이 어댑터의 답을\n * 그대로 되돌리는데 **타입은 `note`·`noteNext`·`retry` 를 버린다고 말했다.**\n *\n * 값은 흘렀다 — JS 객체가 키를 안 버리므로. 어제 `dispatch_note` 에 실제로 앉았다. 그런데\n * **커넥터를 만드는 사람이 그 칸이 있다는 것을 알 방법이 없었다.** 우리가 어제 하루 종일\n * 「자리는 있고 길이 없다」로 부른 것의 타입 판이다.\n *\n * 좁히지 않는다: 세 칸을 다 실어 `CommandAck` 와 같은 모양으로 둔다. 커널이 어느 칸을 어떻게\n * 쓰는지는 `dispatch` 가 정하고, 이 자리는 나르기만 한다.\n */\nexport interface ActuationAck {\n ok: boolean\n /** 저쪽 문서·작업 식별자. 없으면 무엇을 되돌릴지 지목할 수 없다. */\n ref?: string\n /** 받아들였으나 저쪽이 덧붙인 말 — **실패가 아니다.** 원인 쪽 문장. */\n note?: string\n /** 그 말 중 다음에 할 일 한 줄 — 붙여 보내지 않는다. */\n noteNext?: string\n error?: string\n /** 다시 해서 될 일인가 — 실패에만. 말하지 않으면 일꾼이 집지 않는다. */\n retry?: 'again' | 'after-fix' | 'never'\n}\n\n/**\n * 저장소를 **한 번만** 읽고, 판정과 함께 부를 것까지 돌려준다.\n *\n * 판정한 뒤 보낼 때 다시 읽으면 그 사이에 연결 설정이 바뀔 수 있다 — 판정한 커넥터와 실제로 부르는\n * 커넥터가 달라진다. 그리고 같은 행을 두 번 읽을 이유가 없다.\n */\nasync function resolve(domainId: string, instanceId: string) {\n const inst = await getRepository(TwinInstance).findOne({ where: { domain: { id: domainId }, instanceId } })\n const ref = inst\n ? await getRepository(TwinReference).findOne({ where: { domain: { id: domainId }, source: instanceId } })\n : null\n const adapter = ref ? getAdapter(ref.adapterType) : undefined\n\n const target = actuationTarget({\n instanceId,\n twinExists: !!inst,\n siteId: (inst?.origin as any)?.siteId,\n ...(ref ? { ref: { source: ref.source, adapterType: ref.adapterType } } : {}),\n adapterRegistered: !!adapter,\n capabilities: capabilitiesOf(adapter),\n declaresActuate: !!adapter?.capabilities?.includes('actuate'),\n hasActuateImpl: typeof adapter?.actuate === 'function'\n })\n\n return { target, ref, adapter }\n}\n\n/**\n * 이 트윈의 원본에 승인된 조치를 보낸다.\n *\n * **거절할 때는 무엇이 없어서인지 말한다.** 「보내지 못했습니다」만 답하면 사람이 연결 설정을 열어야\n * 하는지, 커넥터를 고쳐야 하는지, 트윈을 다시 만들어야 하는지 알 수 없다.\n */\nexport async function routeActuation(\n domainId: string,\n instanceId: string,\n command: ApprovedCommand\n): Promise<ActuationAck> {\n const { target, ref, adapter } = await resolve(domainId, instanceId)\n if (!isActuationAllowed(target)) return { ok: false, error: `${target.detail}. ${APPROVAL_STANDS}` }\n\n /*\n * 여기까지 왔으면 판정이 그 둘의 존재를 이미 확인했다(§`actuationTarget`). 그래도 조용히 지나가지\n * 않는다 — 판정과 배선이 어긋나면 그것이 결함이고, `!` 로 덮으면 그 결함이 「아무 일도 일어나지\n * 않았다」로 나타난다.\n */\n if (!adapter?.actuate) {\n return { ok: false, error: `커넥터를 찾았는데 조치를 보낼 자리가 없습니다 (${target.adapterType}). ${APPROVAL_STANDS}` }\n }\n\n const site: SiteDescriptor = { siteId: target.siteId }\n return adapter.actuate(ref?.connectionConfig ?? {}, site, command)\n}\n\n/**\n * 이 트윈이 조치를 받을 수 있나 — **넘기기 전에 화면이 먼저 알 수 있게.**\n *\n * 승인 화면이 「넘기기」를 보이기 전에 이것을 묻는다. 누를 수 없는 것을 보여 주고 눌렀을 때 거절하는\n * 것보다, 왜 못 넘기는지 먼저 말하는 것이 낫다.\n */\nexport async function actuationTargetOf(domainId: string, instanceId: string): Promise<ActuationTarget> {\n return (await resolve(domainId, instanceId)).target\n}\n\n/**\n * **저쪽이 받을 준비가 됐나** — 넘기기 전에 묻는다. 화면이 사람을 헛걸음시키지 않게.\n *\n * ── 왜 `actuationTargetOf` 와 따로인가 ──────────────────────────────────────\n * 그쪽은 **망을 타지 않는다.** 화면이 목록을 그릴 때마다 부를 수 있어야 하므로 그래야 한다.\n * 이 함수는 커넥터가 저쪽에 물을 수 있고, 그래서 부르는 시점이 다르다 — 사람이 「넘기기」를\n * 열었을 때다.\n *\n * ── 답이 문이 아니다 ────────────────────────────────────────────────────────\n * **이 답으로 넘김을 막지 않는다.** 막으면 점검이 틀렸거나 잠깐 못 닿은 사이에 성공할 수 있었던\n * 조치가 못 나간다. 화면이 미리 말하는 데까지가 이 함수의 일이다.\n *\n * 커넥터가 이 얼굴을 안 만들었으면 **「모른다」로 답한다** — 「준비됐다」가 아니다. 없는 확인을\n * 통과로 읽으면 이 함수가 있으나 없으나 같아진다.\n */\nexport async function actuationReadinessOf(domainId: string, instanceId: string): Promise<ActuationReadiness> {\n const { target, ref, adapter } = await resolve(domainId, instanceId)\n\n /* 보낼 대상 자체가 없으면 저쪽에 물을 것도 없다 — 그 사실을 그대로 옮긴다. */\n if (!isActuationAllowed(target)) {\n return { checked: 'local', ready: false, reason: target.detail, code: target.code }\n }\n\n if (!adapter?.actuationReadiness) {\n /* 확인할 길이 없다. 「준비됐다」로 답하지 않는다 — 그러면 화면이 없는 확인을 통과로 읽는다. */\n return {\n checked: 'local',\n reason: `커넥터 \"${target.adapterType}\" 는 저쪽이 받을 준비가 됐는지 알려 주지 않습니다 — 넘겨 보고 알게 됩니다`,\n code: 'not-checkable'\n }\n }\n\n try {\n return await adapter.actuationReadiness(ref?.connectionConfig ?? {}, { siteId: target.siteId })\n } catch (e: any) {\n /*\n * 점검이 터진 것을 「저쪽이 죽었다」로 옮기지 않는다 — 우리 점검의 실패다. 그리고 던지지 않는다:\n * 이 함수 때문에 승인 화면이 안 열리면, 알려 주려던 것보다 큰 것을 잃는다.\n */\n return {\n checked: 'local',\n reason: `준비 상태를 확인하다 실패했습니다 — ${String(e?.message ?? e).slice(0, 160)}`,\n code: 'check-failed'\n }\n }\n}\n"]}
|