bdy 1.25.18-stage → 1.25.19-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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bdy",
|
|
3
3
|
"preferGlobal": false,
|
|
4
|
-
"version": "1.25.
|
|
4
|
+
"version": "1.25.19-beta",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://buddy.works/docs/cli",
|
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
"@xhmikosr/decompress": "11.1.4",
|
|
62
62
|
"ajv": "8.20.0",
|
|
63
63
|
"ajv-formats": "3.0.1",
|
|
64
|
+
"asn1js": "3.0.10",
|
|
64
65
|
"basic-auth": "2.0.1",
|
|
65
66
|
"chalk": "5.6.2",
|
|
66
67
|
"commander": "15.0.0",
|
|
@@ -78,7 +79,6 @@
|
|
|
78
79
|
"mime-db": "1.54.0",
|
|
79
80
|
"mime-types": "3.0.2",
|
|
80
81
|
"netmask": "2.1.1",
|
|
81
|
-
"node-forge": "1.4.0",
|
|
82
82
|
"open": "11.0.0",
|
|
83
83
|
"path-is-inside": "1.0.2",
|
|
84
84
|
"picomatch": "4.0.4",
|
|
@@ -113,7 +113,6 @@
|
|
|
113
113
|
"@types/mime-types": "3.0.1",
|
|
114
114
|
"@types/netmask": "2.0.6",
|
|
115
115
|
"@types/node": "24.13.3",
|
|
116
|
-
"@types/node-forge": "1.3.14",
|
|
117
116
|
"@types/path-is-inside": "1.0.3",
|
|
118
117
|
"@types/picomatch": "4.0.3",
|
|
119
118
|
"@types/punycode": "2.1.4",
|
|
@@ -9,6 +9,7 @@ const texts_1 = require("../../texts");
|
|
|
9
9
|
const output_1 = __importDefault(require("../../output"));
|
|
10
10
|
const input_1 = __importDefault(require("../../input"));
|
|
11
11
|
const distro_1 = require("../../types/distro");
|
|
12
|
+
const name_1 = require("./name");
|
|
12
13
|
const VALID_RESOURCES = [
|
|
13
14
|
'1x2',
|
|
14
15
|
'2x4',
|
|
@@ -86,16 +87,25 @@ commandSandboxCreate.action(async (options) => {
|
|
|
86
87
|
result = await client.createSandboxByYaml(workspace, scope === distro_1.SCOPE.PROJECT ? project : null, body, environmentId);
|
|
87
88
|
}
|
|
88
89
|
else {
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
const fetch = options.fetch
|
|
91
|
+
? input_1.default.sandboxFetch(options.fetch)
|
|
92
|
+
: undefined;
|
|
93
|
+
const randomName = () => humanId({ separator: '-', capitalize: false });
|
|
94
|
+
const name = options.name ||
|
|
95
|
+
options.identifier ||
|
|
96
|
+
(0, name_1.sandboxNameFromFetch)(fetch, scope === distro_1.SCOPE.PROJECT ? project : null) ||
|
|
97
|
+
randomName();
|
|
98
|
+
// keeps repeated creates from the same name or repo unique, the random
|
|
99
|
+
// part covers parallel creates within the same millisecond
|
|
100
|
+
const suffix = Date.now().toString(36) + Math.random().toString(36).substring(2, 5);
|
|
92
101
|
const body = {
|
|
93
|
-
name
|
|
94
|
-
identifier: options.identifier ||
|
|
102
|
+
name,
|
|
103
|
+
identifier: options.identifier ||
|
|
104
|
+
`${(0, name_1.sandboxSlug)(name) || randomName()}-${suffix}`,
|
|
95
105
|
};
|
|
96
106
|
body.variables = input_1.default.runVariables(options);
|
|
97
|
-
if (
|
|
98
|
-
body.fetch =
|
|
107
|
+
if (fetch) {
|
|
108
|
+
body.fetch = fetch;
|
|
99
109
|
}
|
|
100
110
|
if (environmentId) {
|
|
101
111
|
body.environment = {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sandboxNameFromFetch = exports.sandboxSlug = void 0;
|
|
4
|
+
const sandbox_1 = require("../../types/sandbox");
|
|
5
|
+
// the API limit on identifier length is not documented, keep the derived part
|
|
6
|
+
// short so the timestamp suffix always fits
|
|
7
|
+
const MAX_SLUG = 40;
|
|
8
|
+
// identifier-safe form of a sandbox name (it must start and end with a letter
|
|
9
|
+
// or digit); empty when nothing usable is left
|
|
10
|
+
const sandboxSlug = (name) => name
|
|
11
|
+
.toLowerCase()
|
|
12
|
+
.replace(/[^a-z0-9_-]+/g, '-')
|
|
13
|
+
.replace(/^[-_]+/, '')
|
|
14
|
+
.substring(0, MAX_SLUG)
|
|
15
|
+
.replace(/[-_]+$/, '');
|
|
16
|
+
exports.sandboxSlug = sandboxSlug;
|
|
17
|
+
// a full commit sha is unreadable in a name, shorten it like git does
|
|
18
|
+
const shortRef = (ref) => ref && /^[0-9a-f]{40}$/i.test(ref) ? ref.substring(0, 7) : ref;
|
|
19
|
+
const repoName = (url) => {
|
|
20
|
+
const last = url.replace(/\/+$/, '').split(/[/:]/).pop() || '';
|
|
21
|
+
return last.replace(/\.git$/, '');
|
|
22
|
+
};
|
|
23
|
+
// Derives a readable sandbox name from the first --fetch entry, so a sandbox
|
|
24
|
+
// created for a repo or branch is recognisable on the list without -n.
|
|
25
|
+
// `project` is the project the sandbox is created in - pass null outside the
|
|
26
|
+
// project scope, a linked project there has nothing to do with the sandbox.
|
|
27
|
+
const sandboxNameFromFetch = (fetch, project) => {
|
|
28
|
+
const f = fetch?.[0];
|
|
29
|
+
if (!f)
|
|
30
|
+
return null;
|
|
31
|
+
let parts = [];
|
|
32
|
+
if (f.type === sandbox_1.SANDBOX_FETCH.PUBLIC_REPO && f.repository) {
|
|
33
|
+
parts = [repoName(f.repository), shortRef(f.ref)];
|
|
34
|
+
}
|
|
35
|
+
else if (f.type === sandbox_1.SANDBOX_FETCH.PROJECT_REPO && project) {
|
|
36
|
+
parts = [project, shortRef(f.ref)];
|
|
37
|
+
}
|
|
38
|
+
else if (f.type === sandbox_1.SANDBOX_FETCH.ARTIFACT && f.artifact) {
|
|
39
|
+
// artifact is given as name@version or name:version
|
|
40
|
+
parts = f.artifact.split(/[@:]/);
|
|
41
|
+
}
|
|
42
|
+
const name = parts.filter(Boolean).join(' ');
|
|
43
|
+
if (!(0, exports.sandboxSlug)(name))
|
|
44
|
+
return null;
|
|
45
|
+
return name;
|
|
46
|
+
};
|
|
47
|
+
exports.sandboxNameFromFetch = sandboxNameFromFetch;
|
package/distTs/src/texts.js
CHANGED
|
@@ -883,7 +883,7 @@ Authenticates with the SSH key you added to your Buddy account - the session alw
|
|
|
883
883
|
Arguments for ssh itself, or a command to run instead of a shell, go after --`;
|
|
884
884
|
// Sandbox options
|
|
885
885
|
exports.OPTION_SANDBOX_IDENTIFIER = 'Sandbox identifier';
|
|
886
|
-
exports.OPTION_SANDBOX_NAME = 'Sandbox display name';
|
|
886
|
+
exports.OPTION_SANDBOX_NAME = 'Sandbox display name. Defaults to the first --fetch source (repository, project and branch, or artifact) when not set';
|
|
887
887
|
exports.OPTION_SANDBOX_NOTE = 'Sandbox note';
|
|
888
888
|
exports.OPTION_SANDBOX_AGENT_NOTE = 'Sandbox note for AI agents; stored as agent_note and meant to be read before acting on the resource';
|
|
889
889
|
exports.OPTION_SANDBOX_OS = `Operating system image (ubuntu:24.04 (default), ubuntu:22.04)`;
|
|
@@ -1749,6 +1749,10 @@ exports.EXAMPLE_SANDBOX_CREATE = [
|
|
|
1749
1749
|
description: 'create sandbox and fetch artifact',
|
|
1750
1750
|
command: 'bdy sb create --fetch "path=/home/buddy/repo,artifact=name@version"',
|
|
1751
1751
|
},
|
|
1752
|
+
{
|
|
1753
|
+
description: 'create sandbox named after the fetched repository and branch ("Hello-World master")',
|
|
1754
|
+
command: 'bdy sb create --fetch "url=https://github.com/octocat/Hello-World.git,ref=master"',
|
|
1755
|
+
},
|
|
1752
1756
|
{
|
|
1753
1757
|
description: 'create sandbox, fetch project repo branch and build',
|
|
1754
1758
|
command: 'bdy sb create --fetch "path=/home/buddy/repo,ref=branch,build=npm install && npm run build"',
|
|
@@ -1,54 +1,145 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
5
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
39
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
40
|
+
const asn1js = __importStar(require("asn1js"));
|
|
41
|
+
const OID_SHA256_RSA = '1.2.840.113549.1.1.11';
|
|
42
|
+
const OID_COMMON_NAME = '2.5.4.3';
|
|
43
|
+
const OID_BASIC_CONSTRAINTS = '2.5.29.19';
|
|
44
|
+
const OID_KEY_USAGE = '2.5.29.15';
|
|
45
|
+
const COMMON_NAME = 'buddy.tunnel';
|
|
46
|
+
const VALIDITY_MS = 622080000000;
|
|
47
|
+
const algorithm = () => new asn1js.Sequence({
|
|
48
|
+
value: [
|
|
49
|
+
new asn1js.ObjectIdentifier({ value: OID_SHA256_RSA }),
|
|
50
|
+
new asn1js.Null(),
|
|
51
|
+
],
|
|
52
|
+
});
|
|
53
|
+
const name = () => new asn1js.Sequence({
|
|
54
|
+
value: [
|
|
55
|
+
new asn1js.Set({
|
|
56
|
+
value: [
|
|
57
|
+
new asn1js.Sequence({
|
|
58
|
+
value: [
|
|
59
|
+
new asn1js.ObjectIdentifier({ value: OID_COMMON_NAME }),
|
|
60
|
+
new asn1js.Utf8String({ value: COMMON_NAME }),
|
|
61
|
+
],
|
|
62
|
+
}),
|
|
63
|
+
],
|
|
64
|
+
}),
|
|
65
|
+
],
|
|
66
|
+
});
|
|
67
|
+
// UTCTime only reaches 2049, RFC 5280 switches to GeneralizedTime from 2050
|
|
68
|
+
const time = (date) => date.getUTCFullYear() < 2050
|
|
69
|
+
? new asn1js.UTCTime({ valueDate: date })
|
|
70
|
+
: new asn1js.GeneralizedTime({ valueDate: date });
|
|
71
|
+
const extension = (oid, value) => new asn1js.Sequence({
|
|
72
|
+
value: [
|
|
73
|
+
new asn1js.ObjectIdentifier({ value: oid }),
|
|
74
|
+
new asn1js.OctetString({ valueHex: value.toBER() }),
|
|
75
|
+
],
|
|
76
|
+
});
|
|
77
|
+
const toPem = (label, der) => {
|
|
78
|
+
const lines = Buffer.from(der).toString('base64').match(/.{1,64}/g) || [];
|
|
79
|
+
return `-----BEGIN ${label}-----\r\n${lines.join('\r\n')}\r\n-----END ${label}-----\r\n`;
|
|
14
80
|
};
|
|
81
|
+
// Self-signed certificate for the local TLS server when the user does not
|
|
82
|
+
// provide one. Keys and the signature come from node crypto, asn1js only
|
|
83
|
+
// encodes the certificate structure.
|
|
15
84
|
class ServerCert {
|
|
16
85
|
key;
|
|
17
86
|
cert;
|
|
18
87
|
constructor() {
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
name
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
88
|
+
const { publicKey, privateKey } = crypto_1.default.generateKeyPairSync('rsa', {
|
|
89
|
+
modulusLength: 2048,
|
|
90
|
+
});
|
|
91
|
+
// positive and without a leading zero byte, so the DER integer is minimal
|
|
92
|
+
const serial = crypto_1.default.randomBytes(9);
|
|
93
|
+
serial[0] = (serial[0] & 0x7f) | 0x40;
|
|
94
|
+
// whole seconds: asn1js writes milliseconds into GeneralizedTime and RFC
|
|
95
|
+
// 5280 forbids fractions, VALIDITY_MS keeps notAfter on a whole second too
|
|
96
|
+
const notBefore = new Date(Math.floor(Date.now() / 1000) * 1000);
|
|
97
|
+
const notAfter = new Date(notBefore.getTime() + VALIDITY_MS);
|
|
98
|
+
const spki = asn1js.fromBER(publicKey.export({ type: 'spki', format: 'der' }));
|
|
99
|
+
const tbs = new asn1js.Sequence({
|
|
100
|
+
value: [
|
|
101
|
+
// v3
|
|
102
|
+
new asn1js.Constructed({
|
|
103
|
+
idBlock: { tagClass: 3, tagNumber: 0 },
|
|
104
|
+
value: [new asn1js.Integer({ value: 2 })],
|
|
105
|
+
}),
|
|
106
|
+
new asn1js.Integer({ valueHex: serial }),
|
|
107
|
+
algorithm(),
|
|
108
|
+
name(),
|
|
109
|
+
new asn1js.Sequence({ value: [time(notBefore), time(notAfter)] }),
|
|
110
|
+
name(),
|
|
111
|
+
spki.result,
|
|
112
|
+
new asn1js.Constructed({
|
|
113
|
+
idBlock: { tagClass: 3, tagNumber: 3 },
|
|
114
|
+
value: [
|
|
115
|
+
new asn1js.Sequence({
|
|
116
|
+
value: [
|
|
117
|
+
extension(OID_BASIC_CONSTRAINTS, new asn1js.Sequence({
|
|
118
|
+
value: [new asn1js.Boolean({ value: true })],
|
|
119
|
+
})),
|
|
120
|
+
// digitalSignature, nonRepudiation, keyEncipherment,
|
|
121
|
+
// dataEncipherment and keyCertSign - bits 0-3 and 5
|
|
122
|
+
extension(OID_KEY_USAGE, new asn1js.BitString({
|
|
123
|
+
valueHex: new Uint8Array([0xf4]),
|
|
124
|
+
unusedBits: 2,
|
|
125
|
+
})),
|
|
126
|
+
],
|
|
127
|
+
}),
|
|
128
|
+
],
|
|
129
|
+
}),
|
|
130
|
+
],
|
|
131
|
+
});
|
|
132
|
+
const tbsDer = Buffer.from(tbs.toBER());
|
|
133
|
+
const signature = crypto_1.default.sign('sha256', tbsDer, privateKey);
|
|
134
|
+
const cert = new asn1js.Sequence({
|
|
135
|
+
value: [
|
|
136
|
+
asn1js.fromBER(tbsDer).result,
|
|
137
|
+
algorithm(),
|
|
138
|
+
new asn1js.BitString({ valueHex: signature }),
|
|
139
|
+
],
|
|
140
|
+
});
|
|
141
|
+
this.key = privateKey.export({ type: 'pkcs1', format: 'pem' }).toString();
|
|
142
|
+
this.cert = toPem('CERTIFICATE', cert.toBER());
|
|
52
143
|
}
|
|
53
144
|
}
|
|
54
145
|
exports.default = ServerCert;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bdy",
|
|
3
3
|
"preferGlobal": false,
|
|
4
|
-
"version": "1.25.
|
|
4
|
+
"version": "1.25.19-beta",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://buddy.works/docs/cli",
|
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
"@xhmikosr/decompress": "11.1.4",
|
|
62
62
|
"ajv": "8.20.0",
|
|
63
63
|
"ajv-formats": "3.0.1",
|
|
64
|
+
"asn1js": "3.0.10",
|
|
64
65
|
"basic-auth": "2.0.1",
|
|
65
66
|
"chalk": "5.6.2",
|
|
66
67
|
"commander": "15.0.0",
|
|
@@ -78,7 +79,6 @@
|
|
|
78
79
|
"mime-db": "1.54.0",
|
|
79
80
|
"mime-types": "3.0.2",
|
|
80
81
|
"netmask": "2.1.1",
|
|
81
|
-
"node-forge": "1.4.0",
|
|
82
82
|
"open": "11.0.0",
|
|
83
83
|
"path-is-inside": "1.0.2",
|
|
84
84
|
"picomatch": "4.0.4",
|
|
@@ -113,7 +113,6 @@
|
|
|
113
113
|
"@types/mime-types": "3.0.1",
|
|
114
114
|
"@types/netmask": "2.0.6",
|
|
115
115
|
"@types/node": "24.13.3",
|
|
116
|
-
"@types/node-forge": "1.3.14",
|
|
117
116
|
"@types/path-is-inside": "1.0.3",
|
|
118
117
|
"@types/picomatch": "4.0.3",
|
|
119
118
|
"@types/punycode": "2.1.4",
|