@trillboards/edge-sdk 0.2.136 → 0.2.138
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/deploy/macos/README.md +21 -1
- package/deploy/macos/TrillboardsSetupApp.m +206 -10
- package/deploy/macos/build-macos-pkg.sh +4 -2
- package/deploy/macos/pair.command +3 -1
- package/dist/EdgeAgent.js +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +68 -14
- package/dist/cli.js.map +1 -1
- package/dist/diagnose.d.ts +1 -1
- package/dist/diagnose.js +1 -1
- package/dist/installerBrowserSession.d.ts +21 -0
- package/dist/installerBrowserSession.d.ts.map +1 -0
- package/dist/installerBrowserSession.js +128 -0
- package/dist/installerBrowserSession.js.map +1 -0
- package/dist/installerCameraHandoff.d.ts +14 -0
- package/dist/installerCameraHandoff.d.ts.map +1 -0
- package/dist/installerCameraHandoff.js +28 -0
- package/dist/installerCameraHandoff.js.map +1 -0
- package/dist/installerOnboardingMode.d.ts +2 -0
- package/dist/installerOnboardingMode.d.ts.map +1 -1
- package/dist/installerOnboardingMode.js +25 -4
- package/dist/installerOnboardingMode.js.map +1 -1
- package/dist/setupMode.d.ts +2 -0
- package/dist/setupMode.d.ts.map +1 -1
- package/dist/setupMode.js +25 -5
- package/dist/setupMode.js.map +1 -1
- package/package.json +1 -1
package/deploy/macos/README.md
CHANGED
|
@@ -49,7 +49,12 @@ delivered **only by pairing** — it is never baked into the `.pkg`.
|
|
|
49
49
|
**Start setup**. Follow **Welcome → Account → Account setup → Plan & Payment**.
|
|
50
50
|
Sign in with Google or email, choose the account if you manage more than one,
|
|
51
51
|
accept the current agreements, then choose a plan
|
|
52
|
-
and finish secure Stripe checkout.
|
|
52
|
+
and finish secure Stripe checkout. The first browser step opens a **Sense Anything
|
|
53
|
+
setup** tab in the system default browser. Keep it open: Google and checkout
|
|
54
|
+
links originate from this tab so they stay in that browser/profile. **Reopen
|
|
55
|
+
setup tab** is an explicit recovery through the current default browser; it
|
|
56
|
+
revokes the old tab's connection. Agreement review and the authenticated
|
|
57
|
+
recorder/camera wizard open in owned Connector windows on the Mac. Cancelling checkout preserves account setup;
|
|
53
58
|
use **Back to plans** in the Connector to retry. Existing subscribed accounts
|
|
54
59
|
resume at recorder setup. The Connector checks subscription access automatically
|
|
55
60
|
before opening the signed-in Sense Anything camera wizard,
|
|
@@ -180,3 +185,18 @@ runs the local model:
|
|
|
180
185
|
The pairing endpoint (`POST /v2/earner/cameras/pair`), the config shape, and the
|
|
181
186
|
local-first sensing runtime are shared across all platforms — only the
|
|
182
187
|
service-supervisor wrapper differs.
|
|
188
|
+
|
|
189
|
+
### Browser continuity implementation
|
|
190
|
+
|
|
191
|
+
`installerBrowserSession.ts` serves one loopback-only setup tab. Its capability
|
|
192
|
+
is carried in the initial fragment and tab session storage, then authenticated
|
|
193
|
+
in request headers. Host/origin checks reject rebinding and cross-origin access;
|
|
194
|
+
only one tab owner receives pending actions. Later stages never use the OS opener
|
|
195
|
+
unless the user explicitly requests recovery. Links use a user gesture and
|
|
196
|
+
`noopener noreferrer`; Google and Stripe stay in the real external browser.
|
|
197
|
+
|
|
198
|
+
The Mac shell receives the one-use camera grant over inherited pipe descriptor
|
|
199
|
+
198, passed explicitly through `pair.command`. It is never placed in logs or the
|
|
200
|
+
progress file. Camera and legal views use separate nonpersistent WebKit stores
|
|
201
|
+
and restricted navigation hosts. The native release build smoke-tests pipe
|
|
202
|
+
delivery, cancellation, and temporary-directory handling.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#import <Cocoa/Cocoa.h>
|
|
2
|
+
#import <WebKit/WebKit.h>
|
|
2
3
|
#import <errno.h>
|
|
3
4
|
#import <fcntl.h>
|
|
4
5
|
#import <signal.h>
|
|
@@ -37,14 +38,21 @@ static BOOL acquireSetupInstanceLock(void) {
|
|
|
37
38
|
return NO;
|
|
38
39
|
}
|
|
39
40
|
|
|
40
|
-
@interface TrillboardsSetupDelegate : NSObject <NSApplicationDelegate>
|
|
41
|
+
@interface TrillboardsSetupDelegate : NSObject <NSApplicationDelegate, NSWindowDelegate, WKNavigationDelegate, WKUIDelegate>
|
|
41
42
|
@property(nonatomic, strong) NSWindow *window;
|
|
43
|
+
@property(nonatomic, strong) NSFileHandle *setupOutput;
|
|
44
|
+
@property(nonatomic, strong) NSMutableData *setupOutputBuffer;
|
|
45
|
+
@property(nonatomic, strong) NSWindow *cameraWindow;
|
|
46
|
+
@property(nonatomic, strong) WKWebView *cameraWebView;
|
|
47
|
+
@property(nonatomic, strong) NSWindow *legalWindow;
|
|
48
|
+
@property(nonatomic, strong) WKWebView *legalWebView;
|
|
42
49
|
@property(nonatomic, strong) NSTextField *headline;
|
|
43
50
|
@property(nonatomic, strong) NSTextField *detail;
|
|
44
51
|
@property(nonatomic, strong) NSTextField *statusValue;
|
|
45
52
|
@property(nonatomic, strong) NSTextField *metric;
|
|
46
53
|
@property(nonatomic, strong) NSProgressIndicator *progress;
|
|
47
54
|
@property(nonatomic, strong) NSButton *actionButton;
|
|
55
|
+
@property(nonatomic, strong) NSButton *googleReopenButton;
|
|
48
56
|
@property(nonatomic, strong) NSButton *closeButton;
|
|
49
57
|
@property(nonatomic, strong) NSButton *updateButton;
|
|
50
58
|
@property(nonatomic, strong) NSTextField *updateStatus;
|
|
@@ -162,6 +170,11 @@ static BOOL acquireSetupInstanceLock(void) {
|
|
|
162
170
|
- (void)stopPairProcess {
|
|
163
171
|
pid_t pid = self.pairProcessIdentifier;
|
|
164
172
|
self.pairProcessIdentifier = 0;
|
|
173
|
+
if (self.setupOutput) {
|
|
174
|
+
self.setupOutput.readabilityHandler = nil;
|
|
175
|
+
[self.setupOutput closeFile];
|
|
176
|
+
self.setupOutput = nil;
|
|
177
|
+
}
|
|
165
178
|
if (self.setupInput) {
|
|
166
179
|
[self.setupInput closeFile];
|
|
167
180
|
self.setupInput = nil;
|
|
@@ -205,9 +218,18 @@ static BOOL acquireSetupInstanceLock(void) {
|
|
|
205
218
|
return NO;
|
|
206
219
|
}
|
|
207
220
|
|
|
221
|
+
int outputPipe[2];
|
|
222
|
+
if (pipe(outputPipe) != 0) {
|
|
223
|
+
close(logFd); close(inputPipe[0]); close(inputPipe[1]);
|
|
224
|
+
if (error) *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil];
|
|
225
|
+
return NO;
|
|
226
|
+
}
|
|
208
227
|
posix_spawn_file_actions_t fileActions;
|
|
209
228
|
posix_spawnattr_t attributes;
|
|
210
229
|
posix_spawn_file_actions_init(&fileActions);
|
|
230
|
+
posix_spawn_file_actions_adddup2(&fileActions, outputPipe[1], 198);
|
|
231
|
+
posix_spawn_file_actions_addclose(&fileActions, outputPipe[0]);
|
|
232
|
+
posix_spawn_file_actions_addclose(&fileActions, outputPipe[1]);
|
|
211
233
|
posix_spawn_file_actions_adddup2(&fileActions, logFd, STDOUT_FILENO);
|
|
212
234
|
posix_spawn_file_actions_adddup2(&fileActions, logFd, STDERR_FILENO);
|
|
213
235
|
posix_spawn_file_actions_adddup2(&fileActions, inputPipe[0], STDIN_FILENO);
|
|
@@ -238,12 +260,24 @@ static BOOL acquireSetupInstanceLock(void) {
|
|
|
238
260
|
posix_spawn_file_actions_destroy(&fileActions);
|
|
239
261
|
close(logFd);
|
|
240
262
|
close(inputPipe[0]);
|
|
263
|
+
close(outputPipe[1]);
|
|
241
264
|
if (spawnError != 0) {
|
|
242
265
|
close(inputPipe[1]);
|
|
266
|
+
close(outputPipe[0]);
|
|
243
267
|
if (error) *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:spawnError userInfo:nil];
|
|
244
268
|
return NO;
|
|
245
269
|
}
|
|
246
270
|
|
|
271
|
+
self.setupOutputBuffer = [NSMutableData data];
|
|
272
|
+
self.setupOutput = [[NSFileHandle alloc] initWithFileDescriptor:outputPipe[0] closeOnDealloc:YES];
|
|
273
|
+
__weak typeof(self) outputSelf = self;
|
|
274
|
+
self.setupOutput.readabilityHandler = ^(NSFileHandle *handle) {
|
|
275
|
+
NSData *data = handle.availableData;
|
|
276
|
+
if (data.length == 0) { handle.readabilityHandler = nil; return; }
|
|
277
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
278
|
+
if (outputSelf.setupOutput == handle) [outputSelf receiveSetupOutput:data];
|
|
279
|
+
});
|
|
280
|
+
};
|
|
247
281
|
self.setupInput = [[NSFileHandle alloc] initWithFileDescriptor:inputPipe[1] closeOnDealloc:YES];
|
|
248
282
|
self.pairProcessIdentifier = pid;
|
|
249
283
|
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC,
|
|
@@ -264,6 +298,11 @@ static BOOL acquireSetupInstanceLock(void) {
|
|
|
264
298
|
int status = 0;
|
|
265
299
|
pid_t waited = waitpid(pid, &status, 0);
|
|
266
300
|
self.pairProcessIdentifier = 0;
|
|
301
|
+
if (self.setupOutput) {
|
|
302
|
+
self.setupOutput.readabilityHandler = nil;
|
|
303
|
+
[self.setupOutput closeFile];
|
|
304
|
+
self.setupOutput = nil;
|
|
305
|
+
}
|
|
267
306
|
if (self.setupInput) {
|
|
268
307
|
[self.setupInput closeFile];
|
|
269
308
|
self.setupInput = nil;
|
|
@@ -293,6 +332,7 @@ static BOOL acquireSetupInstanceLock(void) {
|
|
|
293
332
|
backing:NSBackingStoreBuffered
|
|
294
333
|
defer:NO];
|
|
295
334
|
self.window.title = @"Sense Anything Connector";
|
|
335
|
+
self.window.delegate = self;
|
|
296
336
|
self.window.minSize = NSMakeSize(760, 540);
|
|
297
337
|
self.window.maxSize = NSMakeSize(760, 540);
|
|
298
338
|
[self.window center];
|
|
@@ -339,6 +379,14 @@ static BOOL acquireSetupInstanceLock(void) {
|
|
|
339
379
|
self.statusValue.layer.cornerRadius = 6;
|
|
340
380
|
[root addSubview:self.statusValue];
|
|
341
381
|
|
|
382
|
+
self.googleReopenButton = [[NSButton alloc] initWithFrame:NSMakeRect(360, 219, 218, 28)];
|
|
383
|
+
self.googleReopenButton.title = @"Reopen setup tab";
|
|
384
|
+
self.googleReopenButton.bezelStyle = NSBezelStyleRounded;
|
|
385
|
+
self.googleReopenButton.target = self;
|
|
386
|
+
self.googleReopenButton.action = @selector(googleReopenPressed);
|
|
387
|
+
self.googleReopenButton.hidden = YES;
|
|
388
|
+
[root addSubview:self.googleReopenButton];
|
|
389
|
+
|
|
342
390
|
self.metric = [self label:@"" size:13 weight:NSFontWeightRegular frame:NSMakeRect(236, 174, 465, 28)];
|
|
343
391
|
self.metric.textColor = NSColor.secondaryLabelColor;
|
|
344
392
|
[root addSubview:self.metric];
|
|
@@ -503,6 +551,7 @@ static BOOL acquireSetupInstanceLock(void) {
|
|
|
503
551
|
self.privacyLabel.hidden = !showStatus;
|
|
504
552
|
self.actionButton.hidden = !showStatus;
|
|
505
553
|
self.closeButton.hidden = YES;
|
|
554
|
+
self.googleReopenButton.hidden = YES;
|
|
506
555
|
}
|
|
507
556
|
|
|
508
557
|
- (void)chooseAccountPressed {
|
|
@@ -526,6 +575,10 @@ static BOOL acquireSetupInstanceLock(void) {
|
|
|
526
575
|
}
|
|
527
576
|
}
|
|
528
577
|
|
|
578
|
+
- (void)googleReopenPressed {
|
|
579
|
+
[self sendOnboardingCommand:@{ @"action": @"browser_reopen" }];
|
|
580
|
+
}
|
|
581
|
+
|
|
529
582
|
- (void)googlePressed {
|
|
530
583
|
[self sendOnboardingCommand:@{ @"action": @"google_start" }];
|
|
531
584
|
}
|
|
@@ -572,15 +625,15 @@ static BOOL acquireSetupInstanceLock(void) {
|
|
|
572
625
|
}
|
|
573
626
|
|
|
574
627
|
- (void)reviewTermsPressed {
|
|
575
|
-
[self
|
|
628
|
+
[self showLegalURL:@"https://trillboards.com/legal/terms"];
|
|
576
629
|
}
|
|
577
630
|
|
|
578
631
|
- (void)reviewPrivacyPressed {
|
|
579
|
-
[self
|
|
632
|
+
[self showLegalURL:@"https://trillboards.com/legal/privacy-policy"];
|
|
580
633
|
}
|
|
581
634
|
|
|
582
635
|
- (void)reviewDpaPressed {
|
|
583
|
-
[self
|
|
636
|
+
[self showLegalURL:@"https://trillboards.com/legal/data-processing"];
|
|
584
637
|
}
|
|
585
638
|
|
|
586
639
|
- (void)termsSubmitPressed {
|
|
@@ -634,6 +687,15 @@ static BOOL acquireSetupInstanceLock(void) {
|
|
|
634
687
|
self.actionButton.title = @"Try again";
|
|
635
688
|
self.actionButton.enabled = YES;
|
|
636
689
|
self.closeButton.hidden = YES;
|
|
690
|
+
self.googleReopenButton.hidden = YES;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
- (void)windowWillClose:(NSNotification *)notification {
|
|
694
|
+
if (notification.object == self.window) {
|
|
695
|
+
[self.cameraWindow close];
|
|
696
|
+
[self.legalWindow close];
|
|
697
|
+
[NSApp terminate:nil];
|
|
698
|
+
}
|
|
637
699
|
}
|
|
638
700
|
|
|
639
701
|
- (void)closePressed {
|
|
@@ -741,9 +803,11 @@ static BOOL acquireSetupInstanceLock(void) {
|
|
|
741
803
|
[self sendOnboardingCommand:@{ @"action": @"refresh" }];
|
|
742
804
|
} else if ([self.lastStage isEqualToString:@"waiting_for_account"] ||
|
|
743
805
|
[self.lastStage isEqualToString:@"account_attention"]) {
|
|
744
|
-
[self
|
|
745
|
-
|
|
746
|
-
[self
|
|
806
|
+
[self sendOnboardingCommand:@{ @"action": @"camera_reopen" }];
|
|
807
|
+
if (self.cameraWindow) [self.cameraWindow makeKeyAndOrderFront:nil];
|
|
808
|
+
else [self showLocalError:@"Camera setup unavailable" message:@"Restart setup to reconnect the camera window to your account."];
|
|
809
|
+
} else if ([@[@"ready", @"account_connected", @"discovering", @"devices_found", @"validating"] containsObject:self.lastStage]) {
|
|
810
|
+
if (self.cameraWindow) [self.cameraWindow makeKeyAndOrderFront:nil];
|
|
747
811
|
} else if ([self.lastStage isEqualToString:@"local_ai_error"]) {
|
|
748
812
|
[NSApp terminate:nil];
|
|
749
813
|
} else if ([self.lastStage isEqualToString:@"welcome"] || [self.lastStage isEqualToString:@"error"]) {
|
|
@@ -752,6 +816,9 @@ static BOOL acquireSetupInstanceLock(void) {
|
|
|
752
816
|
}
|
|
753
817
|
|
|
754
818
|
- (void)startSetup {
|
|
819
|
+
[self stopPairProcess];
|
|
820
|
+
[self.cameraWindow close]; self.cameraWindow = nil; self.cameraWebView = nil;
|
|
821
|
+
[self.legalWindow close]; self.legalWindow = nil; self.legalWebView = nil;
|
|
755
822
|
NSString *pairer = [self homePath:@"Library/Trillboards/edge/bin/trillboards-pair"];
|
|
756
823
|
NSString *stateDirectory = [self homePath:@"Library/Application Support/Trillboards"];
|
|
757
824
|
NSString *statePath = [stateDirectory stringByAppendingPathComponent:@"setup-progress.json"];
|
|
@@ -778,7 +845,7 @@ static BOOL acquireSetupInstanceLock(void) {
|
|
|
778
845
|
|
|
779
846
|
NSError *error = nil;
|
|
780
847
|
if (![self launchSetupExecutable:pairer
|
|
781
|
-
arguments:@[@"--setup", @"--status-file", statePath]
|
|
848
|
+
arguments:@[@"--setup", @"--status-file", statePath, @"--native-ui-fd", @"198"]
|
|
782
849
|
logPath:logPath
|
|
783
850
|
error:&error]) {
|
|
784
851
|
[self showLocalError:@"Could not start setup" message:@"Reinstall the Sense Anything Connector, then try again."];
|
|
@@ -884,8 +951,10 @@ static BOOL acquireSetupInstanceLock(void) {
|
|
|
884
951
|
self.statusValue.stringValue = state[@"displayCode"];
|
|
885
952
|
} else if ([stage isEqualToString:@"onboarding_google"]) {
|
|
886
953
|
self.statusValue.stringValue = @"Waiting for Google";
|
|
954
|
+
self.googleReopenButton.hidden = NO;
|
|
887
955
|
} else if ([stage isEqualToString:@"onboarding_checkout"]) {
|
|
888
956
|
self.statusValue.stringValue = @"Waiting for Stripe";
|
|
957
|
+
self.googleReopenButton.hidden = NO;
|
|
889
958
|
} else if ([stage isEqualToString:@"onboarding_install"]) {
|
|
890
959
|
self.statusValue.stringValue = @"Onboarding complete";
|
|
891
960
|
} else if (cameraCount.integerValue > 0) {
|
|
@@ -940,10 +1009,13 @@ static BOOL acquireSetupInstanceLock(void) {
|
|
|
940
1009
|
self.actionButton.title = @"Preparing cameras…";
|
|
941
1010
|
self.actionButton.enabled = NO;
|
|
942
1011
|
} else if ([stage isEqualToString:@"waiting_for_account"]) {
|
|
943
|
-
self.actionButton.title = @"Open
|
|
1012
|
+
self.actionButton.title = @"Open camera setup";
|
|
944
1013
|
self.actionButton.enabled = YES;
|
|
945
1014
|
} else if ([stage isEqualToString:@"account_attention"]) {
|
|
946
|
-
self.actionButton.title = @"
|
|
1015
|
+
self.actionButton.title = @"Open camera setup";
|
|
1016
|
+
self.actionButton.enabled = YES;
|
|
1017
|
+
} else if ([@[@"account_connected", @"discovering", @"devices_found", @"validating"] containsObject:stage] && self.cameraWindow) {
|
|
1018
|
+
self.actionButton.title = @"Open camera setup";
|
|
947
1019
|
self.actionButton.enabled = YES;
|
|
948
1020
|
} else if ([stage isEqualToString:@"local_ai_error"]) {
|
|
949
1021
|
self.actionButton.title = @"Close";
|
|
@@ -997,6 +1069,98 @@ static BOOL acquireSetupInstanceLock(void) {
|
|
|
997
1069
|
}
|
|
998
1070
|
}
|
|
999
1071
|
|
|
1072
|
+
- (void)receiveSetupOutput:(NSData *)data {
|
|
1073
|
+
if (self.terminating || !self.setupOutput) return;
|
|
1074
|
+
[self.setupOutputBuffer appendData:data];
|
|
1075
|
+
if (self.setupOutputBuffer.length > 16384) {
|
|
1076
|
+
[self.setupOutputBuffer setLength:0];
|
|
1077
|
+
[self showLocalError:@"Camera setup unavailable" message:@"Restart setup to reconnect the camera window."];
|
|
1078
|
+
return;
|
|
1079
|
+
}
|
|
1080
|
+
NSData *newline = [@"\n" dataUsingEncoding:NSUTF8StringEncoding];
|
|
1081
|
+
while (YES) {
|
|
1082
|
+
NSRange boundary = [self.setupOutputBuffer rangeOfData:newline options:0 range:NSMakeRange(0, self.setupOutputBuffer.length)];
|
|
1083
|
+
if (boundary.location == NSNotFound) break;
|
|
1084
|
+
NSData *line = [self.setupOutputBuffer subdataWithRange:NSMakeRange(0, boundary.location)];
|
|
1085
|
+
[self.setupOutputBuffer replaceBytesInRange:NSMakeRange(0, boundary.location + 1) withBytes:NULL length:0];
|
|
1086
|
+
NSDictionary *message = [NSJSONSerialization JSONObjectWithData:line options:0 error:nil];
|
|
1087
|
+
if (![message isKindOfClass:NSDictionary.class] || ![message[@"action"] isEqual:@"camera"] || ![message[@"url"] isKindOfClass:NSString.class]) continue;
|
|
1088
|
+
NSURL *url = [NSURL URLWithString:message[@"url"]];
|
|
1089
|
+
if (![url.scheme isEqual:@"https"] || ![url.host isEqual:@"app.senseanything.com"] || ![url.path isEqual:@"/sense/installer-return"]) {
|
|
1090
|
+
[self showLocalError:@"Camera setup unavailable" message:@"The camera setup destination was not recognized."];
|
|
1091
|
+
continue;
|
|
1092
|
+
}
|
|
1093
|
+
if (!self.cameraWebView) {
|
|
1094
|
+
self.cameraWebView = [self setupWebView];
|
|
1095
|
+
self.cameraWindow = [self contentWindow:@"Sense Anything — Recorder and cameras" webView:self.cameraWebView];
|
|
1096
|
+
}
|
|
1097
|
+
[self.cameraWebView loadRequest:[NSURLRequest requestWithURL:url]];
|
|
1098
|
+
[self.cameraWindow makeKeyAndOrderFront:nil];
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
- (WKWebView *)setupWebView {
|
|
1103
|
+
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
|
|
1104
|
+
configuration.websiteDataStore = WKWebsiteDataStore.nonPersistentDataStore;
|
|
1105
|
+
WKWebView *view = [[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 960, 700) configuration:configuration];
|
|
1106
|
+
view.navigationDelegate = self;
|
|
1107
|
+
view.UIDelegate = self;
|
|
1108
|
+
return view;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
- (NSWindow *)contentWindow:(NSString *)title webView:(WKWebView *)view {
|
|
1112
|
+
NSWindow *window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 960, 700)
|
|
1113
|
+
styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable
|
|
1114
|
+
backing:NSBackingStoreBuffered defer:NO];
|
|
1115
|
+
window.title = title;
|
|
1116
|
+
window.releasedWhenClosed = NO;
|
|
1117
|
+
window.contentView = view;
|
|
1118
|
+
// An owned window stays with the installer rather than activating whichever
|
|
1119
|
+
// unrelated browser window happens to be on another desktop.
|
|
1120
|
+
[self.window addChildWindow:window ordered:NSWindowAbove];
|
|
1121
|
+
NSRect visible = (self.window.screen ?: NSScreen.mainScreen).visibleFrame;
|
|
1122
|
+
NSRect frame = window.frame;
|
|
1123
|
+
frame.size.width = MIN(frame.size.width, NSWidth(visible) - 40);
|
|
1124
|
+
frame.size.height = MIN(frame.size.height, NSHeight(visible) - 40);
|
|
1125
|
+
frame.origin.x = MAX(NSMinX(visible) + 20, MIN(NSMinX(self.window.frame) + 30, NSMaxX(visible) - NSWidth(frame) - 20));
|
|
1126
|
+
frame.origin.y = MAX(NSMinY(visible) + 20, MIN(NSMinY(self.window.frame) + 20, NSMaxY(visible) - NSHeight(frame) - 20));
|
|
1127
|
+
[window setFrame:frame display:NO];
|
|
1128
|
+
return window;
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
- (void)showLegalURL:(NSString *)string {
|
|
1132
|
+
if (!self.legalWebView) {
|
|
1133
|
+
self.legalWebView = [self setupWebView];
|
|
1134
|
+
self.legalWindow = [self contentWindow:@"Sense Anything — Agreements" webView:self.legalWebView];
|
|
1135
|
+
}
|
|
1136
|
+
[self.legalWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:string]]];
|
|
1137
|
+
[self.legalWindow makeKeyAndOrderFront:nil];
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)action
|
|
1141
|
+
decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
|
|
1142
|
+
NSURL *url = action.request.URL;
|
|
1143
|
+
BOOL allowedHost = webView == self.cameraWebView
|
|
1144
|
+
? [url.host isEqual:@"app.senseanything.com"]
|
|
1145
|
+
: ([url.host isEqual:@"trillboards.com"] || [url.host isEqual:@"www.trillboards.com"]);
|
|
1146
|
+
BOOL allowed = [url.scheme isEqual:@"https"] && allowedHost;
|
|
1147
|
+
if (allowed && !action.targetFrame) {
|
|
1148
|
+
[webView loadRequest:action.request];
|
|
1149
|
+
decisionHandler(WKNavigationActionPolicyCancel);
|
|
1150
|
+
return;
|
|
1151
|
+
}
|
|
1152
|
+
decisionHandler(allowed ? WKNavigationActionPolicyAllow : WKNavigationActionPolicyCancel);
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error {
|
|
1156
|
+
(void)webView; (void)navigation;
|
|
1157
|
+
if (error.code == NSURLErrorCancelled) return;
|
|
1158
|
+
NSAlert *alert = [[NSAlert alloc] init];
|
|
1159
|
+
alert.messageText = @"Could not load this setup page";
|
|
1160
|
+
alert.informativeText = @"Check your internet connection, then reopen the page from the Connector.";
|
|
1161
|
+
[alert beginSheetModalForWindow:self.window completionHandler:nil];
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1000
1164
|
- (void)openURL:(NSString *)string {
|
|
1001
1165
|
NSURL *url = [NSURL URLWithString:string];
|
|
1002
1166
|
if (url) [NSWorkspace.sharedWorkspace openURL:url];
|
|
@@ -1052,6 +1216,35 @@ static int runCancellationSmokeTest(void) {
|
|
|
1052
1216
|
return 0;
|
|
1053
1217
|
}
|
|
1054
1218
|
|
|
1219
|
+
@interface SetupPipeSmokeDelegate : TrillboardsSetupDelegate
|
|
1220
|
+
@property(nonatomic, strong) NSMutableData *received;
|
|
1221
|
+
@end
|
|
1222
|
+
@implementation SetupPipeSmokeDelegate
|
|
1223
|
+
- (void)receiveSetupOutput:(NSData *)data { [self.received appendData:data]; }
|
|
1224
|
+
@end
|
|
1225
|
+
|
|
1226
|
+
static int runNativePipeSmokeTest(NSString *nodePath) {
|
|
1227
|
+
SetupPipeSmokeDelegate *delegate = [[SetupPipeSmokeDelegate alloc] init];
|
|
1228
|
+
delegate.received = [NSMutableData data];
|
|
1229
|
+
NSError *error = nil;
|
|
1230
|
+
if (![delegate launchSetupExecutable:@"/bin/bash"
|
|
1231
|
+
arguments:@[@"-c", @"exec \"$1\" -e \"$2\"", @"pipe-smoke", nodePath,
|
|
1232
|
+
@"require('fs').writeSync(198, 'native-ui-pipe-ok\\n'); setTimeout(() => {}, 10000)"]
|
|
1233
|
+
logPath:@"/dev/null" error:&error]) return 1;
|
|
1234
|
+
NSDate *deadline = [NSDate dateWithTimeIntervalSinceNow:3];
|
|
1235
|
+
while (delegate.received.length == 0 && deadline.timeIntervalSinceNow > 0) {
|
|
1236
|
+
[NSRunLoop.currentRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.02]];
|
|
1237
|
+
}
|
|
1238
|
+
NSString *received = [[NSString alloc] initWithData:delegate.received encoding:NSUTF8StringEncoding];
|
|
1239
|
+
[delegate stopPairProcess];
|
|
1240
|
+
if (![received isEqualToString:@"native-ui-pipe-ok\n"]) {
|
|
1241
|
+
fprintf(stderr, "Native setup private UI pipe did not deliver its message.\n");
|
|
1242
|
+
return 1;
|
|
1243
|
+
}
|
|
1244
|
+
fprintf(stdout, "Sense Anything Connector private UI pipe smoke test passed.\n");
|
|
1245
|
+
return 0;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1055
1248
|
static int runTemporaryDirectorySmokeTest(void) {
|
|
1056
1249
|
const char *environmentTemporaryDirectory = getenv("TMPDIR");
|
|
1057
1250
|
if (environmentTemporaryDirectory != NULL &&
|
|
@@ -1092,6 +1285,9 @@ int main(int argc, const char *argv[]) {
|
|
|
1092
1285
|
// setup app or its pairing child retain that now-stale TMPDIR.
|
|
1093
1286
|
normalizeTemporaryDirectory();
|
|
1094
1287
|
@autoreleasepool {
|
|
1288
|
+
if (argc == 3 && [[NSString stringWithUTF8String:argv[1]] isEqualToString:@"--smoke-test-ui-pipe"]) {
|
|
1289
|
+
return runNativePipeSmokeTest([NSString stringWithUTF8String:argv[2]]);
|
|
1290
|
+
}
|
|
1095
1291
|
if (argc == 2 && [[NSString stringWithUTF8String:argv[1]] isEqualToString:@"--smoke-test-cancel"]) {
|
|
1096
1292
|
return runCancellationSmokeTest();
|
|
1097
1293
|
}
|
|
@@ -492,7 +492,7 @@ cp "$PKG_ROOT/deploy/branding/SenseAnythingConnector.icns" \
|
|
|
492
492
|
"$SETUP_APP_RESOURCES/SenseAnythingConnector.icns"
|
|
493
493
|
log "Building native Sense Anything Connector.app…"
|
|
494
494
|
if [ "$UNIVERSAL" -eq 1 ]; then
|
|
495
|
-
clang -fobjc-arc -Wall -Wextra -Werror -O2 -mmacosx-version-min=12.0 -arch arm64 -arch x86_64 -framework Cocoa \
|
|
495
|
+
clang -fobjc-arc -Wall -Wextra -Werror -O2 -mmacosx-version-min=12.0 -arch arm64 -arch x86_64 -framework Cocoa -framework WebKit \
|
|
496
496
|
"$PKG_ROOT/deploy/macos/TrillboardsSetupApp.m" -o "$SETUP_APP_BIN" \
|
|
497
497
|
|| die "native setup app universal build failed"
|
|
498
498
|
else
|
|
@@ -500,13 +500,15 @@ else
|
|
|
500
500
|
arm64) SETUP_ARCH="arm64";;
|
|
501
501
|
x64) SETUP_ARCH="x86_64";;
|
|
502
502
|
esac
|
|
503
|
-
clang -fobjc-arc -Wall -Wextra -Werror -O2 -mmacosx-version-min=12.0 -arch "$SETUP_ARCH" -framework Cocoa \
|
|
503
|
+
clang -fobjc-arc -Wall -Wextra -Werror -O2 -mmacosx-version-min=12.0 -arch "$SETUP_ARCH" -framework Cocoa -framework WebKit \
|
|
504
504
|
"$PKG_ROOT/deploy/macos/TrillboardsSetupApp.m" -o "$SETUP_APP_BIN" \
|
|
505
505
|
|| die "native setup app build failed"
|
|
506
506
|
fi
|
|
507
507
|
chmod +x "$SETUP_APP_BIN"
|
|
508
508
|
"$SETUP_APP_BIN" --smoke-test-cancel \
|
|
509
509
|
|| die "native setup app cancellation smoke test failed"
|
|
510
|
+
"$SETUP_APP_BIN" --smoke-test-ui-pipe "$PAYLOAD/bin/node" \
|
|
511
|
+
|| die "native setup private UI pipe smoke test failed"
|
|
510
512
|
INSTALLER_TMP="$STAGE/PKInstallSandbox.trillboards-test/tmp"
|
|
511
513
|
mkdir -p "$INSTALLER_TMP"
|
|
512
514
|
TMPDIR="$INSTALLER_TMP" "$SETUP_APP_BIN" --smoke-test-tempdir \
|
|
@@ -41,6 +41,7 @@ LABEL="com.trillboards.edge"
|
|
|
41
41
|
API_BASE="${TRILLBOARDS_API_BASE:-}"
|
|
42
42
|
HEADLESS_CODE=""
|
|
43
43
|
UI_STATUS_FILE=""
|
|
44
|
+
NATIVE_UI_ARGS=()
|
|
44
45
|
|
|
45
46
|
# Parse args. First non-flag positional == the code (headless mode).
|
|
46
47
|
while [ $# -gt 0 ]; do
|
|
@@ -48,6 +49,7 @@ while [ $# -gt 0 ]; do
|
|
|
48
49
|
--api-base) API_BASE="$2"; shift 2;;
|
|
49
50
|
--setup) SETUP_MODE=1; shift;;
|
|
50
51
|
--status-file) UI_STATUS_FILE="$2"; shift 2;;
|
|
52
|
+
--native-ui-fd) NATIVE_UI_ARGS=(--native-ui-fd "$2"); shift 2;;
|
|
51
53
|
-h|--help) sed -n '2,20p' "${BASH_SOURCE[0]}"; exit 0;;
|
|
52
54
|
*) if [ -z "$HEADLESS_CODE" ]; then HEADLESS_CODE="$1"; fi; shift;;
|
|
53
55
|
esac
|
|
@@ -368,7 +370,7 @@ if [ "${SETUP_MODE:-0}" = "1" ]; then
|
|
|
368
370
|
# says legal + entitlement are current, so cameras cannot open early.
|
|
369
371
|
# The same process keeps the authenticated session in memory through camera
|
|
370
372
|
# setup and mints a one-use signed-in browser handoff at first launch.
|
|
371
|
-
"$LAUNCHER" installer-onboarding --config "$CONFIG" --status-file "$UI_STATUS_FILE"
|
|
373
|
+
"$LAUNCHER" installer-onboarding --config "$CONFIG" --status-file "$UI_STATUS_FILE" "${NATIVE_UI_ARGS[@]}"
|
|
372
374
|
else
|
|
373
375
|
"$LAUNCHER" setup --config "$CONFIG"
|
|
374
376
|
fi
|
package/dist/EdgeAgent.js
CHANGED
|
@@ -44,7 +44,7 @@ const edge_ads_1 = require("@trillboards/edge-ads");
|
|
|
44
44
|
const CommandRouter_1 = require("./CommandRouter");
|
|
45
45
|
const config_1 = require("./config");
|
|
46
46
|
const dataDir_1 = require("./dataDir");
|
|
47
|
-
const SDK_VERSION = '0.2.
|
|
47
|
+
const SDK_VERSION = '0.2.138';
|
|
48
48
|
class EdgeAgent extends events_1.EventEmitter {
|
|
49
49
|
constructor(config, deps) {
|
|
50
50
|
super();
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAsgBA;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAKvD;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAAC,kBAAkB,EAAE,MAAM,GAAG,MAAM,CAW/E;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,kBAAkB,EAAE,MAAM,EAC1B,UAAU,EAAE,MAAM,EAClB,SAAS,GAAE,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,KAAK,IAC/B,GAChC,IAAI,CAWN;AAUD,gEAAgE;AAChE,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CASvE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EAAE,EACd,UAAU,EAAE,MAAM,EAClB,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,QAAQ,GAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAA2C,EACpE,MAAM,GAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAiC,GACvD;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAyB3F;AAgHD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA+M7D"}
|
package/dist/cli.js
CHANGED
|
@@ -239,22 +239,76 @@ async function main() {
|
|
|
239
239
|
process.exit(2);
|
|
240
240
|
return;
|
|
241
241
|
}
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
242
|
+
const { InstallerBrowserSession } = require('./installerBrowserSession');
|
|
243
|
+
const browserSession = new InstallerBrowserSession(openSetupPortal);
|
|
244
|
+
const nativeUiFd = getArg(onboardingArgs, '--native-ui-fd');
|
|
245
|
+
const nativeCameraView = process.platform === 'darwin' && nativeUiFd === '198';
|
|
246
|
+
// A private inherited pipe carries one-use grants directly to the native
|
|
247
|
+
// UI. They must never be written to logs or the progress file.
|
|
248
|
+
const reportSetup = createSetupProgressReporter(statusFile);
|
|
249
|
+
const cameraProgress = (update) => {
|
|
250
|
+
if (nativeCameraView && update.stage === 'waiting_for_account') {
|
|
251
|
+
const { portalUrl: _portalUrl, ...safe } = update;
|
|
252
|
+
reportSetup({ ...safe, title: 'Connect your recorder',
|
|
253
|
+
message: 'Continue in the camera setup window inside this Connector.' });
|
|
254
|
+
}
|
|
255
|
+
else
|
|
256
|
+
reportSetup(update);
|
|
257
|
+
};
|
|
258
|
+
let rc;
|
|
259
|
+
try {
|
|
260
|
+
rc = await runInstallerOnboardingMode({
|
|
246
261
|
apiBaseUrl: getArg(onboardingArgs, '--api-base') || process.env.TRILLBOARDS_API_BASE || 'https://api.trillboards.com',
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
const
|
|
254
|
-
|
|
262
|
+
progress: reportSetup,
|
|
263
|
+
openBrowser: (url) => browserSession.open(url),
|
|
264
|
+
reopenBrowser: () => browserSession.reopen(),
|
|
265
|
+
clearBrowserStep: () => browserSession.clear(),
|
|
266
|
+
afterComplete: async (onboardingClient) => {
|
|
267
|
+
const { InstallerCameraHandoff } = require('./installerCameraHandoff');
|
|
268
|
+
const handoff = new InstallerCameraHandoff((url) => onboardingClient.createPortalHandoff(url), async (url) => {
|
|
269
|
+
if (nativeCameraView) {
|
|
270
|
+
fs.writeSync(198, JSON.stringify({ action: 'camera', url }) + '\n');
|
|
271
|
+
return true;
|
|
272
|
+
}
|
|
273
|
+
return browserSession.open(url);
|
|
274
|
+
});
|
|
275
|
+
const commands = require('readline').createInterface({ input: process.stdin });
|
|
276
|
+
commands.on('line', (line) => {
|
|
277
|
+
let command;
|
|
278
|
+
try {
|
|
279
|
+
command = JSON.parse(line);
|
|
280
|
+
}
|
|
281
|
+
catch {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
if (command?.action === 'camera_reopen') {
|
|
285
|
+
void handoff.reopen().catch(() => {
|
|
286
|
+
cameraProgress({ stage: 'account_attention', title: 'Camera setup unavailable',
|
|
287
|
+
message: 'Check your connection, then open camera setup again.', recoverable: true });
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
try {
|
|
292
|
+
return await runSetupMode({
|
|
293
|
+
apiBaseUrl: getArg(onboardingArgs, '--api-base') || process.env.TRILLBOARDS_API_BASE || 'https://api.trillboards.com',
|
|
294
|
+
portalBaseUrl: getArg(onboardingArgs, '--portal-base') || process.env.TRILLBOARDS_PORTAL_BASE || DEFAULT_PORTAL_BASE_URL,
|
|
295
|
+
configPath: getArg(onboardingArgs, '--config') || 'trillboards.config.json',
|
|
296
|
+
connectorVersion: VERSION,
|
|
297
|
+
ffmpegPath: (0, runtimePaths_1.resolveFfmpegBinaryPath)(getArg(onboardingArgs, '--ffmpeg')),
|
|
298
|
+
progress: cameraProgress,
|
|
299
|
+
refreshPortalOnCodeRenewal: true,
|
|
300
|
+
openPortal: (cameraUrl) => handoff.open(cameraUrl),
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
finally {
|
|
304
|
+
commands.close();
|
|
305
|
+
}
|
|
255
306
|
},
|
|
256
|
-
})
|
|
257
|
-
}
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
finally {
|
|
310
|
+
await browserSession.close();
|
|
311
|
+
}
|
|
258
312
|
process.exit(rc);
|
|
259
313
|
break;
|
|
260
314
|
}
|