@agentworkforce/deploy 3.0.35 → 3.0.37
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/deploy.d.ts.map +1 -1
- package/dist/deploy.js +155 -15
- package/dist/deploy.js.map +1 -1
- package/dist/deploy.test.js +555 -0
- package/dist/deploy.test.js.map +1 -1
- package/dist/modes/sandbox-client.d.ts +4 -0
- package/dist/modes/sandbox-client.d.ts.map +1 -1
- package/dist/modes/sandbox-client.js +4 -0
- package/dist/modes/sandbox-client.js.map +1 -1
- package/dist/modes/sandbox-client.test.js +26 -1
- package/dist/modes/sandbox-client.test.js.map +1 -1
- package/dist/modes/sandbox.d.ts.map +1 -1
- package/dist/modes/sandbox.js +3 -1
- package/dist/modes/sandbox.js.map +1 -1
- package/dist/modes/sandbox.test.js +135 -1
- package/dist/modes/sandbox.test.js.map +1 -1
- package/dist/runtime-credentials-live.test.d.ts +2 -0
- package/dist/runtime-credentials-live.test.d.ts.map +1 -0
- package/dist/runtime-credentials-live.test.js +20 -0
- package/dist/runtime-credentials-live.test.js.map +1 -0
- package/package.json +3 -3
package/dist/deploy.test.js
CHANGED
|
@@ -431,6 +431,561 @@ test('deploy connects each missing persona integration before launch', async ()
|
|
|
431
431
|
await cleanup();
|
|
432
432
|
}
|
|
433
433
|
});
|
|
434
|
+
test('deploy dev mode injects runtime credentials for a detected writeback trigger without provider-token leakage', async () => {
|
|
435
|
+
const providerTokenSentinel = 'WORKFORCE_PROVIDER_TOKEN_SHOULD_NOT_LEAK';
|
|
436
|
+
const integrations = {
|
|
437
|
+
github: { triggers: [{ on: 'pull_request.opened' }] }
|
|
438
|
+
};
|
|
439
|
+
const { personaPath, cleanup } = await withTempPersona(basePersonaJson({ integrations }));
|
|
440
|
+
const io = createBufferedIO();
|
|
441
|
+
const originalFetch = globalThis.fetch;
|
|
442
|
+
const originalProviderToken = process.env.WORKFORCE_INTEGRATION_GITHUB_TOKEN;
|
|
443
|
+
const calls = [];
|
|
444
|
+
let launchedEnv;
|
|
445
|
+
let launched = false;
|
|
446
|
+
process.env.WORKFORCE_INTEGRATION_GITHUB_TOKEN = providerTokenSentinel;
|
|
447
|
+
globalThis.fetch = (async (input, init) => {
|
|
448
|
+
const url = String(input);
|
|
449
|
+
const method = init?.method ?? 'GET';
|
|
450
|
+
const body = init?.body ? JSON.parse(String(init.body)) : undefined;
|
|
451
|
+
calls.push({ url, method, ...(body !== undefined ? { body } : {}) });
|
|
452
|
+
if (url.includes('/api/v1/workspaces/ws-test/integrations/github/status')) {
|
|
453
|
+
return jsonResponse({ provider: 'github', status: 'ready' });
|
|
454
|
+
}
|
|
455
|
+
if (url.includes('/api/v1/workspaces/ws-test/runtime-credentials')) {
|
|
456
|
+
assert.equal(method, 'POST');
|
|
457
|
+
assert.equal(new Headers(init?.headers).get('authorization'), 'Bearer relay_ws_workspace');
|
|
458
|
+
assert.deepEqual(body, {
|
|
459
|
+
personaId: 'demo',
|
|
460
|
+
agentId: 'demo',
|
|
461
|
+
integrations: {
|
|
462
|
+
github: {
|
|
463
|
+
source: { kind: 'deployer_user' },
|
|
464
|
+
triggers: [{ on: 'pull_request.opened' }]
|
|
465
|
+
}
|
|
466
|
+
},
|
|
467
|
+
ttlSeconds: 3600
|
|
468
|
+
});
|
|
469
|
+
return jsonResponse({
|
|
470
|
+
relayfileUrl: 'https://relayfile.test',
|
|
471
|
+
relayfileWorkspaceId: 'ws-test',
|
|
472
|
+
relayfileToken: 'relay_pa_scoped',
|
|
473
|
+
relayfileMountPaths: ['/github/repos/**/**/pulls/**']
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
throw new Error(`unexpected URL ${url}`);
|
|
477
|
+
});
|
|
478
|
+
try {
|
|
479
|
+
const result = await deploy({
|
|
480
|
+
personaPath,
|
|
481
|
+
mode: 'dev',
|
|
482
|
+
noPrompt: true,
|
|
483
|
+
cloudUrl: 'https://cloud.example.test',
|
|
484
|
+
io
|
|
485
|
+
}, {
|
|
486
|
+
workspaceAuth: {
|
|
487
|
+
async resolveWorkspace() {
|
|
488
|
+
return { workspace: 'ws-test', token: 'relay_ws_workspace' };
|
|
489
|
+
}
|
|
490
|
+
},
|
|
491
|
+
bundle: successfulBundleStager(),
|
|
492
|
+
modes: {
|
|
493
|
+
dev: {
|
|
494
|
+
async launch(input) {
|
|
495
|
+
launched = true;
|
|
496
|
+
launchedEnv = input.env;
|
|
497
|
+
return {
|
|
498
|
+
id: 'dev-1',
|
|
499
|
+
async stop() {
|
|
500
|
+
/* no-op */
|
|
501
|
+
},
|
|
502
|
+
done: Promise.resolve({ code: 0 })
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
assert.equal(result.deploymentId, 'demo');
|
|
509
|
+
assert.equal(launched, true);
|
|
510
|
+
assert.equal(launchedEnv?.RELAYFILE_URL, 'https://relayfile.test');
|
|
511
|
+
assert.equal(launchedEnv?.RELAYFILE_WORKSPACE_ID, 'ws-test');
|
|
512
|
+
assert.equal(launchedEnv?.RELAYFILE_TOKEN, 'relay_pa_scoped');
|
|
513
|
+
assert.equal(launchedEnv?.RELAYFILE_MOUNT_PATHS, JSON.stringify(['/github/repos/**/**/pulls/**']));
|
|
514
|
+
assert.equal(launchedEnv?.WORKFORCE_INTEGRATION_GITHUB_TOKEN, '');
|
|
515
|
+
assert.doesNotMatch(JSON.stringify(launchedEnv), new RegExp(providerTokenSentinel));
|
|
516
|
+
assert.doesNotMatch(JSON.stringify(calls), new RegExp(providerTokenSentinel));
|
|
517
|
+
assert.doesNotMatch(JSON.stringify(io.messages), new RegExp(providerTokenSentinel));
|
|
518
|
+
}
|
|
519
|
+
finally {
|
|
520
|
+
if (originalProviderToken === undefined) {
|
|
521
|
+
delete process.env.WORKFORCE_INTEGRATION_GITHUB_TOKEN;
|
|
522
|
+
}
|
|
523
|
+
else {
|
|
524
|
+
process.env.WORKFORCE_INTEGRATION_GITHUB_TOKEN = originalProviderToken;
|
|
525
|
+
}
|
|
526
|
+
globalThis.fetch = originalFetch;
|
|
527
|
+
await cleanup();
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
test('deploy dev mode preserves no-trigger null-token runtime credentials', async () => {
|
|
531
|
+
const { personaPath, cleanup } = await withTempPersona(basePersonaJson({ integrations: { github: {} } }));
|
|
532
|
+
const io = createBufferedIO();
|
|
533
|
+
const originalFetch = globalThis.fetch;
|
|
534
|
+
let launchedEnv;
|
|
535
|
+
globalThis.fetch = (async (input, init) => {
|
|
536
|
+
const url = String(input);
|
|
537
|
+
const method = init?.method ?? 'GET';
|
|
538
|
+
const body = init?.body ? JSON.parse(String(init.body)) : undefined;
|
|
539
|
+
if (url.includes('/api/v1/workspaces/ws-test/integrations/github/status')) {
|
|
540
|
+
return jsonResponse({ provider: 'github', status: 'ready' });
|
|
541
|
+
}
|
|
542
|
+
if (url.includes('/api/v1/workspaces/ws-test/runtime-credentials')) {
|
|
543
|
+
assert.equal(method, 'POST');
|
|
544
|
+
assert.deepEqual(body, {
|
|
545
|
+
personaId: 'demo',
|
|
546
|
+
agentId: 'demo',
|
|
547
|
+
integrations: {
|
|
548
|
+
github: { source: { kind: 'deployer_user' } }
|
|
549
|
+
},
|
|
550
|
+
ttlSeconds: 3600
|
|
551
|
+
});
|
|
552
|
+
return jsonResponse({
|
|
553
|
+
relayfileUrl: 'https://relayfile.test',
|
|
554
|
+
relayfileWorkspaceId: 'ws-test',
|
|
555
|
+
relayfileToken: null,
|
|
556
|
+
relayfileMountPaths: []
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
throw new Error(`unexpected URL ${url}`);
|
|
560
|
+
});
|
|
561
|
+
try {
|
|
562
|
+
await deploy({
|
|
563
|
+
personaPath,
|
|
564
|
+
mode: 'dev',
|
|
565
|
+
noPrompt: true,
|
|
566
|
+
cloudUrl: 'https://cloud.example.test',
|
|
567
|
+
io
|
|
568
|
+
}, {
|
|
569
|
+
workspaceAuth: {
|
|
570
|
+
async resolveWorkspace() {
|
|
571
|
+
return { workspace: 'ws-test', token: 'relay_ws_workspace' };
|
|
572
|
+
}
|
|
573
|
+
},
|
|
574
|
+
bundle: successfulBundleStager(),
|
|
575
|
+
modes: {
|
|
576
|
+
dev: {
|
|
577
|
+
async launch(input) {
|
|
578
|
+
launchedEnv = input.env;
|
|
579
|
+
return {
|
|
580
|
+
id: 'dev-1',
|
|
581
|
+
async stop() {
|
|
582
|
+
/* no-op */
|
|
583
|
+
},
|
|
584
|
+
done: Promise.resolve({ code: 0 })
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
});
|
|
590
|
+
assert.equal(launchedEnv?.RELAYFILE_URL, 'https://relayfile.test');
|
|
591
|
+
assert.equal(launchedEnv?.RELAYFILE_WORKSPACE_ID, 'ws-test');
|
|
592
|
+
assert.equal(launchedEnv?.RELAYFILE_TOKEN, '');
|
|
593
|
+
assert.equal(launchedEnv?.RELAYFILE_MOUNT_PATHS, '[]');
|
|
594
|
+
}
|
|
595
|
+
finally {
|
|
596
|
+
globalThis.fetch = originalFetch;
|
|
597
|
+
await cleanup();
|
|
598
|
+
}
|
|
599
|
+
});
|
|
600
|
+
test('deploy dev mode preserves env-only provider token fallback without runtime credential masking', async () => {
|
|
601
|
+
const providerTokenSentinel = 'WORKFORCE_ENV_ONLY_PROVIDER_TOKEN';
|
|
602
|
+
const { personaPath, cleanup } = await withTempPersona(basePersonaJson({
|
|
603
|
+
integrations: {
|
|
604
|
+
github: { triggers: [{ on: 'pull_request.opened' }] }
|
|
605
|
+
}
|
|
606
|
+
}));
|
|
607
|
+
const io = createBufferedIO();
|
|
608
|
+
const originalFetch = globalThis.fetch;
|
|
609
|
+
const originalProviderToken = process.env.WORKFORCE_INTEGRATION_GITHUB_TOKEN;
|
|
610
|
+
const urls = [];
|
|
611
|
+
let launched = false;
|
|
612
|
+
let launchedEnv;
|
|
613
|
+
let launchedProcessProviderToken;
|
|
614
|
+
process.env.WORKFORCE_INTEGRATION_GITHUB_TOKEN = providerTokenSentinel;
|
|
615
|
+
globalThis.fetch = (async (input) => {
|
|
616
|
+
const url = String(input);
|
|
617
|
+
urls.push(url);
|
|
618
|
+
if (url.includes('/api/v1/workspaces/ws-test/integrations/github/status')) {
|
|
619
|
+
return jsonResponse({ provider: 'github', status: 'pending' });
|
|
620
|
+
}
|
|
621
|
+
throw new Error(`unexpected URL ${url}`);
|
|
622
|
+
});
|
|
623
|
+
try {
|
|
624
|
+
await deploy({
|
|
625
|
+
personaPath,
|
|
626
|
+
mode: 'dev',
|
|
627
|
+
noPrompt: true,
|
|
628
|
+
cloudUrl: 'https://cloud.example.test',
|
|
629
|
+
io
|
|
630
|
+
}, {
|
|
631
|
+
workspaceAuth: {
|
|
632
|
+
async resolveWorkspace() {
|
|
633
|
+
return { workspace: 'ws-test', token: 'relay_ws_workspace' };
|
|
634
|
+
}
|
|
635
|
+
},
|
|
636
|
+
bundle: successfulBundleStager(),
|
|
637
|
+
modes: {
|
|
638
|
+
dev: {
|
|
639
|
+
async launch(input) {
|
|
640
|
+
launched = true;
|
|
641
|
+
launchedEnv = input.env;
|
|
642
|
+
launchedProcessProviderToken = process.env.WORKFORCE_INTEGRATION_GITHUB_TOKEN;
|
|
643
|
+
return {
|
|
644
|
+
id: 'dev-1',
|
|
645
|
+
async stop() {
|
|
646
|
+
/* no-op */
|
|
647
|
+
},
|
|
648
|
+
done: Promise.resolve({ code: 0 })
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
});
|
|
654
|
+
assert.equal(launched, true);
|
|
655
|
+
assert.equal(launchedEnv, undefined);
|
|
656
|
+
assert.equal(launchedProcessProviderToken, providerTokenSentinel);
|
|
657
|
+
assert.ok(!urls.find((url) => url.endsWith('/runtime-credentials')));
|
|
658
|
+
}
|
|
659
|
+
finally {
|
|
660
|
+
if (originalProviderToken === undefined) {
|
|
661
|
+
delete process.env.WORKFORCE_INTEGRATION_GITHUB_TOKEN;
|
|
662
|
+
}
|
|
663
|
+
else {
|
|
664
|
+
process.env.WORKFORCE_INTEGRATION_GITHUB_TOKEN = originalProviderToken;
|
|
665
|
+
}
|
|
666
|
+
globalThis.fetch = originalFetch;
|
|
667
|
+
await cleanup();
|
|
668
|
+
}
|
|
669
|
+
});
|
|
670
|
+
test('deploy dev mode runtime credential eligibility preserves legacy workspace fallback semantics', async () => {
|
|
671
|
+
const { personaPath, cleanup } = await withTempPersona(basePersonaJson({
|
|
672
|
+
integrations: {
|
|
673
|
+
github: { triggers: [{ on: 'pull_request.opened' }] }
|
|
674
|
+
}
|
|
675
|
+
}));
|
|
676
|
+
const io = createBufferedIO();
|
|
677
|
+
const originalFetch = globalThis.fetch;
|
|
678
|
+
const urls = [];
|
|
679
|
+
let launchedEnv;
|
|
680
|
+
globalThis.fetch = (async (input, init) => {
|
|
681
|
+
const url = String(input);
|
|
682
|
+
urls.push(url);
|
|
683
|
+
if (url.includes('/api/v1/workspaces/ws-test/integrations/github/status')) {
|
|
684
|
+
const parsed = new URL(url);
|
|
685
|
+
return jsonResponse({
|
|
686
|
+
provider: 'github',
|
|
687
|
+
status: parsed.searchParams.get('scope') === 'workspace' ? 'ready' : 'pending'
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
if (url.includes('/api/v1/workspaces/ws-test/runtime-credentials')) {
|
|
691
|
+
return jsonResponse({
|
|
692
|
+
relayfileUrl: 'https://relayfile.test',
|
|
693
|
+
relayfileWorkspaceId: 'ws-test',
|
|
694
|
+
relayfileToken: 'relay_pa_workspace_fallback',
|
|
695
|
+
relayfileMountPaths: ['/github/repos/**/**/pulls/**']
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
throw new Error(`unexpected URL ${url}`);
|
|
699
|
+
});
|
|
700
|
+
try {
|
|
701
|
+
await deploy({
|
|
702
|
+
personaPath,
|
|
703
|
+
mode: 'dev',
|
|
704
|
+
noPrompt: true,
|
|
705
|
+
cloudUrl: 'https://cloud.example.test',
|
|
706
|
+
io
|
|
707
|
+
}, {
|
|
708
|
+
workspaceAuth: {
|
|
709
|
+
async resolveWorkspace() {
|
|
710
|
+
return { workspace: 'ws-test', token: 'relay_ws_workspace' };
|
|
711
|
+
}
|
|
712
|
+
},
|
|
713
|
+
bundle: successfulBundleStager(),
|
|
714
|
+
modes: {
|
|
715
|
+
dev: {
|
|
716
|
+
async launch(input) {
|
|
717
|
+
launchedEnv = input.env;
|
|
718
|
+
return {
|
|
719
|
+
id: 'dev-1',
|
|
720
|
+
async stop() {
|
|
721
|
+
/* no-op */
|
|
722
|
+
},
|
|
723
|
+
done: Promise.resolve({ code: 0 })
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
});
|
|
729
|
+
assert.equal(launchedEnv?.RELAYFILE_TOKEN, 'relay_pa_workspace_fallback');
|
|
730
|
+
assert.deepEqual(urls.filter((url) => url.includes('/integrations/github/status')), [
|
|
731
|
+
'https://cloud.example.test/api/v1/workspaces/ws-test/integrations/github/status?scope=deployer_user',
|
|
732
|
+
'https://cloud.example.test/api/v1/workspaces/ws-test/integrations/github/status?scope=workspace',
|
|
733
|
+
'https://cloud.example.test/api/v1/workspaces/ws-test/integrations/github/status?scope=deployer_user',
|
|
734
|
+
'https://cloud.example.test/api/v1/workspaces/ws-test/integrations/github/status?scope=workspace'
|
|
735
|
+
]);
|
|
736
|
+
assert.ok(urls.find((url) => url.endsWith('/runtime-credentials')));
|
|
737
|
+
}
|
|
738
|
+
finally {
|
|
739
|
+
globalThis.fetch = originalFetch;
|
|
740
|
+
await cleanup();
|
|
741
|
+
}
|
|
742
|
+
});
|
|
743
|
+
test('deploy dev mode runtime credential eligibility preserves expected provider config key semantics', async () => {
|
|
744
|
+
const providerTokenSentinel = 'WORKFORCE_ENV_CONFIG_MISMATCH_TOKEN';
|
|
745
|
+
const { personaPath, cleanup } = await withTempPersona(basePersonaJson({
|
|
746
|
+
integrations: {
|
|
747
|
+
github: { triggers: [{ on: 'pull_request.opened' }] }
|
|
748
|
+
}
|
|
749
|
+
}));
|
|
750
|
+
const io = createBufferedIO();
|
|
751
|
+
const originalFetch = globalThis.fetch;
|
|
752
|
+
const originalProviderToken = process.env.WORKFORCE_INTEGRATION_GITHUB_TOKEN;
|
|
753
|
+
const urls = [];
|
|
754
|
+
let launchedEnv;
|
|
755
|
+
let launchedProcessProviderToken;
|
|
756
|
+
process.env.WORKFORCE_INTEGRATION_GITHUB_TOKEN = providerTokenSentinel;
|
|
757
|
+
globalThis.fetch = (async (input) => {
|
|
758
|
+
const url = String(input);
|
|
759
|
+
urls.push(url);
|
|
760
|
+
if (url.includes('/api/v1/workspaces/ws-test/integrations/github/status')) {
|
|
761
|
+
return jsonResponse({
|
|
762
|
+
provider: 'github',
|
|
763
|
+
providerConfigKey: 'github-other',
|
|
764
|
+
status: 'ready'
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
throw new Error(`unexpected URL ${url}`);
|
|
768
|
+
});
|
|
769
|
+
try {
|
|
770
|
+
await deploy({
|
|
771
|
+
personaPath,
|
|
772
|
+
mode: 'dev',
|
|
773
|
+
noPrompt: true,
|
|
774
|
+
cloudUrl: 'https://cloud.example.test',
|
|
775
|
+
io
|
|
776
|
+
}, {
|
|
777
|
+
workspaceAuth: {
|
|
778
|
+
async resolveWorkspace() {
|
|
779
|
+
return { workspace: 'ws-test', token: 'relay_ws_workspace' };
|
|
780
|
+
}
|
|
781
|
+
},
|
|
782
|
+
providerConfigKeys: {
|
|
783
|
+
async resolve(provider) {
|
|
784
|
+
assert.equal(provider, 'github');
|
|
785
|
+
return 'github-relay';
|
|
786
|
+
}
|
|
787
|
+
},
|
|
788
|
+
bundle: successfulBundleStager(),
|
|
789
|
+
modes: {
|
|
790
|
+
dev: {
|
|
791
|
+
async launch(input) {
|
|
792
|
+
launchedEnv = input.env;
|
|
793
|
+
launchedProcessProviderToken = process.env.WORKFORCE_INTEGRATION_GITHUB_TOKEN;
|
|
794
|
+
return {
|
|
795
|
+
id: 'dev-1',
|
|
796
|
+
async stop() {
|
|
797
|
+
/* no-op */
|
|
798
|
+
},
|
|
799
|
+
done: Promise.resolve({ code: 0 })
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
});
|
|
805
|
+
assert.equal(launchedEnv, undefined);
|
|
806
|
+
assert.equal(launchedProcessProviderToken, providerTokenSentinel);
|
|
807
|
+
assert.ok(!urls.find((url) => url.endsWith('/runtime-credentials')));
|
|
808
|
+
}
|
|
809
|
+
finally {
|
|
810
|
+
if (originalProviderToken === undefined) {
|
|
811
|
+
delete process.env.WORKFORCE_INTEGRATION_GITHUB_TOKEN;
|
|
812
|
+
}
|
|
813
|
+
else {
|
|
814
|
+
process.env.WORKFORCE_INTEGRATION_GITHUB_TOKEN = originalProviderToken;
|
|
815
|
+
}
|
|
816
|
+
globalThis.fetch = originalFetch;
|
|
817
|
+
await cleanup();
|
|
818
|
+
}
|
|
819
|
+
});
|
|
820
|
+
test('deploy dev mode rejects malformed runtime credential tokens before launch', async () => {
|
|
821
|
+
const { personaPath, cleanup } = await withTempPersona(basePersonaJson({
|
|
822
|
+
integrations: {
|
|
823
|
+
github: { triggers: [{ on: 'pull_request.opened' }] }
|
|
824
|
+
}
|
|
825
|
+
}));
|
|
826
|
+
const io = createBufferedIO();
|
|
827
|
+
const originalFetch = globalThis.fetch;
|
|
828
|
+
let launched = false;
|
|
829
|
+
globalThis.fetch = (async (input) => {
|
|
830
|
+
const url = String(input);
|
|
831
|
+
if (url.includes('/api/v1/workspaces/ws-test/integrations/github/status')) {
|
|
832
|
+
return jsonResponse({ provider: 'github', status: 'ready' });
|
|
833
|
+
}
|
|
834
|
+
if (url.includes('/api/v1/workspaces/ws-test/runtime-credentials')) {
|
|
835
|
+
return jsonResponse({
|
|
836
|
+
relayfileUrl: 'https://relayfile.test',
|
|
837
|
+
relayfileWorkspaceId: 'ws-test',
|
|
838
|
+
relayfileToken: 'not_relay_pa',
|
|
839
|
+
relayfileMountPaths: ['/github/repos/**/**/pulls/**']
|
|
840
|
+
});
|
|
841
|
+
}
|
|
842
|
+
throw new Error(`unexpected URL ${url}`);
|
|
843
|
+
});
|
|
844
|
+
try {
|
|
845
|
+
await assert.rejects(deploy({
|
|
846
|
+
personaPath,
|
|
847
|
+
mode: 'dev',
|
|
848
|
+
noPrompt: true,
|
|
849
|
+
cloudUrl: 'https://cloud.example.test',
|
|
850
|
+
io
|
|
851
|
+
}, {
|
|
852
|
+
workspaceAuth: {
|
|
853
|
+
async resolveWorkspace() {
|
|
854
|
+
return { workspace: 'ws-test', token: 'relay_ws_workspace' };
|
|
855
|
+
}
|
|
856
|
+
},
|
|
857
|
+
bundle: successfulBundleStager(),
|
|
858
|
+
modes: { dev: successfulDevLauncher(() => { launched = true; }) }
|
|
859
|
+
}), /runtime-credentials returned a token without expected relay_pa_ prefix/);
|
|
860
|
+
assert.equal(launched, false);
|
|
861
|
+
}
|
|
862
|
+
finally {
|
|
863
|
+
globalThis.fetch = originalFetch;
|
|
864
|
+
await cleanup();
|
|
865
|
+
}
|
|
866
|
+
});
|
|
867
|
+
test('deploy dev mode rejects runtime credential tokens without mount paths before launch', async () => {
|
|
868
|
+
const { personaPath, cleanup } = await withTempPersona(basePersonaJson({
|
|
869
|
+
integrations: {
|
|
870
|
+
github: { triggers: [{ on: 'pull_request.opened' }] }
|
|
871
|
+
}
|
|
872
|
+
}));
|
|
873
|
+
const io = createBufferedIO();
|
|
874
|
+
const originalFetch = globalThis.fetch;
|
|
875
|
+
let launched = false;
|
|
876
|
+
globalThis.fetch = (async (input) => {
|
|
877
|
+
const url = String(input);
|
|
878
|
+
if (url.includes('/api/v1/workspaces/ws-test/integrations/github/status')) {
|
|
879
|
+
return jsonResponse({ provider: 'github', status: 'ready' });
|
|
880
|
+
}
|
|
881
|
+
if (url.includes('/api/v1/workspaces/ws-test/runtime-credentials')) {
|
|
882
|
+
return jsonResponse({
|
|
883
|
+
relayfileUrl: 'https://relayfile.test',
|
|
884
|
+
relayfileWorkspaceId: 'ws-test',
|
|
885
|
+
relayfileToken: 'relay_pa_missing_scope',
|
|
886
|
+
relayfileMountPaths: []
|
|
887
|
+
});
|
|
888
|
+
}
|
|
889
|
+
throw new Error(`unexpected URL ${url}`);
|
|
890
|
+
});
|
|
891
|
+
try {
|
|
892
|
+
await assert.rejects(deploy({
|
|
893
|
+
personaPath,
|
|
894
|
+
mode: 'dev',
|
|
895
|
+
noPrompt: true,
|
|
896
|
+
cloudUrl: 'https://cloud.example.test',
|
|
897
|
+
io
|
|
898
|
+
}, {
|
|
899
|
+
workspaceAuth: {
|
|
900
|
+
async resolveWorkspace() {
|
|
901
|
+
return { workspace: 'ws-test', token: 'relay_ws_workspace' };
|
|
902
|
+
}
|
|
903
|
+
},
|
|
904
|
+
bundle: successfulBundleStager(),
|
|
905
|
+
modes: { dev: successfulDevLauncher(() => { launched = true; }) }
|
|
906
|
+
}), /runtime-credentials returned a token without relayfile mount paths/);
|
|
907
|
+
assert.equal(launched, false);
|
|
908
|
+
}
|
|
909
|
+
finally {
|
|
910
|
+
globalThis.fetch = originalFetch;
|
|
911
|
+
await cleanup();
|
|
912
|
+
}
|
|
913
|
+
});
|
|
914
|
+
test('deploy dev mode fails closed before runtime credentials when workspace token is missing', async () => {
|
|
915
|
+
const { personaPath, cleanup } = await withTempPersona(basePersonaJson({
|
|
916
|
+
integrations: {
|
|
917
|
+
github: { triggers: [{ on: 'pull_request.opened' }] }
|
|
918
|
+
}
|
|
919
|
+
}));
|
|
920
|
+
const originalFetch = globalThis.fetch;
|
|
921
|
+
let fetchCalled = false;
|
|
922
|
+
globalThis.fetch = (async () => {
|
|
923
|
+
fetchCalled = true;
|
|
924
|
+
throw new Error('runtime credentials should not be requested without a workspace token');
|
|
925
|
+
});
|
|
926
|
+
try {
|
|
927
|
+
await assert.rejects(deploy({
|
|
928
|
+
personaPath,
|
|
929
|
+
mode: 'dev',
|
|
930
|
+
noPrompt: true,
|
|
931
|
+
cloudUrl: 'https://cloud.example.test',
|
|
932
|
+
io: createBufferedIO()
|
|
933
|
+
}, {
|
|
934
|
+
workspaceAuth: {
|
|
935
|
+
async resolveWorkspace() {
|
|
936
|
+
return { workspace: 'ws-test', token: undefined };
|
|
937
|
+
}
|
|
938
|
+
},
|
|
939
|
+
bundle: successfulBundleStager(),
|
|
940
|
+
modes: { dev: successfulDevLauncher() }
|
|
941
|
+
}), /workspace token is required for deploy/);
|
|
942
|
+
assert.equal(fetchCalled, false);
|
|
943
|
+
}
|
|
944
|
+
finally {
|
|
945
|
+
globalThis.fetch = originalFetch;
|
|
946
|
+
await cleanup();
|
|
947
|
+
}
|
|
948
|
+
});
|
|
949
|
+
test('deploy dev mode still fails fast for genuinely unconnected workspace integrations with --no-prompt', async () => {
|
|
950
|
+
const { personaPath, cleanup } = await withTempPersona(basePersonaJson({
|
|
951
|
+
integrations: {
|
|
952
|
+
github: { triggers: [{ on: 'pull_request.opened' }] }
|
|
953
|
+
}
|
|
954
|
+
}));
|
|
955
|
+
const io = createBufferedIO();
|
|
956
|
+
const originalFetch = globalThis.fetch;
|
|
957
|
+
const urls = [];
|
|
958
|
+
globalThis.fetch = (async (input) => {
|
|
959
|
+
const url = String(input);
|
|
960
|
+
urls.push(url);
|
|
961
|
+
if (url.includes('/api/v1/workspaces/ws-test/integrations/github/status')) {
|
|
962
|
+
return jsonResponse({ provider: 'github', status: 'pending' });
|
|
963
|
+
}
|
|
964
|
+
throw new Error(`unexpected URL ${url}`);
|
|
965
|
+
});
|
|
966
|
+
try {
|
|
967
|
+
await assert.rejects(deploy({
|
|
968
|
+
personaPath,
|
|
969
|
+
mode: 'dev',
|
|
970
|
+
noPrompt: true,
|
|
971
|
+
cloudUrl: 'https://cloud.example.test',
|
|
972
|
+
io
|
|
973
|
+
}, {
|
|
974
|
+
workspaceAuth: {
|
|
975
|
+
async resolveWorkspace() {
|
|
976
|
+
return { workspace: 'ws-test', token: 'relay_ws_workspace' };
|
|
977
|
+
}
|
|
978
|
+
},
|
|
979
|
+
bundle: successfulBundleStager(),
|
|
980
|
+
modes: { dev: successfulDevLauncher() }
|
|
981
|
+
}), /deploy aborted: 1 integration\(s\) failed to connect: github/);
|
|
982
|
+
assert.ok(!urls.find((url) => url.endsWith('/runtime-credentials')));
|
|
983
|
+
}
|
|
984
|
+
finally {
|
|
985
|
+
globalThis.fetch = originalFetch;
|
|
986
|
+
await cleanup();
|
|
987
|
+
}
|
|
988
|
+
});
|
|
434
989
|
test('deploy aborts cleanly when one missing integration connect fails', async () => {
|
|
435
990
|
const { personaPath, cleanup } = await withTempPersona(basePersonaJson({ integrations: { github: {}, notion: {} } }));
|
|
436
991
|
const io = createBufferedIO();
|