bdy 1.25.18 → 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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bdy",
3
3
  "preferGlobal": false,
4
- "version": "1.25.18",
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 timestamp = Date.now().toString(36);
90
- const baseName = humanId({ separator: '-', capitalize: false });
91
- const defaultIdentifier = `${baseName}-${timestamp}`;
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: options.name || options.identifier || baseName,
94
- identifier: options.identifier || defaultIdentifier,
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 (options.fetch) {
98
- body.fetch = input_1.default.sandboxFetch(options.fetch);
107
+ if (fetch) {
108
+ body.fetch = fetch;
99
109
  }
100
110
  if (environmentId) {
101
111
  body.environment = {
@@ -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 node_forge_1 = __importDefault(require("node-forge"));
7
- const toPositiveHex = (hexString) => {
8
- let hexAsInt = parseInt(hexString[0], 16);
9
- if (hexAsInt < 8) {
10
- return hexString;
11
- }
12
- hexAsInt -= 8;
13
- return hexAsInt.toString() + hexString.substring(1);
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 keyPair = node_forge_1.default.pki.rsa.generateKeyPair(2048);
20
- const cert = node_forge_1.default.pki.createCertificate();
21
- cert.serialNumber = toPositiveHex(node_forge_1.default.util.bytesToHex(node_forge_1.default.random.getBytesSync(9)));
22
- cert.validity.notBefore = new Date();
23
- const notAfter = new Date();
24
- notAfter.setTime(notAfter.getTime() + 622080000000);
25
- cert.validity.notAfter = notAfter;
26
- const attrs = [
27
- {
28
- name: 'commonName',
29
- value: 'buddy.tunnel',
30
- },
31
- ];
32
- cert.setSubject(attrs);
33
- cert.setIssuer(attrs);
34
- cert.publicKey = keyPair.publicKey;
35
- cert.setExtensions([
36
- {
37
- name: 'basicConstraints',
38
- cA: true,
39
- },
40
- {
41
- name: 'keyUsage',
42
- keyCertSign: true,
43
- digitalSignature: true,
44
- nonRepudiation: true,
45
- keyEncipherment: true,
46
- dataEncipherment: true,
47
- },
48
- ]);
49
- cert.sign(keyPair.privateKey, node_forge_1.default.md.sha256.create());
50
- this.key = node_forge_1.default.pki.privateKeyToPem(keyPair.privateKey);
51
- this.cert = node_forge_1.default.pki.certificateToPem(cert);
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.18",
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",