@trillboards/edge-sdk 0.2.138 → 0.2.150
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/README.md +2 -2
- package/deploy/detector/pyproject.toml +20 -2
- package/deploy/detector/tests/test_execution.py +124 -0
- package/deploy/detector/tests/test_model_identity.py +80 -0
- package/deploy/detector/tests/test_registry.py +18 -0
- package/deploy/detector/tests/test_server.py +3 -1
- package/deploy/detector/tests/test_version_stamp.py +4 -1
- package/deploy/detector/trillboards_detector/_version.py +2 -2
- package/deploy/detector/trillboards_detector/cli.py +3 -0
- package/deploy/detector/trillboards_detector/detections.py +7 -0
- package/deploy/detector/trillboards_detector/errors.py +4 -0
- package/deploy/detector/trillboards_detector/execution.py +109 -0
- package/deploy/detector/trillboards_detector/loader.py +6 -3
- package/deploy/detector/trillboards_detector/registry.py +27 -5
- package/deploy/detector/trillboards_detector/server.py +29 -2
- package/deploy/detector/trillboards_detector/session.py +15 -3
- package/deploy/litertlm/test_trillboards_litertlm_shim.py +81 -0
- package/deploy/litertlm/test_verify_model_proof.py +30 -0
- package/deploy/litertlm/trillboards_litertlm_shim.py +150 -1
- package/deploy/litertlm/verify_model_proof.py +30 -0
- package/deploy/macos/TrillboardsSetupApp.m +131 -3
- package/deploy/macos/build-macos-pkg.sh +13 -10
- package/deploy/macos/scripts/setup-detector.sh +2 -2
- package/deploy/macos/scripts/setup-litertlm.sh +66 -30
- package/deploy/macos/test-setup-litertlm-reinstall.sh +57 -4
- package/deploy/setup-guidance.json +112 -0
- package/deploy/systemd/setup-detector.sh +2 -2
- package/deploy/systemd/setup-litertlm.sh +51 -27
- package/deploy/windows/build-windows.ps1 +11 -6
- package/deploy/windows/bundle-windows-installer.ps1 +3 -3
- package/deploy/windows/e2e/test-local-sense.ps1 +6 -5
- package/deploy/windows/install-update.ps1 +52 -9
- package/deploy/windows/set-executable-icon.ps1 +24 -5
- package/deploy/windows/setup-detector.ps1 +142 -4
- package/deploy/windows/setup-litertlm.ps1 +53 -10
- package/deploy/windows/trillboards-setup.ps1 +101 -2
- package/dist/AudienceAnalyzeClient.d.ts +10 -0
- package/dist/AudienceAnalyzeClient.d.ts.map +1 -1
- package/dist/AudienceAnalyzeClient.js +5 -4
- package/dist/AudienceAnalyzeClient.js.map +1 -1
- package/dist/CloudSensingManager.d.ts +38 -1
- package/dist/CloudSensingManager.d.ts.map +1 -1
- package/dist/CloudSensingManager.js +403 -25
- package/dist/CloudSensingManager.js.map +1 -1
- package/dist/DetectorFieldProducer.d.ts +5 -1
- package/dist/DetectorFieldProducer.d.ts.map +1 -1
- package/dist/DetectorFieldProducer.js +8 -1
- package/dist/DetectorFieldProducer.js.map +1 -1
- package/dist/DurableVlmCandidateSpool.d.ts +83 -2
- package/dist/DurableVlmCandidateSpool.d.ts.map +1 -1
- package/dist/DurableVlmCandidateSpool.js +596 -30
- package/dist/DurableVlmCandidateSpool.js.map +1 -1
- package/dist/EdgeAgent.js +1 -1
- package/dist/FrameGroupingShadow.d.ts +51 -0
- package/dist/FrameGroupingShadow.d.ts.map +1 -0
- package/dist/FrameGroupingShadow.js +158 -0
- package/dist/FrameGroupingShadow.js.map +1 -0
- package/dist/ImageClassifierFieldProducer.d.ts +36 -0
- package/dist/ImageClassifierFieldProducer.d.ts.map +1 -0
- package/dist/ImageClassifierFieldProducer.js +150 -0
- package/dist/ImageClassifierFieldProducer.js.map +1 -0
- package/dist/LiteRtVlmProvider.d.ts +5 -0
- package/dist/LiteRtVlmProvider.d.ts.map +1 -1
- package/dist/LiteRtVlmProvider.js +72 -7
- package/dist/LiteRtVlmProvider.js.map +1 -1
- package/dist/LocalDetectorClient.d.ts +6 -0
- package/dist/LocalDetectorClient.d.ts.map +1 -1
- package/dist/LocalDetectorClient.js +8 -0
- package/dist/LocalDetectorClient.js.map +1 -1
- package/dist/OrderedFrameReview.d.ts +57 -0
- package/dist/OrderedFrameReview.d.ts.map +1 -0
- package/dist/OrderedFrameReview.js +121 -0
- package/dist/OrderedFrameReview.js.map +1 -0
- package/dist/OrderedReviewClient.d.ts +38 -0
- package/dist/OrderedReviewClient.d.ts.map +1 -0
- package/dist/OrderedReviewClient.js +121 -0
- package/dist/OrderedReviewClient.js.map +1 -0
- package/dist/OrderedSequenceStore.d.ts +69 -0
- package/dist/OrderedSequenceStore.d.ts.map +1 -0
- package/dist/OrderedSequenceStore.js +257 -0
- package/dist/OrderedSequenceStore.js.map +1 -0
- package/dist/ProfileReviewWork.d.ts +68 -0
- package/dist/ProfileReviewWork.d.ts.map +1 -0
- package/dist/ProfileReviewWork.js +150 -0
- package/dist/ProfileReviewWork.js.map +1 -0
- package/dist/RetainedFieldContract.d.ts +21 -0
- package/dist/RetainedFieldContract.d.ts.map +1 -0
- package/dist/RetainedFieldContract.js +92 -0
- package/dist/RetainedFieldContract.js.map +1 -0
- package/dist/ReviewModelUse.d.ts +7 -0
- package/dist/ReviewModelUse.d.ts.map +1 -0
- package/dist/ReviewModelUse.js +30 -0
- package/dist/ReviewModelUse.js.map +1 -0
- package/dist/ReviewPriority.d.ts +12 -0
- package/dist/ReviewPriority.d.ts.map +1 -0
- package/dist/ReviewPriority.js +45 -0
- package/dist/ReviewPriority.js.map +1 -0
- package/dist/RuntimeModelSelection.d.ts +10 -0
- package/dist/RuntimeModelSelection.d.ts.map +1 -0
- package/dist/RuntimeModelSelection.js +58 -0
- package/dist/RuntimeModelSelection.js.map +1 -0
- package/dist/SceneChangeDetector.d.ts +1 -1
- package/dist/SceneChangeDetector.d.ts.map +1 -1
- package/dist/SceneChangeDetector.js +18 -6
- package/dist/SceneChangeDetector.js.map +1 -1
- package/dist/ScopedVlmReview.d.ts +76 -0
- package/dist/ScopedVlmReview.d.ts.map +1 -0
- package/dist/ScopedVlmReview.js +267 -0
- package/dist/ScopedVlmReview.js.map +1 -0
- package/dist/SensingProfileClient.d.ts +50 -0
- package/dist/SensingProfileClient.d.ts.map +1 -1
- package/dist/SensingProfileClient.js +58 -2
- package/dist/SensingProfileClient.js.map +1 -1
- package/dist/TemporalReviewClient.d.ts +43 -0
- package/dist/TemporalReviewClient.d.ts.map +1 -0
- package/dist/TemporalReviewClient.js +117 -0
- package/dist/TemporalReviewClient.js.map +1 -0
- package/dist/TemporalReviewJournal.d.ts +91 -0
- package/dist/TemporalReviewJournal.d.ts.map +1 -0
- package/dist/TemporalReviewJournal.js +337 -0
- package/dist/TemporalReviewJournal.js.map +1 -0
- package/dist/UrgentReviewTriage.d.ts +35 -0
- package/dist/UrgentReviewTriage.d.ts.map +1 -0
- package/dist/UrgentReviewTriage.js +52 -0
- package/dist/UrgentReviewTriage.js.map +1 -0
- package/dist/VideoSegmentUploader.d.ts +293 -0
- package/dist/VideoSegmentUploader.d.ts.map +1 -0
- package/dist/VideoSegmentUploader.js +1002 -0
- package/dist/VideoSegmentUploader.js.map +1 -0
- package/dist/VlmCandidateBuffer.d.ts +19 -0
- package/dist/VlmCandidateBuffer.d.ts.map +1 -1
- package/dist/VlmCandidateBuffer.js.map +1 -1
- package/dist/autoSense.d.ts +2 -2
- package/dist/autoSense.d.ts.map +1 -1
- package/dist/autoSense.js +16 -11
- package/dist/autoSense.js.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/continuousReview.d.ts +71 -0
- package/dist/continuousReview.d.ts.map +1 -0
- package/dist/continuousReview.js +53 -0
- package/dist/continuousReview.js.map +1 -0
- package/dist/detectorCountReview.d.ts +9 -0
- package/dist/detectorCountReview.d.ts.map +1 -0
- package/dist/detectorCountReview.js +79 -0
- package/dist/detectorCountReview.js.map +1 -0
- package/dist/diagnose.d.ts +1 -1
- package/dist/diagnose.js +1 -1
- package/dist/installerOnboardingMode.d.ts.map +1 -1
- package/dist/installerOnboardingMode.js +5 -3
- package/dist/installerOnboardingMode.js.map +1 -1
- package/dist/localInferenceSchema.d.ts +8 -0
- package/dist/localInferenceSchema.d.ts.map +1 -1
- package/dist/localInferenceSchema.js +3 -3
- package/dist/localInferenceSchema.js.map +1 -1
- package/dist/localSenseE2eHarness.d.ts +12 -0
- package/dist/localSenseE2eHarness.d.ts.map +1 -1
- package/dist/localSenseE2eHarness.js +77 -4
- package/dist/localSenseE2eHarness.js.map +1 -1
- package/dist/localSensing.d.ts +91 -18
- package/dist/localSensing.d.ts.map +1 -1
- package/dist/localSensing.js +678 -153
- package/dist/localSensing.js.map +1 -1
- package/dist/mixedScopedCounts.d.ts +17 -0
- package/dist/mixedScopedCounts.d.ts.map +1 -0
- package/dist/mixedScopedCounts.js +90 -0
- package/dist/mixedScopedCounts.js.map +1 -0
- package/dist/sensingCatchRate.d.ts +9 -5
- package/dist/sensingCatchRate.d.ts.map +1 -1
- package/dist/sensingCatchRate.js +10 -6
- package/dist/sensingCatchRate.js.map +1 -1
- package/dist/sensingRunMode.d.ts +5 -0
- package/dist/sensingRunMode.d.ts.map +1 -1
- package/dist/sensingRunMode.js +56 -0
- package/dist/sensingRunMode.js.map +1 -1
- package/package.json +7 -4
- package/retained/ArchiveLifecycle.cjs +79 -0
- package/retained/ModelStageScheduler.cjs +66 -0
- package/retained/ProfileAuthorityClient.cjs +88 -0
- package/retained/RetainedCaptureClient.cjs +164 -0
- package/retained/WaitRecheckCaptureClient.cjs +109 -0
- package/retained/__tests__/archive-lifecycle.test.cjs +117 -0
- package/retained/__tests__/runtime.test.cjs +384 -0
- package/retained/alert-rule-policy.cjs +37 -0
- package/retained/manager.cjs +133 -0
- package/retained/pilot-profile-client.cjs +82 -0
- package/retained/temporal-grouping.cjs +78 -0
- package/retained/website-api-capability.cjs +122 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
{
|
|
2
|
+
"welcome": {
|
|
3
|
+
"title": "Start on the computer that will stay here.",
|
|
4
|
+
"body": "Keep this computer powered and connected to the camera network. Have your recorder login ready.",
|
|
5
|
+
"detail": "A recorder is the box your cameras connect to, often called an NVR or DVR. Internet access alone does not mean this computer can reach it."
|
|
6
|
+
},
|
|
7
|
+
"starting": {
|
|
8
|
+
"title": "Start on the computer that will stay here.",
|
|
9
|
+
"body": "Keep this computer powered and connected to the camera network. Have your recorder login ready.",
|
|
10
|
+
"detail": "A recorder is the box your cameras connect to, often called an NVR or DVR. Internet access alone does not mean this computer can reach it."
|
|
11
|
+
},
|
|
12
|
+
"onboarding_account": {
|
|
13
|
+
"title": "Choose email or Google.",
|
|
14
|
+
"body": "Use your Sense Anything email and password, or continue with Google. New here? Create an account with either option.",
|
|
15
|
+
"detail": "Your Sense Anything login connects your account. Later, camera setup asks for the separate login for your recorder or camera."
|
|
16
|
+
},
|
|
17
|
+
"onboarding_google": {
|
|
18
|
+
"title": "Finish signing in in the setup browser tab.",
|
|
19
|
+
"body": "Keep the setup tab open, complete Google sign-in, then return to this window. Your setup stays here.",
|
|
20
|
+
"detail": "Choose the intended Google account. If the handoff does not complete, use the recovery action in this window instead of reinstalling."
|
|
21
|
+
},
|
|
22
|
+
"onboarding_choose_account": {
|
|
23
|
+
"title": "Check which account will own the cameras.",
|
|
24
|
+
"body": "Choose the account for this location before continuing. Use an account you are authorized to manage.",
|
|
25
|
+
"detail": "If you manage several businesses, check the account name. Follow any account-access notice above before proceeding."
|
|
26
|
+
},
|
|
27
|
+
"account_attention": {
|
|
28
|
+
"title": "Check which account will own the cameras.",
|
|
29
|
+
"body": "Choose the account for this location before continuing. Use an account you are authorized to manage.",
|
|
30
|
+
"detail": "If you manage several businesses, check the account name. Follow any account-access notice above before proceeding."
|
|
31
|
+
},
|
|
32
|
+
"onboarding_terms": {
|
|
33
|
+
"title": "Review the agreements before continuing.",
|
|
34
|
+
"body": "Open the agreement links and review the current terms. Accept only when you are authorized to do so.",
|
|
35
|
+
"detail": "Guiding tips are optional. Turning them off never accepts an agreement or changes a required account step."
|
|
36
|
+
},
|
|
37
|
+
"onboarding_plan": {
|
|
38
|
+
"title": "Choose the plan for this account.",
|
|
39
|
+
"body": "Check the plan and billing interval before continuing to secure Stripe checkout.",
|
|
40
|
+
"detail": "Existing subscription access is checked automatically. If payment is needed, complete checkout in the setup browser and return here."
|
|
41
|
+
},
|
|
42
|
+
"onboarding_checkout": {
|
|
43
|
+
"title": "Complete checkout in your browser.",
|
|
44
|
+
"body": "Keep the setup browser tab open. The Connector continues after subscription access is confirmed.",
|
|
45
|
+
"detail": "If you cancel checkout, account setup is preserved. Use Back to plans to retry. Avoid a second purchase while payment is still being confirmed."
|
|
46
|
+
},
|
|
47
|
+
"onboarding_install": {
|
|
48
|
+
"title": "Continue in camera setup.",
|
|
49
|
+
"body": "Keep the Connector running while you connect the recorder and check its channels in the camera setup window.",
|
|
50
|
+
"detail": "If camera setup asks for a code, use the current code shown in this Connector. An old code belongs to an earlier session."
|
|
51
|
+
},
|
|
52
|
+
"waiting_for_account": {
|
|
53
|
+
"title": "Continue in camera setup.",
|
|
54
|
+
"body": "Keep the Connector running while you connect the recorder and check its channels in the camera setup window.",
|
|
55
|
+
"detail": "If camera setup asks for a code, use the current code shown in this Connector. An old code belongs to an earlier session."
|
|
56
|
+
},
|
|
57
|
+
"account_connected": {
|
|
58
|
+
"title": "Continue in camera setup.",
|
|
59
|
+
"body": "Keep the Connector running while you connect the recorder and check its channels in the camera setup window.",
|
|
60
|
+
"detail": "If camera setup asks for a code, use the current code shown in this Connector. An old code belongs to an earlier session."
|
|
61
|
+
},
|
|
62
|
+
"discovering": {
|
|
63
|
+
"title": "Keep the recorder within reach.",
|
|
64
|
+
"body": "The Connector searches the local network. Select your recorder in camera setup and enter that device\u2019s login.",
|
|
65
|
+
"detail": "If nothing appears, check power and network connections or enter the recorder address manually. Guest Wi-Fi and isolated camera ports can prevent discovery."
|
|
66
|
+
},
|
|
67
|
+
"devices_found": {
|
|
68
|
+
"title": "Keep the recorder within reach.",
|
|
69
|
+
"body": "The Connector searches the local network. Select your recorder in camera setup and enter that device\u2019s login.",
|
|
70
|
+
"detail": "If nothing appears, check power and network connections or enter the recorder address manually. Guest Wi-Fi and isolated camera ports can prevent discovery."
|
|
71
|
+
},
|
|
72
|
+
"validating": {
|
|
73
|
+
"title": "Check each camera before adding it.",
|
|
74
|
+
"body": "Keep setup open while every channel is checked. Only validated feeds can be selected and added.",
|
|
75
|
+
"detail": "Empty, black or unreadable channels are left out. Check a missing camera\u2019s power, cable, permissions and stream settings, then retry in camera setup."
|
|
76
|
+
},
|
|
77
|
+
"cameras_created": {
|
|
78
|
+
"title": "Keep this computer awake while we finish.",
|
|
79
|
+
"body": "Camera pairing is saved. Let the local model and sensing service checks finish before closing setup.",
|
|
80
|
+
"detail": "The local model runs on this computer. If local AI needs attention, retry that step; you do not need to repeat camera pairing."
|
|
81
|
+
},
|
|
82
|
+
"local_ai": {
|
|
83
|
+
"title": "Keep this computer awake while we finish.",
|
|
84
|
+
"body": "Camera pairing is saved. Let the local model and sensing service checks finish before closing setup.",
|
|
85
|
+
"detail": "The local model runs on this computer. If local AI needs attention, retry that step; you do not need to repeat camera pairing."
|
|
86
|
+
},
|
|
87
|
+
"starting_service": {
|
|
88
|
+
"title": "Keep this computer awake while we finish.",
|
|
89
|
+
"body": "Camera pairing is saved. Let the local model and sensing service checks finish before closing setup.",
|
|
90
|
+
"detail": "The local model runs on this computer. If local AI needs attention, retry that step; you do not need to repeat camera pairing."
|
|
91
|
+
},
|
|
92
|
+
"local_ai_error": {
|
|
93
|
+
"title": "Retry local AI without pairing again.",
|
|
94
|
+
"body": "Your camera pairing is saved. Use Retry local AI to prepare the local model and service again.",
|
|
95
|
+
"detail": "Keep the computer powered and check its available disk space. Follow the error above. If the retry fails, contact support with the setup error, without camera credentials."
|
|
96
|
+
},
|
|
97
|
+
"ready": {
|
|
98
|
+
"title": "Leave this computer powered and connected.",
|
|
99
|
+
"body": "Sense Anything starts automatically when this computer logs in. Use View cameras to check sensing status.",
|
|
100
|
+
"detail": "Sleep, power loss or a disconnected camera network can interrupt sensing. Reopen Sense Anything Connector to resume or repair setup."
|
|
101
|
+
},
|
|
102
|
+
"error": {
|
|
103
|
+
"title": "Follow the setup notice above.",
|
|
104
|
+
"body": "Use the recovery action shown in this window. Keep your account and recorder credentials private.",
|
|
105
|
+
"detail": "If the problem continues, contact support with the visible error. Do not share passwords or camera stream links in screenshots."
|
|
106
|
+
},
|
|
107
|
+
"update_required": {
|
|
108
|
+
"title": "Follow the setup notice above.",
|
|
109
|
+
"body": "Use the recovery action shown in this window. Keep your account and recorder credentials private.",
|
|
110
|
+
"detail": "If the problem continues, contact support with the visible error. Do not share passwords or camera stream links in screenshots."
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -93,7 +93,7 @@ SOURCE="$(find_detector_source)" || { log "ERROR: detector package source not fo
|
|
|
93
93
|
# re-downloads the pinned ~250 MB onnxruntime wheel on a shop PC every time, for
|
|
94
94
|
# nothing — an exact pin cannot resolve to different bytes.
|
|
95
95
|
log "installing/repairing trillboards-detector from $SOURCE with uv-managed Python $DETECTOR_PYTHON"
|
|
96
|
-
if ! "$UV" tool install --python "$DETECTOR_PYTHON" --managed-python --force --refresh-package trillboards-detector "$SOURCE" >>"$LOG" 2>&1; then
|
|
96
|
+
if ! "$UV" tool install --python "$DETECTOR_PYTHON" --managed-python --force --refresh-package trillboards-detector "${SOURCE}[cpu]" >>"$LOG" 2>&1; then
|
|
97
97
|
log "ERROR: trillboards-detector install failed; the cascade stays off"
|
|
98
98
|
exit 0
|
|
99
99
|
fi
|
|
@@ -126,7 +126,7 @@ elif [ "$HAVE_VERSION" != "$WANT_VERSION" ]; then
|
|
|
126
126
|
# asking it again the same way is not a repair.
|
|
127
127
|
log "WARN: the installed detector reports '$HAVE_VERSION' but the staged package is '$WANT_VERSION' - reinstalling from scratch"
|
|
128
128
|
"$UV" tool uninstall trillboards-detector >>"$LOG" 2>&1 || true
|
|
129
|
-
"$UV" tool install --python "$DETECTOR_PYTHON" --managed-python --force --refresh-package trillboards-detector "$SOURCE" >>"$LOG" 2>&1 || true
|
|
129
|
+
"$UV" tool install --python "$DETECTOR_PYTHON" --managed-python --force --refresh-package trillboards-detector "${SOURCE}[cpu]" >>"$LOG" 2>&1 || true
|
|
130
130
|
DETECTOR="$LOCAL_BIN/trillboards-detector"
|
|
131
131
|
[ -x "$DETECTOR" ] || DETECTOR="$(command -v trillboards-detector || echo "$DETECTOR")"
|
|
132
132
|
HAVE_VERSION="$(installed_detector_version)"
|
|
@@ -37,6 +37,9 @@ LOG_DIR="${TRILLBOARDS_LOG_DIR:-/var/log/trillboards}"
|
|
|
37
37
|
LOCAL_BIN="$USER_HOME/.local/bin"
|
|
38
38
|
MODEL_ID="gemma-4-E2B-it.litertlm"
|
|
39
39
|
HF_REPO="litert-community/gemma-4-E2B-it-litert-lm"
|
|
40
|
+
MODEL_REVISION="84b6978eff6e4eea02825bc2ee4ea48579f13109"
|
|
41
|
+
APPROVED_MODEL_SHA256="ab7838cdfc8f77e54d8ca45eadceb20452d9f01e4bfade03e5dce27911b27e42"
|
|
42
|
+
MODEL_URL="https://huggingface.co/$HF_REPO/resolve/$MODEL_REVISION/$MODEL_ID"
|
|
40
43
|
SERVE_PORT="9379"
|
|
41
44
|
SERVE_UNIT="trillboards-litertlm"
|
|
42
45
|
LITERT_PYTHON="${LITERT_LM_PYTHON:-3.12}"
|
|
@@ -253,16 +256,29 @@ install_litertlm_shim() {
|
|
|
253
256
|
# Deliberately NOT a /v1/models check: that answers 200 without ever building an
|
|
254
257
|
# engine, so it cannot tell a working GPU encoder from one that fails on first
|
|
255
258
|
# use.
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
259
|
+
verify_litertlm_probe() {
|
|
260
|
+
local requested="$1" body response code tool_dir py verifier candidate valid=1
|
|
261
|
+
body="$LITERT_DIR/.identity-probe.json"
|
|
262
|
+
response="$LITERT_DIR/.identity-response.json"
|
|
263
|
+
printf '{"model":"%s","messages":[{"role":"user","content":[{"type":"text","text":"Reply with the single word: ok"},{"type":"image_url","image_url":{"url":"data:image/jpeg;base64,%s"}}]}],"max_completion_tokens":8,"temperature":0}' \
|
|
264
|
+
"$requested" "$LITERTLM_PROBE_JPEG_B64" > "$body" 2>/dev/null || return 1
|
|
265
|
+
code=$(curl -s -o "$response" -w '%{http_code}' --max-time 180 \
|
|
261
266
|
-X POST "http://127.0.0.1:$SERVE_PORT/v1/chat/completions" \
|
|
262
|
-
-H 'Content-Type: application/json'
|
|
263
|
-
|
|
264
|
-
[ "$
|
|
267
|
+
-H 'Content-Type: application/json' -H "X-Trillboards-Model-Sha256: $APPROVED_MODEL_SHA256" \
|
|
268
|
+
--data-binary @"$body" 2>/dev/null)
|
|
269
|
+
if [ "$code" = "200" ]; then
|
|
270
|
+
tool_dir="$("${UV:-$LOCAL_BIN/uv}" tool dir 2>/dev/null | tr -d '\r')"
|
|
271
|
+
py="$tool_dir/litert-lm/bin/python"
|
|
272
|
+
for candidate in "$SCRIPT_DIR/verify_model_proof.py" "$SCRIPT_DIR/../litertlm/verify_model_proof.py" "$SCRIPT_DIR/../../litertlm/verify_model_proof.py"; do
|
|
273
|
+
[ -f "$candidate" ] && { verifier="$candidate"; break; }
|
|
274
|
+
done
|
|
275
|
+
[ -n "${verifier:-}" ] && [ -x "$py" ] \
|
|
276
|
+
&& "$py" "$verifier" "$response" "$requested" "$APPROVED_MODEL_SHA256" && valid=0
|
|
277
|
+
fi
|
|
278
|
+
rm -f "$body" "$response" 2>/dev/null || true
|
|
279
|
+
return "$valid"
|
|
265
280
|
}
|
|
281
|
+
verify_litertlm_vision() { verify_litertlm_probe "$MODEL_ID,gpu"; }
|
|
266
282
|
|
|
267
283
|
# The same probe, but asking for NO delegate suffix rather than `,gpu`.
|
|
268
284
|
#
|
|
@@ -273,16 +289,7 @@ verify_litertlm_vision() {
|
|
|
273
289
|
# carries no suffix at all, so a 200 means the box built an engine and ran a real
|
|
274
290
|
# image through it under the config now on disk, which is exactly the claim each
|
|
275
291
|
# rung needs.
|
|
276
|
-
verify_litertlm_plain_inference() {
|
|
277
|
-
_body="$LITERT_DIR/.plain-probe.json"
|
|
278
|
-
printf '{"model":"%s","messages":[{"role":"user","content":[{"type":"text","text":"Reply with the single word: ok"},{"type":"image_url","image_url":{"url":"data:image/jpeg;base64,%s"}}]}],"max_completion_tokens":8,"temperature":0}' \
|
|
279
|
-
"$MODEL_ID" "$LITERTLM_PROBE_JPEG_B64" > "$_body" 2>/dev/null || return 1
|
|
280
|
-
_code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 180 \
|
|
281
|
-
-X POST "http://127.0.0.1:$SERVE_PORT/v1/chat/completions" \
|
|
282
|
-
-H 'Content-Type: application/json' --data-binary @"$_body" 2>/dev/null)
|
|
283
|
-
rm -f "$_body" 2>/dev/null || true
|
|
284
|
-
[ "$_code" = "200" ]
|
|
285
|
-
}
|
|
292
|
+
verify_litertlm_plain_inference() { verify_litertlm_probe "$MODEL_ID"; }
|
|
286
293
|
|
|
287
294
|
local_ai_healthy() {
|
|
288
295
|
curl -fsS --max-time 3 "http://127.0.0.1:$SERVE_PORT/v1/models" 2>/dev/null \
|
|
@@ -362,20 +369,37 @@ fi
|
|
|
362
369
|
|
|
363
370
|
install_litertlm_shim || true
|
|
364
371
|
|
|
365
|
-
# 3. Model
|
|
372
|
+
# 3. Model: a matching name is not a matching artifact. Upgrade from the immutable
|
|
373
|
+
# revision, preserve the old bytes, and verify the installed copy.
|
|
366
374
|
HAVE_MODEL=0
|
|
367
|
-
|
|
368
|
-
|
|
375
|
+
INSTALLED_SHA="$(sha256sum "$STORE_DIR/model.litertlm" 2>/dev/null | awk '{print $1}')"
|
|
376
|
+
if [ "$INSTALLED_SHA" = "$APPROVED_MODEL_SHA256" ]; then
|
|
377
|
+
log "approved model $MODEL_ID already present — skipping import"
|
|
369
378
|
HAVE_MODEL=1
|
|
370
379
|
else
|
|
371
380
|
[ "$ALLOW_MODEL_DOWNLOAD" = "1" ] || fail "local AI model is missing and the network import is disabled"
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
381
|
+
mkdir -p "$STORE_DIR" || fail "model store is unavailable"
|
|
382
|
+
DOWNLOAD="$(mktemp "$STORE_DIR/approved-model.XXXXXX")" || fail "could not create a model download file"
|
|
383
|
+
log "downloading approved $MODEL_ID from immutable revision $MODEL_REVISION"
|
|
384
|
+
if ! curl -fL --retry 3 --output "$DOWNLOAD" "$MODEL_URL" >>"$LOG" 2>&1 \
|
|
385
|
+
|| [ "$(sha256sum "$DOWNLOAD" | awk '{print $1}')" != "$APPROVED_MODEL_SHA256" ]; then
|
|
386
|
+
rm -f "$DOWNLOAD"
|
|
387
|
+
fail "approved model download/checksum failed"
|
|
388
|
+
fi
|
|
389
|
+
if [ -n "$INSTALLED_SHA" ]; then
|
|
390
|
+
ROLLBACK="$STORE_DIR/rollback/$INSTALLED_SHA.litertlm"
|
|
391
|
+
mkdir -p "$STORE_DIR/rollback" || fail "could not preserve previous model"
|
|
392
|
+
[ -f "$ROLLBACK" ] || cp "$STORE_DIR/model.litertlm" "$ROLLBACK" || fail "could not preserve previous model"
|
|
393
|
+
[ "$(sha256sum "$ROLLBACK" | awk '{print $1}')" = "$INSTALLED_SHA" ] || fail "previous model backup verification failed"
|
|
378
394
|
fi
|
|
395
|
+
if ! "$LITERTLM" import "$DOWNLOAD" "$MODEL_ID" >>"$LOG" 2>&1 \
|
|
396
|
+
|| [ "$(sha256sum "$STORE_DIR/model.litertlm" | awk '{print $1}')" != "$APPROVED_MODEL_SHA256" ]; then
|
|
397
|
+
rm -f "$DOWNLOAD"
|
|
398
|
+
fail "approved model import verification failed — the connector will WAIT until this succeeds"
|
|
399
|
+
fi
|
|
400
|
+
rm -f "$DOWNLOAD"
|
|
401
|
+
HAVE_MODEL=1
|
|
402
|
+
log "model imported from verified immutable revision"
|
|
379
403
|
fi
|
|
380
404
|
|
|
381
405
|
# Record the installed version next to the store model so the connector's
|
|
@@ -16,11 +16,11 @@
|
|
|
16
16
|
bin\trillboards-pair.cmd first-run claim-code pairing helper
|
|
17
17
|
lib\edge-sdk\dist\cli.js the connector CLI
|
|
18
18
|
lib\node_modules\... runtime subset (edge-core + edge-platform-rtsp +
|
|
19
|
-
edge-sdk + transitive deps
|
|
20
|
-
local ONNX/
|
|
19
|
+
edge-sdk + transitive deps, including Sharp for
|
|
20
|
+
focused image masks), WITHOUT local ONNX/sqlite.
|
|
21
21
|
|
|
22
|
-
The connector is loaded WITHOUT the heavy ONNX/
|
|
23
|
-
lazy-requires EdgeAgent), so the bundle omits onnxruntime-node/
|
|
22
|
+
The connector is loaded WITHOUT the heavy ONNX/sqlite stack (cli.ts
|
|
23
|
+
lazy-requires EdgeAgent), so the bundle omits onnxruntime-node/
|
|
24
24
|
better-sqlite3 — keeping the inner .exe small. The release workflow signs this
|
|
25
25
|
connector installer, then bundle-windows-installer.ps1 wraps it with the
|
|
26
26
|
pinned Gemma model in one customer-facing executable. The inner installer
|
|
@@ -107,6 +107,7 @@ Push-Location $WorkspaceRoot; npm run build; if ($LASTEXITCODE -ne 0) { Die "edg
|
|
|
107
107
|
Log "Staging edge-sdk dist…"
|
|
108
108
|
New-Item -ItemType Directory -Force -Path "$Payload\lib\edge-sdk" | Out-Null
|
|
109
109
|
Copy-Item -Recurse -Force "$PkgRoot\dist" "$Payload\lib\edge-sdk\dist"
|
|
110
|
+
Copy-Item -Recurse -Force "$PkgRoot\retained" "$Payload\lib\edge-sdk\retained"
|
|
110
111
|
Copy-Item -Force "$PkgRoot\package.json" "$Payload\lib\edge-sdk\package.json"
|
|
111
112
|
|
|
112
113
|
# ─── 3. Runtime node_modules subset (NO heavy native stack) ────────────────────
|
|
@@ -123,9 +124,11 @@ Copy-Item -Force "$PkgRoot\package.json" "$Payload\lib\edge-sdk\package.json"
|
|
|
123
124
|
# the job was killed at its timeout on EVERY release run, so the .exe never
|
|
124
125
|
# published. /MT parallelises the small-file copy; /R:1 /W:1 bound retries so a
|
|
125
126
|
# single transiently-locked file can't trigger robocopy's default 1e6×30s hang.
|
|
126
|
-
Log "Staging runtime node_modules (excluding ONNX/
|
|
127
|
+
Log "Staging runtime node_modules (including focused-image masks; excluding ONNX/sqlite/dev)…"
|
|
127
128
|
New-Item -ItemType Directory -Force -Path "$Payload\lib\node_modules" | Out-Null
|
|
128
|
-
|
|
129
|
+
# Focused retained reviews use Sharp to mask pixels, not just prompt geometry.
|
|
130
|
+
# Keep its platform-native dependencies; detectors still use the local HTTP service.
|
|
131
|
+
$xd = @("onnxruntime-node","better-sqlite3","ffmpeg-static",".cache",".bin","typescript","jest","jest-cli","@jest","ts-jest","@types")
|
|
129
132
|
$rcArgs = @("$WorkspaceRoot\node_modules", "$Payload\lib\node_modules", "/E", "/XJ", "/MT:16", "/R:1", "/W:1", "/NFL","/NDL","/NJH","/NJS","/NP")
|
|
130
133
|
foreach ($d in $xd) { $rcArgs += @("/XD", $d) }
|
|
131
134
|
robocopy @rcArgs | Out-Null
|
|
@@ -141,6 +144,7 @@ foreach ($p in @("edge-core","edge-sensing","edge-ads","edge-federated","edge-cl
|
|
|
141
144
|
if (Test-Path $dest) { Remove-Item -Recurse -Force $dest }
|
|
142
145
|
New-Item -ItemType Directory -Force -Path $dest | Out-Null
|
|
143
146
|
if (Test-Path "$src\dist") { Copy-Item -Recurse -Force "$src\dist" "$dest\dist" }
|
|
147
|
+
if ($p -eq 'edge-sdk') { Copy-Item -Recurse -Force "$src\retained" "$dest\retained" }
|
|
144
148
|
Copy-Item -Force "$src\package.json" "$dest\package.json"
|
|
145
149
|
}
|
|
146
150
|
|
|
@@ -193,6 +197,7 @@ Log "Bundled ffmpeg OK"
|
|
|
193
197
|
# PATH=bin first so the connector's default ffmpeg='ffmpeg' resolves to OURS.
|
|
194
198
|
Copy-Item -Force "$Here\trillboards-edge.cmd" "$Payload\bin\trillboards-edge.cmd"
|
|
195
199
|
Copy-Item -Force "$Here\trillboards-pair.cmd" "$Payload\bin\trillboards-pair.cmd"
|
|
200
|
+
Copy-Item -Force "$Here\..\setup-guidance.json" "$Payload\bin\setup-guidance.json"
|
|
196
201
|
Copy-Item -Force "$Here\trillboards-setup.ps1" "$Payload\bin\trillboards-setup.ps1"
|
|
197
202
|
Copy-Item -Force "$Here\trillboards-setup.vbs" "$Payload\bin\trillboards-setup.vbs"
|
|
198
203
|
Copy-Item -Force "$Here\trillboards-edge-task.vbs" "$Payload\bin\trillboards-edge-task.vbs"
|
|
@@ -15,8 +15,8 @@ param(
|
|
|
15
15
|
[Parameter(Mandatory = $true)][string]$InnerInstaller,
|
|
16
16
|
[string]$OutputPath = "",
|
|
17
17
|
[string]$ModelPath = "",
|
|
18
|
-
[string]$ModelUrl = "https://huggingface.co/litert-community/gemma-4-E2B-it-litert-lm/resolve/
|
|
19
|
-
[string]$ModelSha256 = "
|
|
18
|
+
[string]$ModelUrl = "https://huggingface.co/litert-community/gemma-4-E2B-it-litert-lm/resolve/84b6978eff6e4eea02825bc2ee4ea48579f13109/gemma-4-E2B-it.litertlm",
|
|
19
|
+
[string]$ModelSha256 = "ab7838cdfc8f77e54d8ca45eadceb20452d9f01e4bfade03e5dce27911b27e42",
|
|
20
20
|
[string]$SevenZipSdkUrl = "https://www.7-zip.org/a/lzma2602.7z",
|
|
21
21
|
[string]$SevenZipSdkSha256 = "2878c85f5f43a4a4e0952b1fd4e5fe097c1c143997a8047c7e1e788892aa9357",
|
|
22
22
|
[string]$IconPath = ""
|
|
@@ -87,7 +87,7 @@ if (-not $sfxModule) { Fail "official 7zSD.sfx installer module was not found" }
|
|
|
87
87
|
# Brand the executable module before appending the config and archive. This
|
|
88
88
|
# keeps the SFX overlay byte-for-byte intact and the final Authenticode signing
|
|
89
89
|
# step covers both the application icon and the customer payload.
|
|
90
|
-
& (Join-Path $Here "set-executable-icon.ps1") -ExecutablePath $sfxModule.FullName -IconPath $IconPath
|
|
90
|
+
& (Join-Path $Here "set-executable-icon.ps1") -ExecutablePath $sfxModule.FullName -IconPath $IconPath -EnsurePerUserExecution
|
|
91
91
|
|
|
92
92
|
$innerName = "TrillboardsEdgeSetup-core.exe"
|
|
93
93
|
Copy-Item -LiteralPath $InnerInstaller -Destination (Join-Path $Stage $innerName) -Force
|
|
@@ -12,11 +12,12 @@
|
|
|
12
12
|
wiring (DEFAULT, runs on EVERY CI event incl. PRs):
|
|
13
13
|
Builds the connector, then runs the cross-platform Node wiring runner
|
|
14
14
|
(scripts/run-local-sense-wiring-e2e.mjs) which drives the REAL built
|
|
15
|
-
`cli.js local-sense` against
|
|
16
|
-
|
|
17
|
-
fixture through
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
`cli.js local-sense` against STUB local VLM/detector services and an
|
|
16
|
+
authenticated retained capability/profile/ingest API, feeding the committed
|
|
17
|
+
fixture through real RTSP/ffmpeg. Checks declared field ownership, pinned
|
|
18
|
+
requests and original evidence/hash/capture time with the genuine retained
|
|
19
|
+
protocol parser/acknowledgement, WITHOUT downloading the real models.
|
|
20
|
+
This is a STUB-MODEL wiring proof — NOT an inference-accuracy proof.
|
|
20
21
|
|
|
21
22
|
bench (workflow_dispatch only):
|
|
22
23
|
Provisions exactly as 'real' does, then runs scripts/bench-local-vlm.mjs
|
|
@@ -56,12 +56,10 @@ function Register-Connector([string]$Root) {
|
|
|
56
56
|
2026-08-19 Windows BOM incident, and it is why this is here rather than left
|
|
57
57
|
to the repair path.
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
update
|
|
62
|
-
|
|
63
|
-
register-detector-task.ps1 already stops the running service, replaces the
|
|
64
|
-
action and starts it again - so this both migrates and restarts it.
|
|
59
|
+
Re-register when the runtime source stamp matches. When it differs, run the
|
|
60
|
+
owned provisioner so a healthy old CPU-only service cannot survive a GPU
|
|
61
|
+
update indefinitely. The provisioner serializes replacement, verifies its
|
|
62
|
+
installed stamp and chooses one CPU/CUDA dependency set. Pairings stay intact.
|
|
65
63
|
|
|
66
64
|
BEST EFFORT, ALWAYS. The detector is a cost optimisation: without it every
|
|
67
65
|
frame goes to the VLM exactly as it does today. It must never be able to fail
|
|
@@ -77,6 +75,47 @@ function Update-DetectorTask([string]$Root) {
|
|
|
77
75
|
# register-task.ps1 states for its own repair.
|
|
78
76
|
$exe = ($task.Actions | Select-Object -First 1).Execute
|
|
79
77
|
if (-not $exe) { return }
|
|
78
|
+
# Older updater array-splatting bound the text '-DetectorPath' positionally
|
|
79
|
+
# as the executable, shifting the real path into --port. Recover ONLY this
|
|
80
|
+
# known shape, under the current user's SID and exact local tool path.
|
|
81
|
+
if ($exe -eq '-DetectorPath') {
|
|
82
|
+
$taskUser = [string]$task.Principal.UserId
|
|
83
|
+
$taskSid = if ($taskUser -match '^S-1-') { $taskUser } else {
|
|
84
|
+
(New-Object System.Security.Principal.NTAccount($taskUser)).Translate([System.Security.Principal.SecurityIdentifier]).Value
|
|
85
|
+
}
|
|
86
|
+
$currentSid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value
|
|
87
|
+
$oldArgs = [string]($task.Actions | Select-Object -First 1).Arguments
|
|
88
|
+
$ownTool = Join-Path $env:USERPROFILE '.local\bin\trillboards-detector.exe'
|
|
89
|
+
if ($taskSid -ne $currentSid -or $oldArgs -notmatch '^serve --host 127\.0\.0\.1 --port (.+?) --log-file "-RamBudgetMb" --ram-budget-mb \d+$') {
|
|
90
|
+
throw 'Cannot verify ownership of the malformed detector task'
|
|
91
|
+
}
|
|
92
|
+
$recovered = $Matches[1]
|
|
93
|
+
if ([IO.Path]::GetFullPath($recovered) -ine [IO.Path]::GetFullPath($ownTool) -or -not (Test-Path -LiteralPath $ownTool)) {
|
|
94
|
+
throw 'Malformed detector task does not identify this user installation'
|
|
95
|
+
}
|
|
96
|
+
$exe = $ownTool
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
$detectorSource = Join-Path $Root 'bin\detector'
|
|
100
|
+
$project = Join-Path $detectorSource 'pyproject.toml'
|
|
101
|
+
$setup = Join-Path $Root 'bin\setup-detector.ps1'
|
|
102
|
+
if ((Test-Path -LiteralPath $project) -and (Test-Path -LiteralPath $setup)) {
|
|
103
|
+
$versionLine = @(Get-Content -LiteralPath $project | Where-Object { $_ -match '^version\s*=\s*"([^"]+)"\s*$' })
|
|
104
|
+
if ($versionLine.Count -ne 1 -or $versionLine[0] -notmatch '^version\s*=\s*"([^"]+)"\s*$') {
|
|
105
|
+
throw 'Cannot resolve the staged detector source version'
|
|
106
|
+
}
|
|
107
|
+
$stagedDetectorVersion = $Matches[1]
|
|
108
|
+
$installedDetectorVersion = ((& $exe --version 2>$null | Out-String).Trim() -split '\s+')[-1]
|
|
109
|
+
if ($LASTEXITCODE -ne 0 -or $installedDetectorVersion -ne $stagedDetectorVersion) {
|
|
110
|
+
Write-Host "Updating the managed detector runtime to $stagedDetectorVersion."
|
|
111
|
+
& $setup -SourcePath $detectorSource
|
|
112
|
+
$verifiedDetectorVersion = ((& $exe --version 2>$null | Out-String).Trim() -split '\s+')[-1]
|
|
113
|
+
if ($LASTEXITCODE -ne 0 -or $verifiedDetectorVersion -ne $stagedDetectorVersion) {
|
|
114
|
+
throw 'Detector runtime update did not verify; pairing preserved, local detector needs repair'
|
|
115
|
+
}
|
|
116
|
+
return # the owned provisioner already registered and restarted the task
|
|
117
|
+
}
|
|
118
|
+
}
|
|
80
119
|
|
|
81
120
|
$register = Join-Path $Root "bin\register-detector-task.ps1"
|
|
82
121
|
if (-not (Test-Path -LiteralPath $register)) { return }
|
|
@@ -93,12 +132,16 @@ function Update-DetectorTask([string]$Root) {
|
|
|
93
132
|
if ($LASTEXITCODE -eq 0 -and $derived -match '^\d+$') { $budget = $derived }
|
|
94
133
|
}
|
|
95
134
|
|
|
96
|
-
|
|
97
|
-
|
|
135
|
+
# A PowerShell SCRIPT takes named parameters from a hashtable. An array of
|
|
136
|
+
# flag strings is positional; it creates Execute='-DetectorPath' while
|
|
137
|
+
# reporting registration success. Native powershell.exe argument arrays
|
|
138
|
+
# elsewhere are a different boundary and must not be changed with this.
|
|
139
|
+
$registerArgs = @{ DetectorPath = $exe }
|
|
140
|
+
if ($budget) { $registerArgs.RamBudgetMb = $budget }
|
|
98
141
|
& $register @registerArgs | Out-Null
|
|
99
142
|
Write-Host "Detector task migrated$(if ($budget) { " (RAM ceiling $budget MiB)" })."
|
|
100
143
|
} catch {
|
|
101
|
-
Write-Warning "Could not migrate the detector task: $($_.Exception.Message).
|
|
144
|
+
Write-Warning "Could not migrate the detector task: $($_.Exception.Message). Detector-backed profiles need the local detector repaired; camera pairing is preserved."
|
|
102
145
|
}
|
|
103
146
|
}
|
|
104
147
|
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
<#
|
|
2
2
|
.SYNOPSIS
|
|
3
|
-
|
|
3
|
+
Updates installer icons and optionally declares per-user execution.
|
|
4
4
|
|
|
5
5
|
.DESCRIPTION
|
|
6
6
|
The customer installer uses the official 7-Zip SFX module, whose generic
|
|
7
7
|
icon would otherwise become the icon customers see in Explorer. This helper
|
|
8
8
|
updates that module before the installer payload is appended and signed.
|
|
9
|
+
EnsurePerUserExecution adds an asInvoker manifest to avoid Windows installer
|
|
10
|
+
heuristics requesting elevation for this per-user connector wrapper.
|
|
9
11
|
#>
|
|
10
12
|
[CmdletBinding()]
|
|
11
13
|
param(
|
|
12
14
|
[Parameter(Mandatory = $true)][string]$ExecutablePath,
|
|
13
|
-
[Parameter(Mandatory = $true)][string]$IconPath
|
|
15
|
+
[Parameter(Mandatory = $true)][string]$IconPath,
|
|
16
|
+
[switch]$EnsurePerUserExecution
|
|
14
17
|
)
|
|
15
18
|
|
|
16
19
|
$ErrorActionPreference = "Stop"
|
|
@@ -28,6 +31,7 @@ using System.Collections.Generic;
|
|
|
28
31
|
using System.ComponentModel;
|
|
29
32
|
using System.IO;
|
|
30
33
|
using System.Runtime.InteropServices;
|
|
34
|
+
using System.Text;
|
|
31
35
|
|
|
32
36
|
namespace SenseAnything.Build
|
|
33
37
|
{
|
|
@@ -140,7 +144,7 @@ namespace SenseAnything.Build
|
|
|
140
144
|
IntPtr update,
|
|
141
145
|
[MarshalAs(UnmanagedType.Bool)] bool discard);
|
|
142
146
|
|
|
143
|
-
public static void Replace(string executablePath, string iconPath)
|
|
147
|
+
public static void Replace(string executablePath, string iconPath, bool ensurePerUserExecution)
|
|
144
148
|
{
|
|
145
149
|
List<IconImage> images = ReadIcon(iconPath);
|
|
146
150
|
List<ResourceGroup> groups = ReadIconGroups(executablePath);
|
|
@@ -210,7 +214,22 @@ namespace SenseAnything.Build
|
|
|
210
214
|
}
|
|
211
215
|
}
|
|
212
216
|
|
|
213
|
-
|
|
217
|
+
if (ensurePerUserExecution)
|
|
218
|
+
{
|
|
219
|
+
// The official SFX module has no execution-level manifest.
|
|
220
|
+
// Declare the same per-user level as the inner NSIS package;
|
|
221
|
+
// do not let installer heuristics request elevation instead.
|
|
222
|
+
byte[] manifest = Encoding.UTF8.GetBytes(@"<?xml version=""1.0"" encoding=""UTF-8""?>
|
|
223
|
+
<assembly xmlns=""urn:schemas-microsoft-com:asm.v1"" manifestVersion=""1.0"">
|
|
224
|
+
<assemblyIdentity name=""Trillboards.SenseAnything.Connector"" version=""1.0.0.0"" type=""win32""/>
|
|
225
|
+
<trustInfo xmlns=""urn:schemas-microsoft-com:asm.v3""><security><requestedPrivileges>
|
|
226
|
+
<requestedExecutionLevel level=""asInvoker"" uiAccess=""false""/>
|
|
227
|
+
</requestedPrivileges></security></trustInfo>
|
|
228
|
+
</assembly>");
|
|
229
|
+
Require(UpdateResourceById(update, MakeResource(24), MakeResource(1), 0,
|
|
230
|
+
manifest, (uint)manifest.Length), "Could not write the per-user execution manifest");
|
|
231
|
+
}
|
|
232
|
+
Require(EndUpdateResourceW(update, false), "Could not commit the executable resources");
|
|
214
233
|
committed = true;
|
|
215
234
|
}
|
|
216
235
|
finally
|
|
@@ -350,5 +369,5 @@ namespace SenseAnything.Build
|
|
|
350
369
|
}
|
|
351
370
|
'@
|
|
352
371
|
|
|
353
|
-
[SenseAnything.Build.ExecutableIcon]::Replace($ExecutablePath, $IconPath)
|
|
372
|
+
[SenseAnything.Build.ExecutableIcon]::Replace($ExecutablePath, $IconPath, $EnsurePerUserExecution.IsPresent)
|
|
354
373
|
Write-Host "[icon] applied Sense Anything icon to $ExecutablePath" -ForegroundColor Green
|