@fireproof/core-cli 0.23.15 → 0.24.2-dev-cloud-api
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/device-id-cmd.d.ts +150 -0
- package/device-id-cmd.js +578 -0
- package/device-id-cmd.js.map +1 -0
- package/main.js +2 -0
- package/main.js.map +1 -1
- package/package.json +16 -11
- package/test-container-cmd.d.ts +10 -10
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { SuperThis } from "@fireproof/core-types-base";
|
|
2
|
+
export declare function deviceIdCmd(sthis: SuperThis): Partial<import("cmd-ts/dist/cjs/argparser.js").Register> & {
|
|
3
|
+
parse(context: import("cmd-ts/dist/cjs/argparser.js").ParseContext): Promise<import("cmd-ts/dist/cjs/argparser.js").ParsingResult<{
|
|
4
|
+
command: "csr";
|
|
5
|
+
args: {
|
|
6
|
+
commonName: string;
|
|
7
|
+
organization: string;
|
|
8
|
+
locality: string;
|
|
9
|
+
state: string;
|
|
10
|
+
country: string;
|
|
11
|
+
};
|
|
12
|
+
} | {
|
|
13
|
+
command: "cert";
|
|
14
|
+
args: {
|
|
15
|
+
file: string;
|
|
16
|
+
};
|
|
17
|
+
} | {
|
|
18
|
+
command: "register";
|
|
19
|
+
args: {
|
|
20
|
+
caUrl: string;
|
|
21
|
+
port: string;
|
|
22
|
+
timeout: string;
|
|
23
|
+
forceRenew: boolean;
|
|
24
|
+
commonName: string;
|
|
25
|
+
organization: string;
|
|
26
|
+
locality: string;
|
|
27
|
+
state: string;
|
|
28
|
+
country: string;
|
|
29
|
+
};
|
|
30
|
+
} | {
|
|
31
|
+
command: "create";
|
|
32
|
+
args: {
|
|
33
|
+
force: boolean;
|
|
34
|
+
};
|
|
35
|
+
} | {
|
|
36
|
+
command: "export";
|
|
37
|
+
args: {
|
|
38
|
+
private: boolean;
|
|
39
|
+
json: boolean;
|
|
40
|
+
public: boolean;
|
|
41
|
+
cert: boolean;
|
|
42
|
+
};
|
|
43
|
+
} | {
|
|
44
|
+
command: "ca-cert";
|
|
45
|
+
args: {
|
|
46
|
+
keyFile: string;
|
|
47
|
+
outputKey: string;
|
|
48
|
+
outputCert: string;
|
|
49
|
+
json: boolean;
|
|
50
|
+
envVars: boolean;
|
|
51
|
+
commonName: string;
|
|
52
|
+
organization: string;
|
|
53
|
+
locality: string;
|
|
54
|
+
state: string;
|
|
55
|
+
country: string;
|
|
56
|
+
};
|
|
57
|
+
}>>;
|
|
58
|
+
} & import("cmd-ts/dist/cjs/helpdoc.js").Named & Partial<import("cmd-ts/dist/cjs/helpdoc.js").Descriptive & import("cmd-ts/dist/cjs/helpdoc.js").Versioned> & import("cmd-ts/dist/cjs/helpdoc.js").PrintHelp & Partial<import("cmd-ts/dist/cjs/helpdoc.js").Versioned> & import("cmd-ts/dist/cjs/argparser.js").Register & import("cmd-ts/dist/cjs/runner.js").Handling<{
|
|
59
|
+
command: "csr";
|
|
60
|
+
args: {
|
|
61
|
+
commonName: string;
|
|
62
|
+
organization: string;
|
|
63
|
+
locality: string;
|
|
64
|
+
state: string;
|
|
65
|
+
country: string;
|
|
66
|
+
};
|
|
67
|
+
} | {
|
|
68
|
+
command: "cert";
|
|
69
|
+
args: {
|
|
70
|
+
file: string;
|
|
71
|
+
};
|
|
72
|
+
} | {
|
|
73
|
+
command: "register";
|
|
74
|
+
args: {
|
|
75
|
+
caUrl: string;
|
|
76
|
+
port: string;
|
|
77
|
+
timeout: string;
|
|
78
|
+
forceRenew: boolean;
|
|
79
|
+
commonName: string;
|
|
80
|
+
organization: string;
|
|
81
|
+
locality: string;
|
|
82
|
+
state: string;
|
|
83
|
+
country: string;
|
|
84
|
+
};
|
|
85
|
+
} | {
|
|
86
|
+
command: "create";
|
|
87
|
+
args: {
|
|
88
|
+
force: boolean;
|
|
89
|
+
};
|
|
90
|
+
} | {
|
|
91
|
+
command: "export";
|
|
92
|
+
args: {
|
|
93
|
+
private: boolean;
|
|
94
|
+
json: boolean;
|
|
95
|
+
public: boolean;
|
|
96
|
+
cert: boolean;
|
|
97
|
+
};
|
|
98
|
+
} | {
|
|
99
|
+
command: "ca-cert";
|
|
100
|
+
args: {
|
|
101
|
+
keyFile: string;
|
|
102
|
+
outputKey: string;
|
|
103
|
+
outputCert: string;
|
|
104
|
+
json: boolean;
|
|
105
|
+
envVars: boolean;
|
|
106
|
+
commonName: string;
|
|
107
|
+
organization: string;
|
|
108
|
+
locality: string;
|
|
109
|
+
state: string;
|
|
110
|
+
country: string;
|
|
111
|
+
};
|
|
112
|
+
}, {
|
|
113
|
+
command: "csr";
|
|
114
|
+
value: Promise<void>;
|
|
115
|
+
} | {
|
|
116
|
+
command: "cert";
|
|
117
|
+
value: Promise<void>;
|
|
118
|
+
} | {
|
|
119
|
+
command: "register";
|
|
120
|
+
value: Promise<void>;
|
|
121
|
+
} | {
|
|
122
|
+
command: "create";
|
|
123
|
+
value: Promise<void>;
|
|
124
|
+
} | {
|
|
125
|
+
command: "export";
|
|
126
|
+
value: Promise<void>;
|
|
127
|
+
} | {
|
|
128
|
+
command: "ca-cert";
|
|
129
|
+
value: Promise<void>;
|
|
130
|
+
}> & {
|
|
131
|
+
run(context: import("cmd-ts/dist/cjs/argparser.js").ParseContext): Promise<import("cmd-ts/dist/cjs/argparser.js").ParsingResult<{
|
|
132
|
+
command: "csr";
|
|
133
|
+
value: Promise<void>;
|
|
134
|
+
} | {
|
|
135
|
+
command: "cert";
|
|
136
|
+
value: Promise<void>;
|
|
137
|
+
} | {
|
|
138
|
+
command: "register";
|
|
139
|
+
value: Promise<void>;
|
|
140
|
+
} | {
|
|
141
|
+
command: "create";
|
|
142
|
+
value: Promise<void>;
|
|
143
|
+
} | {
|
|
144
|
+
command: "export";
|
|
145
|
+
value: Promise<void>;
|
|
146
|
+
} | {
|
|
147
|
+
command: "ca-cert";
|
|
148
|
+
value: Promise<void>;
|
|
149
|
+
}>>;
|
|
150
|
+
};
|
package/device-id-cmd.js
ADDED
|
@@ -0,0 +1,578 @@
|
|
|
1
|
+
import { command, option, string, subcommands, flag } from "cmd-ts";
|
|
2
|
+
import { CertificatePayloadSchema } from "@fireproof/core-types-base";
|
|
3
|
+
import { DeviceIdKey, DeviceIdCSR, DeviceIdCA } from "@fireproof/core-device-id";
|
|
4
|
+
import { getKeyBag } from "@fireproof/core-keybag";
|
|
5
|
+
import { decodeJwt } from "jose";
|
|
6
|
+
import fs from "fs-extra";
|
|
7
|
+
import { base58btc } from "multiformats/bases/base58";
|
|
8
|
+
import { Hono } from "hono";
|
|
9
|
+
import { serve } from "@hono/node-server";
|
|
10
|
+
import open from "open";
|
|
11
|
+
import { Future, timeouted, isSuccess, isTimeout, BuildURI } from "@adviser/cement";
|
|
12
|
+
function getStdin() {
|
|
13
|
+
return new Promise((resolve) => {
|
|
14
|
+
let data = "";
|
|
15
|
+
process.stdin.setEncoding("utf8");
|
|
16
|
+
process.stdin.on("readable", () => {
|
|
17
|
+
let chunk;
|
|
18
|
+
while ((chunk = process.stdin.read()) !== null) {
|
|
19
|
+
data += chunk;
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
process.stdin.on("end", () => resolve(data));
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function subjectOptions() {
|
|
26
|
+
return {
|
|
27
|
+
commonName: option({
|
|
28
|
+
long: "common-name",
|
|
29
|
+
short: "cn",
|
|
30
|
+
description: "Common Name (required, e.g., 'My Device' or 'device-serial')",
|
|
31
|
+
type: string,
|
|
32
|
+
}),
|
|
33
|
+
organization: option({
|
|
34
|
+
long: "organization",
|
|
35
|
+
short: "o",
|
|
36
|
+
description: "Organization name",
|
|
37
|
+
type: string,
|
|
38
|
+
defaultValue: () => "You did not set the Organization",
|
|
39
|
+
}),
|
|
40
|
+
locality: option({
|
|
41
|
+
long: "locality",
|
|
42
|
+
short: "l",
|
|
43
|
+
description: "Locality/City",
|
|
44
|
+
type: string,
|
|
45
|
+
defaultValue: () => "You did not set the City",
|
|
46
|
+
}),
|
|
47
|
+
state: option({
|
|
48
|
+
long: "state",
|
|
49
|
+
short: "s",
|
|
50
|
+
description: "State or Province",
|
|
51
|
+
type: string,
|
|
52
|
+
defaultValue: () => "You did not set the State",
|
|
53
|
+
}),
|
|
54
|
+
country: option({
|
|
55
|
+
long: "country",
|
|
56
|
+
short: "c",
|
|
57
|
+
description: "Country (2-letter code)",
|
|
58
|
+
type: string,
|
|
59
|
+
defaultValue: () => "WD",
|
|
60
|
+
}),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function buildSubject(args) {
|
|
64
|
+
return {
|
|
65
|
+
commonName: args.commonName,
|
|
66
|
+
organization: args.organization,
|
|
67
|
+
locality: args.locality,
|
|
68
|
+
stateOrProvinceName: args.state,
|
|
69
|
+
countryName: args.country,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
export function deviceIdCmd(sthis) {
|
|
73
|
+
const createCmd = command({
|
|
74
|
+
name: "create",
|
|
75
|
+
description: "Generate a new device ID key pair and store it.",
|
|
76
|
+
args: {
|
|
77
|
+
force: flag({
|
|
78
|
+
long: "force",
|
|
79
|
+
short: "f",
|
|
80
|
+
description: "Force creation of a new device ID, overwriting any existing one.",
|
|
81
|
+
}),
|
|
82
|
+
},
|
|
83
|
+
handler: async function (args) {
|
|
84
|
+
try {
|
|
85
|
+
const keyBag = await getKeyBag(sthis);
|
|
86
|
+
const existingDeviceIdResult = await keyBag.getDeviceId();
|
|
87
|
+
if (existingDeviceIdResult.deviceId.IsSome() && !args.force) {
|
|
88
|
+
const jwk = existingDeviceIdResult.deviceId.unwrap();
|
|
89
|
+
const deviceIdKey = (await DeviceIdKey.createFromJWK(jwk)).unwrap();
|
|
90
|
+
const fingerprint = await deviceIdKey.fingerPrint();
|
|
91
|
+
console.log(`Existing Device ID Fingerprint: ${fingerprint}`);
|
|
92
|
+
process.exit(0);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const deviceIdKey = await DeviceIdKey.create();
|
|
96
|
+
const jwkPrivate = await deviceIdKey.exportPrivateJWK();
|
|
97
|
+
await keyBag.setDeviceId(jwkPrivate);
|
|
98
|
+
const fingerprint = await deviceIdKey.fingerPrint();
|
|
99
|
+
console.log(`Created Device ID Fingerprint: ${fingerprint}`);
|
|
100
|
+
console.log("To generate a Certificate Signing Request (CSR), run: core-cli deviceId csr");
|
|
101
|
+
console.log("To export the public and private keys, run: core-cli deviceId export");
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
console.error("An error occurred during device ID creation:", error);
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
const csrCmd = command({
|
|
110
|
+
name: "csr",
|
|
111
|
+
description: "Generate a Certificate Signing Request (CSR) for the current device ID.",
|
|
112
|
+
args: subjectOptions(),
|
|
113
|
+
handler: async function (args) {
|
|
114
|
+
try {
|
|
115
|
+
const keyBag = await getKeyBag(sthis);
|
|
116
|
+
const existingDeviceIdResult = await keyBag.getDeviceId();
|
|
117
|
+
if (existingDeviceIdResult.deviceId.IsNone()) {
|
|
118
|
+
console.error("No Device ID found. Please create one using 'core-cli deviceId create' first.");
|
|
119
|
+
process.exit(1);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const jwkPrivate = existingDeviceIdResult.deviceId.unwrap();
|
|
123
|
+
const createResult = await DeviceIdKey.createFromJWK(jwkPrivate);
|
|
124
|
+
if (createResult.isErr()) {
|
|
125
|
+
console.error("Error loading existing device ID:", createResult.Err());
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
const deviceIdKey = createResult.Ok();
|
|
129
|
+
const deviceIdCSR = new DeviceIdCSR(sthis, deviceIdKey);
|
|
130
|
+
const subject = buildSubject(args);
|
|
131
|
+
const csrResult = await deviceIdCSR.createCSR(subject);
|
|
132
|
+
if (csrResult.isOk()) {
|
|
133
|
+
console.log("\n--- Certificate Signing Request (CSR) ---");
|
|
134
|
+
console.log(csrResult.Ok());
|
|
135
|
+
console.log("---\n");
|
|
136
|
+
console.log("Please send the above CSR to your Certificate Authority (CA) to get a signed certificate.");
|
|
137
|
+
console.log("Once you receive the certificate, you can use a future command to import it.");
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
console.error("Failed to generate CSR:", csrResult.Err());
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
console.error("An error occurred during CSR generation:", error);
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
const exportCmd = command({
|
|
151
|
+
name: "export",
|
|
152
|
+
description: "Export the public and private parts of the current device ID.",
|
|
153
|
+
args: {
|
|
154
|
+
private: flag({
|
|
155
|
+
long: "private",
|
|
156
|
+
short: "p",
|
|
157
|
+
description: "Export only the private key. Use with caution!",
|
|
158
|
+
}),
|
|
159
|
+
json: flag({
|
|
160
|
+
long: "json",
|
|
161
|
+
description: "Output in single-line JSON format.",
|
|
162
|
+
}),
|
|
163
|
+
public: flag({
|
|
164
|
+
long: "public",
|
|
165
|
+
description: "Export only the public key. Default if no other flags specified for key type.",
|
|
166
|
+
}),
|
|
167
|
+
cert: flag({
|
|
168
|
+
long: "cert",
|
|
169
|
+
description: "Export the certificate if available.",
|
|
170
|
+
}),
|
|
171
|
+
},
|
|
172
|
+
handler: async function (args) {
|
|
173
|
+
try {
|
|
174
|
+
const keyBag = await getKeyBag(sthis);
|
|
175
|
+
const existingDeviceIdResult = await keyBag.getDeviceId();
|
|
176
|
+
if (existingDeviceIdResult.deviceId.IsNone()) {
|
|
177
|
+
console.error("No Device ID found. Please create one using 'core-cli deviceId create' first.");
|
|
178
|
+
process.exit(1);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const jwkPrivate = existingDeviceIdResult.deviceId.unwrap();
|
|
182
|
+
const createResult = await DeviceIdKey.createFromJWK(jwkPrivate);
|
|
183
|
+
if (createResult.isErr()) {
|
|
184
|
+
console.error("Error loading device ID:", createResult.Err());
|
|
185
|
+
process.exit(1);
|
|
186
|
+
}
|
|
187
|
+
const deviceIdKey = createResult.Ok();
|
|
188
|
+
let publicKey;
|
|
189
|
+
let privateKey;
|
|
190
|
+
let certificate;
|
|
191
|
+
const exportPublic = args.public || (!args.private && !args.cert);
|
|
192
|
+
const exportPrivate = args.private;
|
|
193
|
+
const exportCert = args.cert || (!args.private && !args.public && !args.cert);
|
|
194
|
+
if (exportPublic) {
|
|
195
|
+
publicKey = await deviceIdKey.publicKey();
|
|
196
|
+
}
|
|
197
|
+
if (exportPrivate) {
|
|
198
|
+
privateKey = await deviceIdKey.exportPrivateJWK();
|
|
199
|
+
}
|
|
200
|
+
if (exportCert && existingDeviceIdResult.cert.IsSome()) {
|
|
201
|
+
certificate = existingDeviceIdResult.cert.unwrap();
|
|
202
|
+
}
|
|
203
|
+
else if (args.cert && existingDeviceIdResult.cert.IsNone()) {
|
|
204
|
+
console.error("No certificate found for this Device ID.");
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
207
|
+
if (args.json) {
|
|
208
|
+
const outputObject = {};
|
|
209
|
+
if (publicKey)
|
|
210
|
+
outputObject.publicKey = publicKey;
|
|
211
|
+
if (privateKey)
|
|
212
|
+
outputObject.privateKey = privateKey;
|
|
213
|
+
if (certificate)
|
|
214
|
+
outputObject.certificate = certificate.certificateJWT;
|
|
215
|
+
console.log(JSON.stringify(outputObject));
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
console.log("--- Device ID Export ---");
|
|
219
|
+
const fingerprint = await deviceIdKey.fingerPrint();
|
|
220
|
+
console.log(`Fingerprint: ${fingerprint}`);
|
|
221
|
+
if (publicKey) {
|
|
222
|
+
console.log("\nPublic Key (JWK):");
|
|
223
|
+
console.log(JSON.stringify(publicKey, null, 2));
|
|
224
|
+
}
|
|
225
|
+
if (privateKey) {
|
|
226
|
+
console.log("\nPrivate Key (JWK):");
|
|
227
|
+
console.log(JSON.stringify(privateKey, null, 2));
|
|
228
|
+
}
|
|
229
|
+
if (certificate) {
|
|
230
|
+
console.log("\nCertificate (JWT):");
|
|
231
|
+
console.log(certificate.certificateJWT);
|
|
232
|
+
}
|
|
233
|
+
console.log("---\n");
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
console.error("An error occurred during export:", error);
|
|
238
|
+
process.exit(1);
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
const certCmd = command({
|
|
243
|
+
name: "cert",
|
|
244
|
+
description: "Import and store a signed certificate for the current device ID.",
|
|
245
|
+
args: {
|
|
246
|
+
file: option({
|
|
247
|
+
long: "file",
|
|
248
|
+
short: "f",
|
|
249
|
+
description: "Path to the certificate file. If not provided, reads from stdin.",
|
|
250
|
+
type: string,
|
|
251
|
+
defaultValue: () => "",
|
|
252
|
+
}),
|
|
253
|
+
},
|
|
254
|
+
handler: async function (args) {
|
|
255
|
+
try {
|
|
256
|
+
const keyBag = await getKeyBag(sthis);
|
|
257
|
+
const existingDeviceIdResult = await keyBag.getDeviceId();
|
|
258
|
+
if (existingDeviceIdResult.deviceId.IsNone()) {
|
|
259
|
+
console.error("No Device ID found. Please create one using 'core-cli deviceId create' first.");
|
|
260
|
+
process.exit(1);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
const jwkPrivate = existingDeviceIdResult.deviceId.unwrap();
|
|
264
|
+
let certificateContent;
|
|
265
|
+
if (args.file) {
|
|
266
|
+
certificateContent = await fs.readFile(args.file, "utf8");
|
|
267
|
+
console.log(`Certificate read from ${args.file}`);
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
console.log("Waiting for certificate content from stdin (Ctrl+D to finish):");
|
|
271
|
+
certificateContent = await getStdin();
|
|
272
|
+
console.log("Certificate read from stdin.");
|
|
273
|
+
}
|
|
274
|
+
const decoded = decodeJwt(certificateContent);
|
|
275
|
+
const certPayload = CertificatePayloadSchema.parse(decoded);
|
|
276
|
+
const certToStore = {
|
|
277
|
+
certificateJWT: certificateContent,
|
|
278
|
+
certificatePayload: certPayload,
|
|
279
|
+
};
|
|
280
|
+
await keyBag.setDeviceId(jwkPrivate, certToStore);
|
|
281
|
+
console.log("Certificate successfully stored with the Device ID.");
|
|
282
|
+
}
|
|
283
|
+
catch (error) {
|
|
284
|
+
console.error("An error occurred during certificate import:", error);
|
|
285
|
+
process.exit(1);
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
const caCertCmd = command({
|
|
290
|
+
name: "ca-cert",
|
|
291
|
+
description: "Create a self-signed CA certificate for use in DeviceIdCA.",
|
|
292
|
+
args: {
|
|
293
|
+
...subjectOptions(),
|
|
294
|
+
keyFile: option({
|
|
295
|
+
long: "key-file",
|
|
296
|
+
short: "k",
|
|
297
|
+
description: "Path to existing private key file (JWK format). If not provided, a new key will be generated.",
|
|
298
|
+
type: string,
|
|
299
|
+
defaultValue: () => "",
|
|
300
|
+
}),
|
|
301
|
+
outputKey: option({
|
|
302
|
+
long: "output-key",
|
|
303
|
+
description: "Path to save the private key (JWK format). Only used when generating a new key.",
|
|
304
|
+
type: string,
|
|
305
|
+
defaultValue: () => "",
|
|
306
|
+
}),
|
|
307
|
+
outputCert: option({
|
|
308
|
+
long: "output-cert",
|
|
309
|
+
description: "Path to save the certificate (JWT format). If not provided, outputs to stdout.",
|
|
310
|
+
type: string,
|
|
311
|
+
defaultValue: () => "",
|
|
312
|
+
}),
|
|
313
|
+
json: flag({
|
|
314
|
+
long: "json",
|
|
315
|
+
description: "Output in JSON format with both privateKey and signedCert.",
|
|
316
|
+
}),
|
|
317
|
+
envVars: flag({
|
|
318
|
+
long: "envVars",
|
|
319
|
+
description: "Output as environment variables (DEVICE_ID_CA_PRIV_KEY and DEVICE_ID_CA_CERT).",
|
|
320
|
+
}),
|
|
321
|
+
},
|
|
322
|
+
handler: async function (args) {
|
|
323
|
+
try {
|
|
324
|
+
const log = args.json || args.envVars ? () => { } : console.log.bind(console);
|
|
325
|
+
let caKey;
|
|
326
|
+
let jwkPrivate;
|
|
327
|
+
if (args.keyFile) {
|
|
328
|
+
const keyContent = await fs.readFile(args.keyFile, "utf8");
|
|
329
|
+
jwkPrivate = JSON.parse(keyContent);
|
|
330
|
+
const keyResult = await DeviceIdKey.createFromJWK(jwkPrivate);
|
|
331
|
+
if (keyResult.isErr()) {
|
|
332
|
+
console.error("Error loading private key from file:", keyResult.Err());
|
|
333
|
+
process.exit(1);
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
caKey = keyResult.Ok();
|
|
337
|
+
log(`Loaded private key from ${args.keyFile}`);
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
caKey = await DeviceIdKey.create();
|
|
341
|
+
jwkPrivate = await caKey.exportPrivateJWK();
|
|
342
|
+
log("Generated new CA private key");
|
|
343
|
+
if (args.outputKey) {
|
|
344
|
+
await fs.writeFile(args.outputKey, JSON.stringify(jwkPrivate, null, 2), "utf8");
|
|
345
|
+
log(`Private key saved to ${args.outputKey}`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
const caSubject = buildSubject(args);
|
|
349
|
+
const deviceCA = new DeviceIdCA({
|
|
350
|
+
base64: sthis.txt.base64,
|
|
351
|
+
caKey,
|
|
352
|
+
caSubject,
|
|
353
|
+
actions: {
|
|
354
|
+
generateSerialNumber: async () => sthis.nextId(32).str,
|
|
355
|
+
},
|
|
356
|
+
});
|
|
357
|
+
const issueCertResult = await deviceCA.issueCertificate({
|
|
358
|
+
csr: {
|
|
359
|
+
subject: caSubject,
|
|
360
|
+
publicKey: await caKey.publicKey(),
|
|
361
|
+
extensions: {
|
|
362
|
+
keyUsage: ["digitalSignature", "keyCertSign", "cRLSign"],
|
|
363
|
+
extendedKeyUsage: [],
|
|
364
|
+
},
|
|
365
|
+
},
|
|
366
|
+
});
|
|
367
|
+
if (issueCertResult.isErr()) {
|
|
368
|
+
console.error("Error issuing CA certificate:", issueCertResult.Err());
|
|
369
|
+
process.exit(1);
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
const certificateJWT = issueCertResult.Ok().certificateJWT;
|
|
373
|
+
if (args.envVars) {
|
|
374
|
+
const privateKeyJson = JSON.stringify(jwkPrivate);
|
|
375
|
+
const privateKeyBase58 = base58btc.encode(sthis.txt.encode(privateKeyJson));
|
|
376
|
+
console.log(`DEVICE_ID_CA_PRIV_KEY=${privateKeyBase58}`);
|
|
377
|
+
console.log(`DEVICE_ID_CA_CERT=${certificateJWT}`);
|
|
378
|
+
}
|
|
379
|
+
else if (args.json) {
|
|
380
|
+
const jsonOutput = {
|
|
381
|
+
privateKey: jwkPrivate,
|
|
382
|
+
signedCert: certificateJWT,
|
|
383
|
+
};
|
|
384
|
+
console.log(JSON.stringify(jsonOutput, null, 2));
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
log("\n--- CA Private Key (JWK) ---");
|
|
388
|
+
log(JSON.stringify(jwkPrivate, null, 2));
|
|
389
|
+
log("---\n");
|
|
390
|
+
log("\n--- CA Certificate (JWT) ---");
|
|
391
|
+
log(certificateJWT);
|
|
392
|
+
log("---\n");
|
|
393
|
+
log("\nCA Certificate Details:");
|
|
394
|
+
log(` Common Name: ${caSubject.commonName}`);
|
|
395
|
+
log(` Organization: ${caSubject.organization}`);
|
|
396
|
+
log(` Locality: ${caSubject.locality}`);
|
|
397
|
+
log(` State: ${caSubject.stateOrProvinceName}`);
|
|
398
|
+
log(` Country: ${caSubject.countryName}`);
|
|
399
|
+
const fingerprint = await caKey.fingerPrint();
|
|
400
|
+
log(` Key Fingerprint: ${fingerprint}`);
|
|
401
|
+
if (args.outputKey) {
|
|
402
|
+
log(`\n✓ Private key saved to ${args.outputKey}`);
|
|
403
|
+
}
|
|
404
|
+
if (args.outputCert) {
|
|
405
|
+
await fs.writeFile(args.outputCert, certificateJWT, "utf8");
|
|
406
|
+
log(`✓ Certificate saved to ${args.outputCert}`);
|
|
407
|
+
}
|
|
408
|
+
if (!args.outputKey && !args.keyFile) {
|
|
409
|
+
log("\n⚠️ Warning: Private key was not saved to a file. Consider using --output-key to save it.");
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
catch (error) {
|
|
414
|
+
console.error("An error occurred during CA certificate creation:", error);
|
|
415
|
+
process.exit(1);
|
|
416
|
+
}
|
|
417
|
+
},
|
|
418
|
+
});
|
|
419
|
+
const registerCmd = command({
|
|
420
|
+
name: "register",
|
|
421
|
+
description: "Register device by creating key pair, generating CSR, and obtaining certificate from CA.",
|
|
422
|
+
args: {
|
|
423
|
+
...subjectOptions(),
|
|
424
|
+
caUrl: option({
|
|
425
|
+
long: "ca-url",
|
|
426
|
+
description: "CA URL to open in browser for certificate signing",
|
|
427
|
+
type: string,
|
|
428
|
+
defaultValue: () => "http://localhost:7370/fp/cloud/csr2cert",
|
|
429
|
+
}),
|
|
430
|
+
port: option({
|
|
431
|
+
long: "port",
|
|
432
|
+
description: "Local port for callback server (random port if not specified)",
|
|
433
|
+
type: string,
|
|
434
|
+
defaultValue: () => "",
|
|
435
|
+
}),
|
|
436
|
+
timeout: option({
|
|
437
|
+
long: "timeout",
|
|
438
|
+
description: "Timeout in seconds to wait for certificate from CA",
|
|
439
|
+
type: string,
|
|
440
|
+
defaultValue: () => "60",
|
|
441
|
+
}),
|
|
442
|
+
forceRenew: flag({
|
|
443
|
+
long: "force-renew",
|
|
444
|
+
description: "Force certificate renewal even if one already exists",
|
|
445
|
+
}),
|
|
446
|
+
},
|
|
447
|
+
handler: async function (args) {
|
|
448
|
+
try {
|
|
449
|
+
const keyBag = await getKeyBag(sthis);
|
|
450
|
+
const existingDeviceIdResult = await keyBag.getDeviceId();
|
|
451
|
+
if (existingDeviceIdResult.cert.IsSome() && !args.forceRenew) {
|
|
452
|
+
console.log("Device already has a certificate. Registration not needed.");
|
|
453
|
+
console.log("Use --force-renew to renew the certificate.");
|
|
454
|
+
const jwk = existingDeviceIdResult.deviceId.unwrap();
|
|
455
|
+
const deviceIdKey = (await DeviceIdKey.createFromJWK(jwk)).unwrap();
|
|
456
|
+
const fingerprint = await deviceIdKey.fingerPrint();
|
|
457
|
+
console.log(`Existing Device ID Fingerprint: ${fingerprint}`);
|
|
458
|
+
process.exit(0);
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
if (args.forceRenew && existingDeviceIdResult.cert.IsSome()) {
|
|
462
|
+
console.log("Force renewing certificate...");
|
|
463
|
+
}
|
|
464
|
+
let deviceIdKey;
|
|
465
|
+
if (existingDeviceIdResult.deviceId.IsNone()) {
|
|
466
|
+
console.log("Creating new device ID key pair...");
|
|
467
|
+
deviceIdKey = await DeviceIdKey.create();
|
|
468
|
+
const jwkPrivate = await deviceIdKey.exportPrivateJWK();
|
|
469
|
+
await keyBag.setDeviceId(jwkPrivate);
|
|
470
|
+
const fingerprint = await deviceIdKey.fingerPrint();
|
|
471
|
+
console.log(`Created Device ID Fingerprint: ${fingerprint}`);
|
|
472
|
+
}
|
|
473
|
+
else {
|
|
474
|
+
console.log("Using existing device ID key...");
|
|
475
|
+
const jwkPrivate = existingDeviceIdResult.deviceId.unwrap();
|
|
476
|
+
const createResult = await DeviceIdKey.createFromJWK(jwkPrivate);
|
|
477
|
+
if (createResult.isErr()) {
|
|
478
|
+
console.error("Error loading existing device ID:", createResult.Err());
|
|
479
|
+
process.exit(1);
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
deviceIdKey = createResult.Ok();
|
|
483
|
+
const fingerprint = await deviceIdKey.fingerPrint();
|
|
484
|
+
console.log(`Device ID Fingerprint: ${fingerprint}`);
|
|
485
|
+
}
|
|
486
|
+
console.log("Generating Certificate Signing Request (CSR)...");
|
|
487
|
+
const deviceIdCSR = new DeviceIdCSR(sthis, deviceIdKey);
|
|
488
|
+
const subject = buildSubject(args);
|
|
489
|
+
const csrResult = await deviceIdCSR.createCSR(subject);
|
|
490
|
+
if (csrResult.isErr()) {
|
|
491
|
+
console.error("Failed to generate CSR:", csrResult.Err());
|
|
492
|
+
process.exit(1);
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
const csrJWS = csrResult.Ok();
|
|
496
|
+
console.log("CSR generated successfully.");
|
|
497
|
+
const certFuture = new Future();
|
|
498
|
+
const app = new Hono();
|
|
499
|
+
let serverInstance = null;
|
|
500
|
+
app.get("/cert", (c) => {
|
|
501
|
+
const cert = c.req.query("cert");
|
|
502
|
+
if (!cert) {
|
|
503
|
+
certFuture.reject(new Error("Missing cert parameter"));
|
|
504
|
+
return c.text("Missing cert parameter", 400);
|
|
505
|
+
}
|
|
506
|
+
console.log("\nCertificate received from CA!");
|
|
507
|
+
certFuture.resolve(cert);
|
|
508
|
+
return c.text("Certificate received successfully. You can close this window.");
|
|
509
|
+
});
|
|
510
|
+
const port = args.port ? parseInt(args.port, 10) : Math.floor(Math.random() * (65535 - 49152) + 49152);
|
|
511
|
+
const callbackUrl = `http://localhost:${port}/cert`;
|
|
512
|
+
console.log(`Starting local server on port ${port}...`);
|
|
513
|
+
serverInstance = serve({
|
|
514
|
+
fetch: app.fetch,
|
|
515
|
+
port,
|
|
516
|
+
});
|
|
517
|
+
const caUri = BuildURI.from(args.caUrl).setParam("csr", csrJWS).setParam("returnUrl", callbackUrl);
|
|
518
|
+
const caUrlWithParams = caUri.toString();
|
|
519
|
+
console.log(`\nOpening browser to CA for certificate signing...`);
|
|
520
|
+
console.log(`URL: ${caUrlWithParams}\n`);
|
|
521
|
+
try {
|
|
522
|
+
await open(caUrlWithParams);
|
|
523
|
+
}
|
|
524
|
+
catch (error) {
|
|
525
|
+
console.log("Could not automatically open browser. Please open this URL manually:");
|
|
526
|
+
console.log(caUrlWithParams);
|
|
527
|
+
}
|
|
528
|
+
console.log("Waiting for certificate from CA...");
|
|
529
|
+
console.log("(The browser should redirect back to this application after signing)\n");
|
|
530
|
+
const timeoutMs = parseInt(args.timeout, 10) * 1000;
|
|
531
|
+
const result = await timeouted(certFuture.asPromise(), { timeout: timeoutMs });
|
|
532
|
+
if (serverInstance) {
|
|
533
|
+
serverInstance.close();
|
|
534
|
+
}
|
|
535
|
+
if (!isSuccess(result)) {
|
|
536
|
+
if (isTimeout(result)) {
|
|
537
|
+
console.error(`Timeout waiting for certificate from CA (${args.timeout}s).`);
|
|
538
|
+
}
|
|
539
|
+
else {
|
|
540
|
+
console.error("Failed to receive certificate:", result.state === "error" ? result.error : result);
|
|
541
|
+
}
|
|
542
|
+
process.exit(1);
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
const receivedCert = result.value;
|
|
546
|
+
console.log("Storing certificate...");
|
|
547
|
+
const decoded = decodeJwt(receivedCert);
|
|
548
|
+
const certPayload = CertificatePayloadSchema.parse(decoded);
|
|
549
|
+
const jwkPrivate = await deviceIdKey.exportPrivateJWK();
|
|
550
|
+
const certToStore = {
|
|
551
|
+
certificateJWT: receivedCert,
|
|
552
|
+
certificatePayload: certPayload,
|
|
553
|
+
};
|
|
554
|
+
await keyBag.setDeviceId(jwkPrivate, certToStore);
|
|
555
|
+
console.log("\n✓ Registration complete! Certificate successfully stored with Device ID.");
|
|
556
|
+
const fingerprint = await deviceIdKey.fingerPrint();
|
|
557
|
+
console.log(`Device ID Fingerprint: ${fingerprint}`);
|
|
558
|
+
}
|
|
559
|
+
catch (error) {
|
|
560
|
+
console.error("An error occurred during registration:", error);
|
|
561
|
+
process.exit(1);
|
|
562
|
+
}
|
|
563
|
+
},
|
|
564
|
+
});
|
|
565
|
+
return subcommands({
|
|
566
|
+
name: "device-id",
|
|
567
|
+
description: "Manage device identities.",
|
|
568
|
+
cmds: {
|
|
569
|
+
create: createCmd,
|
|
570
|
+
csr: csrCmd,
|
|
571
|
+
export: exportCmd,
|
|
572
|
+
cert: certCmd,
|
|
573
|
+
"ca-cert": caCertCmd,
|
|
574
|
+
register: registerCmd,
|
|
575
|
+
},
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
//# sourceMappingURL=device-id-cmd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-id-cmd.js","sourceRoot":"","sources":["../jsr/device-id-cmd.ts"],"names":[],"mappings":"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"}
|
package/main.js
CHANGED
|
@@ -9,6 +9,7 @@ import { keyCmd } from "./cloud-token-key-cmd.js";
|
|
|
9
9
|
import { preSignedUrlCmd } from "./pre-signed-url.js";
|
|
10
10
|
import { dependabotCmd } from "./dependabot-cmd.js";
|
|
11
11
|
import { testContainerCmd } from "./test-container-cmd.js";
|
|
12
|
+
import { deviceIdCmd } from "./device-id-cmd.js";
|
|
12
13
|
(async () => {
|
|
13
14
|
dotenv.config(process.env.FP_ENV ?? ".env");
|
|
14
15
|
const sthis = ensureSuperThis();
|
|
@@ -29,6 +30,7 @@ import { testContainerCmd } from "./test-container-cmd.js";
|
|
|
29
30
|
setDependencies: setDependenciesCmd(sthis),
|
|
30
31
|
dependabot: dependabotCmd(sthis),
|
|
31
32
|
testContainer: testContainerCmd(sthis),
|
|
33
|
+
deviceId: deviceIdCmd(sthis),
|
|
32
34
|
},
|
|
33
35
|
});
|
|
34
36
|
await run(cmd, process.argv.slice(2));
|
package/main.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","sourceRoot":"","sources":["../jsr/main.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAE1C,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AAC5B,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACzE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../jsr/main.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAE1C,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AAC5B,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACzE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,CAAC,KAAK,IAAI,EAAE;IACV,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,MAAM,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAG,eAAe,EAAE,CAAC;IAGhC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,KAAK,EAAE,CAAC;QAC9B,OAAO,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IACjD,CAAC;IAED,MAAM,GAAG,GAAG,WAAW,CAAC;QACtB,IAAI,EAAE,UAAU;QAChB,WAAW,EAAE,oBAAoB;QACjC,OAAO,EAAE,OAAO;QAChB,IAAI,EAAE;YACJ,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC;YAClB,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC;YAClB,QAAQ,EAAE,WAAW,CAAC,KAAK,CAAC;YAC5B,SAAS,EAAE,eAAe,CAAC,KAAK,CAAC;YACjC,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC;YACtB,UAAU,EAAE,aAAa,CAAC,KAAK,CAAC;YAChC,eAAe,EAAE,kBAAkB,CAAC,KAAK,CAAC;YAC1C,UAAU,EAAE,aAAa,CAAC,KAAK,CAAC;YAChC,aAAa,EAAE,gBAAgB,CAAC,KAAK,CAAC;YACtC,QAAQ,EAAE,WAAW,CAAC,KAAK,CAAC;SAC7B;KACF,CAAC,CAAC;IAEH,MAAM,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAExC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fireproof/core-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.2-dev-cloud-api",
|
|
4
4
|
"description": "Live ledger for the web.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": "./run.js",
|
|
@@ -31,26 +31,31 @@
|
|
|
31
31
|
"url": "https://github.com/fireproof-storage/fireproof/issues"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@adviser/cement": "^0.4.
|
|
35
|
-
"@fireproof/core-
|
|
36
|
-
"@fireproof/core-
|
|
37
|
-
"@fireproof/
|
|
38
|
-
"@
|
|
34
|
+
"@adviser/cement": "^0.4.72",
|
|
35
|
+
"@fireproof/core-device-id": "0.24.2-dev-cloud-api",
|
|
36
|
+
"@fireproof/core-keybag": "0.24.2-dev-cloud-api",
|
|
37
|
+
"@fireproof/core-runtime": "0.24.2-dev-cloud-api",
|
|
38
|
+
"@fireproof/core-types-base": "0.24.2-dev-cloud-api",
|
|
39
|
+
"@fireproof/vendor": "0.24.2-dev-cloud-api",
|
|
40
|
+
"@hono/node-server": "^1.19.6",
|
|
41
|
+
"@typescript/native-preview": "7.0.0-dev.20251123.1",
|
|
39
42
|
"aws4fetch": "^1.0.20",
|
|
40
|
-
"cmd-ts": "^0.14.
|
|
43
|
+
"cmd-ts": "^0.14.3",
|
|
41
44
|
"find-up": "^8.0.0",
|
|
42
45
|
"fs-extra": "^11.3.1",
|
|
43
|
-
"
|
|
46
|
+
"hono": "^4.10.6",
|
|
47
|
+
"jose": "^6.1.2",
|
|
44
48
|
"multiformats": "^13.4.0",
|
|
49
|
+
"open": "^11.0.0",
|
|
45
50
|
"semver": "^7.7.3",
|
|
46
|
-
"zx": "^8.8.
|
|
51
|
+
"zx": "^8.8.5"
|
|
47
52
|
},
|
|
48
53
|
"devDependencies": {
|
|
49
|
-
"@fireproof/core-cli": "0.
|
|
54
|
+
"@fireproof/core-cli": "0.24.2-dev-cloud-api",
|
|
50
55
|
"@types/fs-extra": "^11.0.4",
|
|
51
56
|
"@types/semver": "^7.7.1",
|
|
52
57
|
"tsx": "^4.20.4",
|
|
53
|
-
"vitest": "^
|
|
58
|
+
"vitest": "^4.0.8"
|
|
54
59
|
},
|
|
55
60
|
"scripts": {
|
|
56
61
|
"build": "node ./run.js tsc",
|
package/test-container-cmd.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { SuperThis } from "@fireproof/core-types-base";
|
|
2
2
|
export declare function testContainerCmd(sthis: SuperThis): Partial<import("cmd-ts/dist/cjs/argparser.js").Register> & {
|
|
3
3
|
parse(context: import("cmd-ts/dist/cjs/argparser.js").ParseContext): Promise<import("cmd-ts/dist/cjs/argparser.js").ParsingResult<{
|
|
4
|
+
command: "build";
|
|
5
|
+
args: {};
|
|
6
|
+
} | {
|
|
4
7
|
command: "publish";
|
|
5
8
|
args: {
|
|
6
9
|
repoUrl: string;
|
|
@@ -10,9 +13,6 @@ export declare function testContainerCmd(sthis: SuperThis): Partial<import("cmd-
|
|
|
10
13
|
dockerfile: string;
|
|
11
14
|
context: string;
|
|
12
15
|
};
|
|
13
|
-
} | {
|
|
14
|
-
command: "build";
|
|
15
|
-
args: {};
|
|
16
16
|
} | {
|
|
17
17
|
command: "template";
|
|
18
18
|
args: {
|
|
@@ -31,6 +31,9 @@ export declare function testContainerCmd(sthis: SuperThis): Partial<import("cmd-
|
|
|
31
31
|
};
|
|
32
32
|
}>>;
|
|
33
33
|
} & import("cmd-ts/dist/cjs/helpdoc.js").Named & Partial<import("cmd-ts/dist/cjs/helpdoc.js").Descriptive & import("cmd-ts/dist/cjs/helpdoc.js").Versioned> & import("cmd-ts/dist/cjs/helpdoc.js").PrintHelp & Partial<import("cmd-ts/dist/cjs/helpdoc.js").Versioned> & import("cmd-ts/dist/cjs/argparser.js").Register & import("cmd-ts/dist/cjs/runner.js").Handling<{
|
|
34
|
+
command: "build";
|
|
35
|
+
args: {};
|
|
36
|
+
} | {
|
|
34
37
|
command: "publish";
|
|
35
38
|
args: {
|
|
36
39
|
repoUrl: string;
|
|
@@ -40,9 +43,6 @@ export declare function testContainerCmd(sthis: SuperThis): Partial<import("cmd-
|
|
|
40
43
|
dockerfile: string;
|
|
41
44
|
context: string;
|
|
42
45
|
};
|
|
43
|
-
} | {
|
|
44
|
-
command: "build";
|
|
45
|
-
args: {};
|
|
46
46
|
} | {
|
|
47
47
|
command: "template";
|
|
48
48
|
args: {
|
|
@@ -60,20 +60,20 @@ export declare function testContainerCmd(sthis: SuperThis): Partial<import("cmd-
|
|
|
60
60
|
context: string;
|
|
61
61
|
};
|
|
62
62
|
}, {
|
|
63
|
-
command: "
|
|
63
|
+
command: "build";
|
|
64
64
|
value: Promise<void>;
|
|
65
65
|
} | {
|
|
66
|
-
command: "
|
|
66
|
+
command: "publish";
|
|
67
67
|
value: Promise<void>;
|
|
68
68
|
} | {
|
|
69
69
|
command: "template";
|
|
70
70
|
value: Promise<void>;
|
|
71
71
|
}> & {
|
|
72
72
|
run(context: import("cmd-ts/dist/cjs/argparser.js").ParseContext): Promise<import("cmd-ts/dist/cjs/argparser.js").ParsingResult<{
|
|
73
|
-
command: "
|
|
73
|
+
command: "build";
|
|
74
74
|
value: Promise<void>;
|
|
75
75
|
} | {
|
|
76
|
-
command: "
|
|
76
|
+
command: "publish";
|
|
77
77
|
value: Promise<void>;
|
|
78
78
|
} | {
|
|
79
79
|
command: "template";
|