bdy 1.23.11-stage → 1.23.13-beta
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/distTs/package.json +3 -3
- package/distTs/src/agent/manager.js +9 -2
- package/distTs/src/command/pre.js +9 -8
- package/distTs/src/command/version.js +1 -1
- package/distTs/src/logger.js +7 -1
- package/distTs/src/tunnel/server/sftp.js +24 -5
- package/distTs/src/tunnel/ssh/client.js +35 -4
- package/distTs/src/utils.js +49 -14
- package/package.json +3 -3
package/distTs/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bdy",
|
|
3
3
|
"preferGlobal": false,
|
|
4
|
-
"version": "1.23.
|
|
4
|
+
"version": "1.23.13-beta",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://buddy.works/docs/cli",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"@fastify/cors": "11.2.0",
|
|
52
52
|
"@inquirer/prompts": "8.5.2",
|
|
53
53
|
"@modelcontextprotocol/sdk": "1.30.0",
|
|
54
|
-
"@puppeteer/browsers": "2.
|
|
54
|
+
"@puppeteer/browsers": "3.2.0",
|
|
55
55
|
"@scalar/json-magic": "0.12.16",
|
|
56
56
|
"@scalar/openapi-parser": "0.28.7",
|
|
57
57
|
"@scalar/openapi-types": "0.9.1",
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"picomatch": "4.0.4",
|
|
80
80
|
"pino": "10.3.1",
|
|
81
81
|
"punycode": "2.3.1",
|
|
82
|
-
"puppeteer-core": "
|
|
82
|
+
"puppeteer-core": "25.6.0",
|
|
83
83
|
"range-parser": "1.2.1",
|
|
84
84
|
"socket.io-client": "4.8.3",
|
|
85
85
|
"ssh2": "1.17.0",
|
|
@@ -161,6 +161,10 @@ class AgentManagerClass {
|
|
|
161
161
|
}
|
|
162
162
|
const current = (0, utils_1.getVersion)();
|
|
163
163
|
const latest = await (0, utils_1.getLatestVersion)();
|
|
164
|
+
// Equality only, deliberately not "is latest newer": this runs on an explicit remote
|
|
165
|
+
// update action, whose meaning is "match what the channel publishes". Refusing an
|
|
166
|
+
// older latest would take away the only rollback the fleet has - moving the channel
|
|
167
|
+
// pointer back after a bad release.
|
|
164
168
|
if (!latest || latest === '0.0.0' || latest === current) {
|
|
165
169
|
logger_1.default.info((0, texts_1.LOG_AGENT_UPDATE_NOT_NEEDED)(current));
|
|
166
170
|
// Nothing to do is not a failure, and showing it as one would make an update of
|
|
@@ -174,12 +178,15 @@ class AgentManagerClass {
|
|
|
174
178
|
// - this is the value that stays behind if the restart never lands.
|
|
175
179
|
this.agent?.reportUpdateStatus(tunnel_1.TUNNEL_AGENT_UPDATE_STATUS.STARTED);
|
|
176
180
|
this.agent?.flushUpdateStatus();
|
|
177
|
-
|
|
181
|
+
// getLatestVersion already refuses a version it cannot parse, so this is belt and
|
|
182
|
+
// braces - kept because the download url is built from the triple, and shipping a
|
|
183
|
+
// malformed one would fail much further away from the cause.
|
|
184
|
+
const target = (0, utils_1.parseVersion)(latest);
|
|
178
185
|
if (!target) {
|
|
179
186
|
logger_1.default.error(texts_1.LOG_AGENT_UPDATE_FAILED);
|
|
180
187
|
return report(tunnel_1.TUNNEL_AGENT_UPDATE_STATUS.FAILED);
|
|
181
188
|
}
|
|
182
|
-
const swapped = await this.system.swapBinaryToVersion(target
|
|
189
|
+
const swapped = await this.system.swapBinaryToVersion(target.version);
|
|
183
190
|
if (!swapped) {
|
|
184
191
|
logger_1.default.error((0, texts_1.LOG_AGENT_UPDATE_BINARY_INVALID)(latest));
|
|
185
192
|
return report(tunnel_1.TUNNEL_AGENT_UPDATE_STATUS.FAILED);
|
|
@@ -46,14 +46,15 @@ const commandPre = async (_, command) => {
|
|
|
46
46
|
if (!output_1.default.isTTY() || command.hideVersionUpdate || ['json', 'jsonl'].includes(command.opts()?.format)) {
|
|
47
47
|
return;
|
|
48
48
|
}
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
49
|
+
// No truthiness guards on the versions: an empty or unparsable one is never newer
|
|
50
|
+
const latest = command.latestVersion || '';
|
|
51
|
+
const newCli = (0, utils_1.isVersionNewer)(latest, command.currentVersion || '');
|
|
52
|
+
const newAgent = !!command.agentStatus && (0, utils_1.isVersionNewer)(latest, command.agentStatus.version);
|
|
53
|
+
if ((0, utils_1.isDocker)() && newCli) {
|
|
54
|
+
output_1.default.newCliDockerVersion(latest);
|
|
55
|
+
}
|
|
56
|
+
else if (newCli) {
|
|
57
|
+
output_1.default.newCliVersion(latest);
|
|
57
58
|
}
|
|
58
59
|
else if (newAgent) {
|
|
59
60
|
output_1.default.newAgentVersion();
|
|
@@ -78,7 +78,7 @@ commandVersion.action(async () => {
|
|
|
78
78
|
const currVersion = commandVersion.currentVersion || '';
|
|
79
79
|
const lastVersion = commandVersion.latestVersion || '';
|
|
80
80
|
const env = commandVersion.env || '';
|
|
81
|
-
if (
|
|
81
|
+
if ((0, utils_1.isVersionNewer)(lastVersion, currVersion)) {
|
|
82
82
|
const platform = (0, utils_1.getPlatform)();
|
|
83
83
|
const isMac = platform === 'darwin';
|
|
84
84
|
const isWin = platform === 'win32';
|
package/distTs/src/logger.js
CHANGED
|
@@ -64,7 +64,13 @@ class Logger {
|
|
|
64
64
|
this.logPath = (0, path_1.resolve)(this.rootPath, 'cli.log');
|
|
65
65
|
this.log1Path = (0, path_1.resolve)(this.rootPath, 'cli.1.log');
|
|
66
66
|
try {
|
|
67
|
-
|
|
67
|
+
// 'a', not 'w': every start used to truncate the log, so an agent that died and was
|
|
68
|
+
// restarted by its service manager came back having erased the only record of why -
|
|
69
|
+
// and a crash loop leaves nothing at all. Append also survives an external truncation
|
|
70
|
+
// (rotation, someone clearing the file) without the zero-padding a tracked write offset
|
|
71
|
+
// produces, because O_APPEND puts every write at the current end of the file.
|
|
72
|
+
// checkLogSize() below still bounds it at 5 MB.
|
|
73
|
+
this.logStream = fs_1.default.openSync(this.logPath, 'a');
|
|
68
74
|
}
|
|
69
75
|
catch {
|
|
70
76
|
// A log we cannot open is not a reason to fail the command - a read-only home, a full
|
|
@@ -82,11 +82,19 @@ class ServerSftp {
|
|
|
82
82
|
s._protocol.exitStatus(s.outgoing.id, 0);
|
|
83
83
|
s._protocol.channelEOF(s.outgoing.id);
|
|
84
84
|
}
|
|
85
|
+
// origDestroy() inside the try, not after it: it sends CHANNEL_CLOSE, and a peer that
|
|
86
|
+
// simply vanished leaves the channel 'open' while ssh2 has already torn the connection
|
|
87
|
+
// down (Protocol.cleanup on socket close). With zlib@openssh.com negotiated - which the
|
|
88
|
+
// java client on the other end of a deployment always picks - that write reaches a
|
|
89
|
+
// destroyed zlib and throws 'Invalid Zlib instance' out of ZlibPacketWriter.finalize.
|
|
90
|
+
// It escaped from inside a 'close' handler, so it took down the whole tunnel and, past
|
|
91
|
+
// the agent's uncaughtException hook, the process with it. There is nothing to salvage
|
|
92
|
+
// by letting it out: the channel this belongs to is already gone.
|
|
93
|
+
return origDestroy();
|
|
85
94
|
}
|
|
86
95
|
catch {
|
|
87
|
-
|
|
96
|
+
return undefined;
|
|
88
97
|
}
|
|
89
|
-
return origDestroy();
|
|
90
98
|
};
|
|
91
99
|
this.sftp.on('OPEN', (reqId, fileName, flags, attrs) => this.open(reqId, fileName, flags, attrs));
|
|
92
100
|
this.sftp.on('READ', (reqId, handle, offset, length) => this.read(reqId, handle, offset, length));
|
|
@@ -604,13 +612,24 @@ class ServerSftp {
|
|
|
604
612
|
}
|
|
605
613
|
destroy() {
|
|
606
614
|
if (this.sftp) {
|
|
607
|
-
|
|
608
|
-
|
|
615
|
+
try {
|
|
616
|
+
this.sftp.removeAllListeners();
|
|
617
|
+
// end() writes: EOF and CHANNEL_CLOSE go out through the connection this channel sits
|
|
618
|
+
// on, which on the teardown path is usually already gone. Whatever that write throws,
|
|
619
|
+
// the handles below still have to be released - and this runs from a 'close' handler,
|
|
620
|
+
// where an escaping error reaches uncaughtException.
|
|
621
|
+
this.sftp.end();
|
|
622
|
+
}
|
|
623
|
+
catch {
|
|
624
|
+
// do nothing
|
|
625
|
+
}
|
|
609
626
|
}
|
|
610
627
|
if (this.openHandlers) {
|
|
611
628
|
this.openHandlers.forEach((fd, id) => {
|
|
612
629
|
try {
|
|
613
|
-
|
|
630
|
+
// FileHandle.close() and Dir.close() both return a promise. try/catch cannot see a
|
|
631
|
+
// rejected one, and an unhandled rejection is as fatal to the agent as a throw.
|
|
632
|
+
Promise.resolve(fd.close()).catch(() => { });
|
|
614
633
|
}
|
|
615
634
|
catch {
|
|
616
635
|
// do nothing
|
|
@@ -16,6 +16,8 @@ class SshClient extends events_1.default {
|
|
|
16
16
|
password;
|
|
17
17
|
keepalive;
|
|
18
18
|
client;
|
|
19
|
+
lastError;
|
|
20
|
+
openedAt;
|
|
19
21
|
constructor(ip, port, username, password) {
|
|
20
22
|
super();
|
|
21
23
|
this.ip = ip;
|
|
@@ -23,8 +25,25 @@ class SshClient extends events_1.default {
|
|
|
23
25
|
this.username = username;
|
|
24
26
|
this.password = password;
|
|
25
27
|
this.keepalive = false;
|
|
28
|
+
this.lastError = null;
|
|
29
|
+
this.openedAt = 0;
|
|
26
30
|
logger_1.default.debug(`New ssh client: ${username}:${password}@${ip}:${port}`);
|
|
27
31
|
}
|
|
32
|
+
// Why the tunnel connection went away, for the one line openKeepAlive logs on its way to
|
|
33
|
+
// reconnecting. ssh2 emits 'error' and destroys the socket itself on everything fatal - a
|
|
34
|
+
// keepalive timeout, a protocol error, a socket reset - so without keeping that error the
|
|
35
|
+
// 'close' that follows is indistinguishable from the peer hanging up on us, and a tunnel that
|
|
36
|
+
// drops mid-transfer looks like nothing happened at all.
|
|
37
|
+
closeReason() {
|
|
38
|
+
const err = this.lastError;
|
|
39
|
+
const reason = err
|
|
40
|
+
? `${err.level ? `${err.level}: ` : ''}${err.message}`
|
|
41
|
+
: 'closed by peer or locally, no error reported';
|
|
42
|
+
if (!this.openedAt)
|
|
43
|
+
return `SSH client closed before it was ready (${reason})`;
|
|
44
|
+
const age = Math.round((Date.now() - this.openedAt) / 1000);
|
|
45
|
+
return `SSH client closed after ${age}s (${reason})`;
|
|
46
|
+
}
|
|
28
47
|
async open() {
|
|
29
48
|
return new Promise((resolve, reject) => {
|
|
30
49
|
this.close();
|
|
@@ -33,8 +52,14 @@ class SshClient extends events_1.default {
|
|
|
33
52
|
this.client.on('ready', () => {
|
|
34
53
|
if (this.client) {
|
|
35
54
|
this.client.removeAllListeners();
|
|
36
|
-
this
|
|
55
|
+
// Kept rather than swallowed: this is the only place an error on a live tunnel
|
|
56
|
+
// connection is ever seen. It must still not throw, so it stays a handler that only
|
|
57
|
+
// records - closeReason() reads it once the 'close' lands.
|
|
58
|
+
this.client.on('error', (err) => {
|
|
59
|
+
this.lastError = err;
|
|
60
|
+
});
|
|
37
61
|
}
|
|
62
|
+
this.openedAt = Date.now();
|
|
38
63
|
resolve();
|
|
39
64
|
});
|
|
40
65
|
this.client.once('error', (err) => {
|
|
@@ -223,20 +248,26 @@ class SshClient extends events_1.default {
|
|
|
223
248
|
openKeepAlive() {
|
|
224
249
|
logger_1.default.debug(`SSH client open`);
|
|
225
250
|
this.keepalive = true;
|
|
251
|
+
this.lastError = null;
|
|
252
|
+
this.openedAt = 0;
|
|
226
253
|
this.open()
|
|
227
254
|
.then(() => {
|
|
228
255
|
logger_1.default.debug(`SSH client opened`);
|
|
229
256
|
this.emit(tunnel_1.TUNNEL_SSH_EVENT.CONNECTED);
|
|
230
257
|
if (this.client) {
|
|
231
258
|
this.client.on('close', () => {
|
|
232
|
-
|
|
259
|
+
// info, not debug: a tunnel that drops takes every connection on it with it, and the
|
|
260
|
+
// transfers that die with it are reported far from here - on the other side, as a
|
|
261
|
+
// failed handshake. One line per drop, and the log says so without debug turned on.
|
|
262
|
+
logger_1.default.info(this.closeReason());
|
|
233
263
|
if (this.keepalive)
|
|
234
264
|
this.openKeepAlive();
|
|
235
265
|
});
|
|
236
266
|
}
|
|
237
267
|
})
|
|
238
|
-
.catch(() => {
|
|
239
|
-
|
|
268
|
+
.catch((err) => {
|
|
269
|
+
this.lastError = err;
|
|
270
|
+
logger_1.default.info(this.closeReason());
|
|
240
271
|
this.emit(tunnel_1.TUNNEL_SSH_EVENT.DISCONNECTED);
|
|
241
272
|
setTimeout(() => {
|
|
242
273
|
if (this.keepalive)
|
package/distTs/src/utils.js
CHANGED
|
@@ -37,7 +37,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.isStringRegExp = exports.getWorkingDir = exports.getRootDir = exports.TARGET_ONLY_PORT_REGEX = exports.TARGET_HTTP_REGEX = exports.TARGET_TCP_TLS_REGEX = exports.ApiErrorTunnelsDisabled = exports.ApiErrorWorkspaceFlagged = exports.ApiErrorTunnelLimitReached = exports.ApiErrorAgentLimitReached = exports.ApiErrorDomainRestricted = exports.ApiErrorTargetInvalid = exports.ApiErrorWrongToken = exports.ApiErrorFailedToConnect = exports.ApiErrorAgentNotFound = exports.ARTIFACT_AUTH_TYPE = exports.ARTIFACT_TYPE = exports.SANDBOX_SNAPSHOT_STATUS = exports.SANDBOX_APP_STATUS = exports.SANDBOX_SETUP_STATUS = exports.SANDBOX_EXEC_STATUS = exports.SANDBOX_EXEC_RUNTIME = exports.SANDBOX_STATUS = exports.REST_API_ENDPOINT = exports.REST_API_REGION = exports.DEFAULT_SANDBOX_CP_USER = exports.ARTIFACT_DEFAULT_VERSION = exports.SUGGESTED_BROWSER_VERSION = exports.AGENT_UPDATE_STATUS_TTL_MS = exports.AGENT_UPDATE_PROBATION_MS = exports.AGENT_UPDATE_MAX_ATTEMPTS = exports.AGENT_FETCH_RETRY_MAX = exports.AGENT_FETCH_RETRY_MIN = exports.SSH_HANDSHAKE_TIMEOUT = exports.DEFAULT_TIMEOUT = exports.getSshIdent = exports.TUNNEL_SSH_IDENT_SHARED_H2 = exports.TUNNEL_CLIENT_IP_HEADER = exports.TUNNEL_HTTP2_SHED_LOG_INTERVAL = exports.TUNNEL_HTTP2_MAX_SESSIONS = exports.TUNNEL_HTTP2_SESSION_IDLE_TIMEOUT = exports.TUNNEL_HTTP_RETRY_AFTER = exports.TUNNEL_HTTP_MAX_INFLIGHT = exports.TUNNEL_HTTP_CB_MIN_REQUESTS = exports.TUNNEL_HTTP_CB_WINDOW = exports.TUNNEL_MAX_REQUEST_SIZE_TO_SYNC = exports.TUNNEL_HTTP_LOG_MAX_REQUESTS = exports.TUNNEL_HTTP_LOG_MAX_BODY = exports.TUNNEL_HTTP_RATE_WINDOW = exports.TUNNEL_HTTP_RATE_LIMIT = void 0;
|
|
40
|
-
exports.getGitName = exports.getGitEmail = exports.MCP_TOOL_CATEGORIES = exports.getBasicCommandTls = exports.getBasicCommandHttp = exports.getBasicCommandSandboxEndpoint = exports.getBasicCommandTcp = exports.getCurrentUser = exports.getServiceUser = exports.getRealTargetHost = exports.isWindows = exports.isLinux = exports.isOsx = exports.isDocker = exports.sleep = exports.getLatestVersion = exports.getVersionEnv = exports.getCurrentVersionWithoutEnv = exports.getVersionWithoutEnv = exports.getVersion = exports.isInstalledByChoco = exports.isInstalledByApt = exports.isInstalledByBrew = exports.isInstalledByNpm = exports.isNpmGlobalWritable = exports.isPathWritable = exports.tryGetEmail = exports.execLocally = exports.getHomeDirectory = exports.HOME_DIR_NAME = exports.chownToHomeOwner = exports.isDomainAvailable = exports.newCommand = exports.formatBytes = exports.formatHelp = exports.getPlatform = exports.getHostname = void 0;
|
|
40
|
+
exports.getGitName = exports.getGitEmail = exports.MCP_TOOL_CATEGORIES = exports.getBasicCommandTls = exports.getBasicCommandHttp = exports.getBasicCommandSandboxEndpoint = exports.getBasicCommandTcp = exports.getCurrentUser = exports.getServiceUser = exports.getRealTargetHost = exports.isWindows = exports.isLinux = exports.isOsx = exports.isDocker = exports.sleep = exports.getLatestVersion = exports.getVersionEnv = exports.getCurrentVersionWithoutEnv = exports.isVersionNewer = exports.getVersionWithoutEnv = exports.parseVersion = exports.getVersion = exports.isInstalledByChoco = exports.isInstalledByApt = exports.isInstalledByBrew = exports.isInstalledByNpm = exports.isNpmGlobalWritable = exports.isPathWritable = exports.tryGetEmail = exports.execLocally = exports.getHomeDirectory = exports.HOME_DIR_NAME = exports.chownToHomeOwner = exports.isDomainAvailable = exports.newCommand = exports.formatBytes = exports.formatHelp = exports.getPlatform = exports.getHostname = void 0;
|
|
41
41
|
exports.apiErrorCodeToClass = apiErrorCodeToClass;
|
|
42
42
|
exports.isFile = isFile;
|
|
43
43
|
exports.getAppWorkspaceUrl = getAppWorkspaceUrl;
|
|
@@ -132,7 +132,7 @@ exports.AGENT_UPDATE_PROBATION_MS = 30 * 60 * 1000;
|
|
|
132
132
|
// 15s; anything past it waits for the one at 60s, so a longer ttl here shows the outcome for
|
|
133
133
|
// four times as long as it says.
|
|
134
134
|
exports.AGENT_UPDATE_STATUS_TTL_MS = 10 * 1000;
|
|
135
|
-
exports.SUGGESTED_BROWSER_VERSION = '
|
|
135
|
+
exports.SUGGESTED_BROWSER_VERSION = '151.0.7922.77';
|
|
136
136
|
exports.ARTIFACT_DEFAULT_VERSION = 'latest';
|
|
137
137
|
exports.DEFAULT_SANDBOX_CP_USER = 'buddy';
|
|
138
138
|
var REST_API_REGION;
|
|
@@ -585,19 +585,44 @@ const getVersion = () => {
|
|
|
585
585
|
return cachedVersion;
|
|
586
586
|
};
|
|
587
587
|
exports.getVersion = getVersion;
|
|
588
|
+
// The one place that knows what a version string looks like: `x.y.z` optionally followed by
|
|
589
|
+
// a channel suffix. AgentSystem.verifyBinary keeps a regex of its own on purpose - it fishes
|
|
590
|
+
// a version out of selftest output rather than parsing one, so it must not be anchored.
|
|
591
|
+
const parseVersion = (version) => {
|
|
592
|
+
const m = (version || '').match(/^(\d+)\.(\d+)\.(\d+)/);
|
|
593
|
+
if (!m)
|
|
594
|
+
return null;
|
|
595
|
+
return {
|
|
596
|
+
version: m[0],
|
|
597
|
+
major: Number(m[1]),
|
|
598
|
+
minor: Number(m[2]),
|
|
599
|
+
patch: Number(m[3]),
|
|
600
|
+
};
|
|
601
|
+
};
|
|
602
|
+
exports.parseVersion = parseVersion;
|
|
588
603
|
const getVersionWithoutEnv = (version) => {
|
|
589
|
-
|
|
590
|
-
if (m)
|
|
591
|
-
return m[0];
|
|
592
|
-
return '0.0.0';
|
|
604
|
+
return (0, exports.parseVersion)(version)?.version || '0.0.0';
|
|
593
605
|
};
|
|
594
606
|
exports.getVersionWithoutEnv = getVersionWithoutEnv;
|
|
607
|
+
// An update is only an update when the other side is actually ahead. Comparing the strings
|
|
608
|
+
// for inequality also fired on a build that is newer than what the channel publishes - a
|
|
609
|
+
// local dev build, or a machine that got the release before es.buddy.works did - and told
|
|
610
|
+
// the user to "update" to an older version. Both sides carry the same env suffix (latest is
|
|
611
|
+
// fetched per env), so the numeric triple decides.
|
|
612
|
+
const isVersionNewer = (version, than) => {
|
|
613
|
+
const a = (0, exports.parseVersion)(version);
|
|
614
|
+
const b = (0, exports.parseVersion)(than);
|
|
615
|
+
if (!a || !b)
|
|
616
|
+
return false;
|
|
617
|
+
if (a.major !== b.major)
|
|
618
|
+
return a.major > b.major;
|
|
619
|
+
if (a.minor !== b.minor)
|
|
620
|
+
return a.minor > b.minor;
|
|
621
|
+
return a.patch > b.patch;
|
|
622
|
+
};
|
|
623
|
+
exports.isVersionNewer = isVersionNewer;
|
|
595
624
|
const getCurrentVersionWithoutEnv = () => {
|
|
596
|
-
|
|
597
|
-
const m = v.match(/^\d+\.\d+\.\d+/);
|
|
598
|
-
if (m)
|
|
599
|
-
return m[0];
|
|
600
|
-
return '0.0.0';
|
|
625
|
+
return (0, exports.getVersionWithoutEnv)((0, exports.getVersion)());
|
|
601
626
|
};
|
|
602
627
|
exports.getCurrentVersionWithoutEnv = getCurrentVersionWithoutEnv;
|
|
603
628
|
const getVersionEnv = () => {
|
|
@@ -626,10 +651,20 @@ const getLatestVersion = async () => {
|
|
|
626
651
|
});
|
|
627
652
|
if (response.status === 200) {
|
|
628
653
|
const data = await response.text();
|
|
629
|
-
|
|
654
|
+
// A 200 whose body is not a version - a padded value, an error page from something in
|
|
655
|
+
// front of the bucket - is nothing a caller can act on, so it fails here rather than
|
|
656
|
+
// downstream, where every comparison would read it as "no update". The parsed triple
|
|
657
|
+
// is what gets returned, not the body: the regex is unanchored at the end, so a body
|
|
658
|
+
// that merely starts with a version would otherwise carry its tail into the banners,
|
|
659
|
+
// the update log and the equality test in AgentManager.remoteUpdate.
|
|
660
|
+
// Only the agent's remote update surfaces this - `commandPre` swallows it, so the CLI
|
|
661
|
+
// stays quiet about a channel it could not read.
|
|
662
|
+
const parsed = (0, exports.parseVersion)(data.trim());
|
|
663
|
+
if (!parsed)
|
|
664
|
+
throw new Error(texts_1.ERR_FETCH_VERSION);
|
|
630
665
|
if (env === 'prod')
|
|
631
|
-
return
|
|
632
|
-
return `${
|
|
666
|
+
return parsed.version;
|
|
667
|
+
return `${parsed.version}-${env}`;
|
|
633
668
|
}
|
|
634
669
|
else {
|
|
635
670
|
return '0.0.0';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bdy",
|
|
3
3
|
"preferGlobal": false,
|
|
4
|
-
"version": "1.23.
|
|
4
|
+
"version": "1.23.13-beta",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://buddy.works/docs/cli",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"@fastify/cors": "11.2.0",
|
|
52
52
|
"@inquirer/prompts": "8.5.2",
|
|
53
53
|
"@modelcontextprotocol/sdk": "1.30.0",
|
|
54
|
-
"@puppeteer/browsers": "2.
|
|
54
|
+
"@puppeteer/browsers": "3.2.0",
|
|
55
55
|
"@scalar/json-magic": "0.12.16",
|
|
56
56
|
"@scalar/openapi-parser": "0.28.7",
|
|
57
57
|
"@scalar/openapi-types": "0.9.1",
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"picomatch": "4.0.4",
|
|
80
80
|
"pino": "10.3.1",
|
|
81
81
|
"punycode": "2.3.1",
|
|
82
|
-
"puppeteer-core": "
|
|
82
|
+
"puppeteer-core": "25.6.0",
|
|
83
83
|
"range-parser": "1.2.1",
|
|
84
84
|
"socket.io-client": "4.8.3",
|
|
85
85
|
"ssh2": "1.17.0",
|