@testdriverai/agent 7.9.0-canary.3 → 7.9.0-canary.5
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/agent/lib/sandbox.js +55 -6
- package/lib/sentry.js +5 -1
- package/package.json +1 -1
package/agent/lib/sandbox.js
CHANGED
|
@@ -12,6 +12,7 @@ const createSandbox = function (emitter, analytics, sessionInstance) {
|
|
|
12
12
|
this._ably = null;
|
|
13
13
|
this._sessionChannel = null;
|
|
14
14
|
this._channelName = null;
|
|
15
|
+
this._membersChannelName = null;
|
|
15
16
|
this.ps = {};
|
|
16
17
|
this._execBuffers = {}; // accumulate streamed exec.output chunks per requestId
|
|
17
18
|
this.heartbeat = null;
|
|
@@ -53,7 +54,7 @@ const createSandbox = function (emitter, analytics, sessionInstance) {
|
|
|
53
54
|
return this._publishCount;
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
async _initAbly(ablyToken, channelName) {
|
|
57
|
+
async _initAbly(ablyToken, channelName, membersChannelName) {
|
|
57
58
|
if (this._ably) {
|
|
58
59
|
try {
|
|
59
60
|
this._ably.close();
|
|
@@ -62,6 +63,19 @@ const createSandbox = function (emitter, analytics, sessionInstance) {
|
|
|
62
63
|
}
|
|
63
64
|
}
|
|
64
65
|
this._channelName = channelName;
|
|
66
|
+
|
|
67
|
+
// Derive the members channel from the session channel if not supplied.
|
|
68
|
+
// Format: testdriver:{env}:{teamId}:{sandboxId} → testdriver:{env}:{teamId}:members
|
|
69
|
+
if (!membersChannelName) {
|
|
70
|
+
const parts = channelName.split(":");
|
|
71
|
+
if (parts.length >= 3) {
|
|
72
|
+
membersChannelName = parts.slice(0, 3).join(":") + ":members";
|
|
73
|
+
} else {
|
|
74
|
+
logger.warn("[ably] Channel name format unexpected (" + channelName + "), cannot derive members channel");
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
this._membersChannelName = membersChannelName;
|
|
78
|
+
|
|
65
79
|
var self = this;
|
|
66
80
|
|
|
67
81
|
this._ably = new Ably.Realtime({
|
|
@@ -103,9 +117,32 @@ const createSandbox = function (emitter, analytics, sessionInstance) {
|
|
|
103
117
|
|
|
104
118
|
this._sessionChannel = this._ably.channels.get(channelName);
|
|
105
119
|
|
|
106
|
-
|
|
120
|
+
// Explicitly attach the session channel BEFORE entering presence on the
|
|
121
|
+
// members channel. Entering members-presence triggers the API's waitpoint
|
|
122
|
+
// completion → claim-slot task → publishes slot-approved on the session
|
|
123
|
+
// channel. If the session channel isn't attached yet, that message lands
|
|
124
|
+
// before our attachment point and historyBeforeSubscribe() won't see it.
|
|
125
|
+
await this._sessionChannel.attach();
|
|
126
|
+
logger.debug(`[realtime] Channel attached: ${channelName}`);
|
|
127
|
+
|
|
128
|
+
// Enter presence on the team members channel so the API can count
|
|
129
|
+
// connected SDK clients with a single direct lookup per team.
|
|
130
|
+
if (membersChannelName) {
|
|
131
|
+
try {
|
|
132
|
+
var membersChannel = this._ably.channels.get(membersChannelName);
|
|
133
|
+
await membersChannel.presence.enter({
|
|
134
|
+
sandboxId: this._sandboxId,
|
|
135
|
+
connectedAt: Date.now(),
|
|
136
|
+
});
|
|
137
|
+
logger.debug(`[realtime] Entered presence on members channel (sandbox=${this._sandboxId})`);
|
|
138
|
+
} catch (e) {
|
|
139
|
+
// Non-fatal — presence is used for concurrency counting, not critical path
|
|
140
|
+
logger.warn("Failed to enter presence on members channel: " + (e.message || e));
|
|
141
|
+
}
|
|
142
|
+
}
|
|
107
143
|
|
|
108
|
-
// Enter presence on the session channel so the API
|
|
144
|
+
// Enter presence on the session channel so the API's session monitor can
|
|
145
|
+
// detect SDK connect/disconnect events for this sandbox.
|
|
109
146
|
try {
|
|
110
147
|
await this._sessionChannel.presence.enter({
|
|
111
148
|
sandboxId: this._sandboxId,
|
|
@@ -113,7 +150,7 @@ const createSandbox = function (emitter, analytics, sessionInstance) {
|
|
|
113
150
|
});
|
|
114
151
|
logger.debug(`[realtime] Entered presence on session channel (sandbox=${this._sandboxId})`);
|
|
115
152
|
} catch (e) {
|
|
116
|
-
// Non-fatal — presence is used for
|
|
153
|
+
// Non-fatal — presence is used for disconnect detection, not critical path
|
|
117
154
|
logger.warn("Failed to enter presence on session channel: " + (e.message || e));
|
|
118
155
|
}
|
|
119
156
|
|
|
@@ -510,6 +547,8 @@ const createSandbox = function (emitter, analytics, sessionInstance) {
|
|
|
510
547
|
timeout,
|
|
511
548
|
);
|
|
512
549
|
|
|
550
|
+
logger.debug(`[sandbox] authenticate response: success=${reply.success} status=${reply.status || 'none'} sandboxId=${reply.sandboxId || 'none'}`);
|
|
551
|
+
|
|
513
552
|
if (!reply.success) {
|
|
514
553
|
var err = new Error(
|
|
515
554
|
reply.errorMessage || "Failed to allocate sandbox",
|
|
@@ -522,7 +561,8 @@ const createSandbox = function (emitter, analytics, sessionInstance) {
|
|
|
522
561
|
this._teamId = reply.teamId;
|
|
523
562
|
|
|
524
563
|
if (reply.ably && reply.ably.token) {
|
|
525
|
-
|
|
564
|
+
logger.debug(`[sandbox] Initializing Ably with channel=${reply.ably.channel}, membersChannel=${reply.ably.membersChannel || '(derived)'}`);
|
|
565
|
+
await this._initAbly(reply.ably.token, reply.ably.channel, reply.ably.membersChannel);
|
|
526
566
|
this.instanceSocketConnected = true;
|
|
527
567
|
|
|
528
568
|
// Tell the runner to enable debug log forwarding if debug mode is on
|
|
@@ -547,6 +587,7 @@ const createSandbox = function (emitter, analytics, sessionInstance) {
|
|
|
547
587
|
var slotPollStart = Date.now();
|
|
548
588
|
while (reply.status === 'pending') {
|
|
549
589
|
logger.log('Slot claim pending — waiting for approval via Ably...');
|
|
590
|
+
logger.debug(`[slots] sandboxId=${this._sandboxId} channel=${this._channelName} membersChannel=${this._membersChannelName}`);
|
|
550
591
|
|
|
551
592
|
var self = this;
|
|
552
593
|
var slotResolved = false;
|
|
@@ -587,24 +628,32 @@ const createSandbox = function (emitter, analytics, sessionInstance) {
|
|
|
587
628
|
// The claim-slot task fires in response to presence enter, so the
|
|
588
629
|
// decision may already be published by the time we get here.
|
|
589
630
|
var slotControlSub = await self._sessionChannel.subscribe('control', onSlotControl);
|
|
631
|
+
logger.debug(`[slots] Subscribed to control channel for slot decision (sandboxId=${this._sandboxId})`);
|
|
590
632
|
|
|
591
633
|
// Check for decisions published before this subscription was active
|
|
592
634
|
if (!slotResolved && slotControlSub) {
|
|
593
635
|
try {
|
|
636
|
+
logger.debug(`[slots] Checking history for pre-subscription slot decisions (sandboxId=${this._sandboxId})`);
|
|
637
|
+
logger.debug(`[slots] Checking history for pre-subscription slot decisions (sandboxId=${this._sandboxId})`);
|
|
594
638
|
var histPage = await slotControlSub.historyBeforeSubscribe({ limit: 10 });
|
|
639
|
+
var histItemCount = 0;
|
|
595
640
|
while (histPage && !slotResolved) {
|
|
596
641
|
for (var hi = 0; hi < histPage.items.length; hi++) {
|
|
642
|
+
histItemCount++;
|
|
643
|
+
logger.debug(`[slots] History item: type=${histPage.items[hi].data && histPage.items[hi].data.type} (sandboxId=${this._sandboxId})`);
|
|
597
644
|
onSlotControl(histPage.items[hi]);
|
|
598
645
|
if (slotResolved) break;
|
|
599
646
|
}
|
|
600
647
|
histPage = (!slotResolved && histPage.hasNext()) ? await histPage.next() : null;
|
|
601
648
|
}
|
|
649
|
+
logger.debug(`[slots] History check done: ${histItemCount} items checked, resolved=${slotResolved} (sandboxId=${this._sandboxId})`);
|
|
602
650
|
} catch (histErr) {
|
|
603
651
|
logger.warn('[slots] Failed to check history for slot decision: ' + (histErr.message || histErr));
|
|
604
652
|
}
|
|
605
653
|
}
|
|
606
654
|
|
|
607
655
|
var slotDecision = await slotDecisionPromise;
|
|
656
|
+
logger.debug(`[slots] Slot decision received: approved=${slotDecision.approved} sandboxId=${this._sandboxId} elapsedMs=${Date.now() - slotPollStart}`);
|
|
608
657
|
|
|
609
658
|
if (!slotDecision.approved) {
|
|
610
659
|
// Slot denied — disconnect Ably and re-try the full authenticate
|
|
@@ -648,7 +697,7 @@ const createSandbox = function (emitter, analytics, sessionInstance) {
|
|
|
648
697
|
if (reply.status === 'pending' && reply.ably && reply.ably.token) {
|
|
649
698
|
this._sandboxId = reply.sandboxId;
|
|
650
699
|
this._teamId = reply.teamId;
|
|
651
|
-
await this._initAbly(reply.ably.token, reply.ably.channel);
|
|
700
|
+
await this._initAbly(reply.ably.token, reply.ably.channel, reply.ably.membersChannel);
|
|
652
701
|
this.instanceSocketConnected = true;
|
|
653
702
|
}
|
|
654
703
|
|
package/lib/sentry.js
CHANGED
|
@@ -14,7 +14,7 @@ const Sentry = require("@sentry/node");
|
|
|
14
14
|
const crypto = require("crypto");
|
|
15
15
|
const os = require("os");
|
|
16
16
|
const { version } = require("../package.json");
|
|
17
|
-
const { sentryEnvironment } = require("./resolve-channel");
|
|
17
|
+
const { active, sentryEnvironment } = require("./resolve-channel");
|
|
18
18
|
const logger = require("../agent/lib/logger");
|
|
19
19
|
|
|
20
20
|
// Store trace contexts per session so concurrent tests don't overwrite each other.
|
|
@@ -33,6 +33,10 @@ const isEnabled = () => {
|
|
|
33
33
|
if (process.env.TD_TELEMETRY === "false") {
|
|
34
34
|
return false;
|
|
35
35
|
}
|
|
36
|
+
// Disable in test channel and PR previews
|
|
37
|
+
if (active === 'test' || process.env.IS_PULL_REQUEST === 'true') {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
36
40
|
return true;
|
|
37
41
|
};
|
|
38
42
|
|