coolsecrets 0.1.0
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/dist/index.js +476 -0
- package/dist/index.js.map +1 -0
- package/package.json +49 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { CommanderError } from "commander";
|
|
5
|
+
|
|
6
|
+
// src/create-cli.ts
|
|
7
|
+
import { Command } from "commander";
|
|
8
|
+
|
|
9
|
+
// ../shared/dist/schemas.js
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
var ZodSchemaValidator = class {
|
|
12
|
+
apiTokenSchema = z.string().trim().min(1, "Coolify API token is required.");
|
|
13
|
+
baseUrlSchema = z.string().trim().min(1, "Coolify base URL is required.").url("Coolify base URL must be a valid absolute URL.").refine((value) => value.startsWith("http://") || value.startsWith("https://"), {
|
|
14
|
+
message: "Coolify base URL must use http or https."
|
|
15
|
+
}).transform((value) => value.replace(/\/$/, ""));
|
|
16
|
+
coolifyCredentialsSchema = z.object({
|
|
17
|
+
baseUrl: this.baseUrlSchema,
|
|
18
|
+
apiToken: this.apiTokenSchema
|
|
19
|
+
});
|
|
20
|
+
localBindingSchema = z.object({
|
|
21
|
+
applicationId: z.string().trim().min(1, "Application ID is required."),
|
|
22
|
+
applicationName: z.string().trim().min(1, "Application name is required."),
|
|
23
|
+
projectName: z.string().trim().min(1).nullable()
|
|
24
|
+
});
|
|
25
|
+
infisicalExportSchema = z.record(z.string().trim().min(1, "Environment variable keys must not be empty."), z.string());
|
|
26
|
+
rawCoolifyApplicationSchema = z.object({
|
|
27
|
+
id: z.string().trim().min(1).optional(),
|
|
28
|
+
uuid: z.string().trim().min(1).optional(),
|
|
29
|
+
name: z.string().trim().min(1, "Coolify application payload is missing a valid name."),
|
|
30
|
+
project: z.object({
|
|
31
|
+
name: z.string().trim().min(1).optional()
|
|
32
|
+
}).partial().nullish(),
|
|
33
|
+
projectName: z.string().trim().min(1).optional(),
|
|
34
|
+
project_name: z.string().trim().min(1).optional()
|
|
35
|
+
}).passthrough().transform((value, context) => {
|
|
36
|
+
const applicationId = value.id ?? value.uuid;
|
|
37
|
+
if (!applicationId) {
|
|
38
|
+
context.addIssue({
|
|
39
|
+
code: "custom",
|
|
40
|
+
message: "Coolify application payload is missing a valid id or uuid."
|
|
41
|
+
});
|
|
42
|
+
return z.NEVER;
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
id: applicationId,
|
|
46
|
+
name: value.name,
|
|
47
|
+
projectName: value.project?.name ?? value.projectName ?? value.project_name ?? null
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
applicationsPayloadSchema = z.union([
|
|
51
|
+
z.array(this.rawCoolifyApplicationSchema),
|
|
52
|
+
z.object({
|
|
53
|
+
data: z.array(this.rawCoolifyApplicationSchema)
|
|
54
|
+
})
|
|
55
|
+
]).transform((value) => Array.isArray(value) ? value : value.data);
|
|
56
|
+
normalizeCoolifyCredentials(input2) {
|
|
57
|
+
return this.parseWithSchema(this.coolifyCredentialsSchema, input2, "Invalid Coolify credentials.");
|
|
58
|
+
}
|
|
59
|
+
parseGlobalConfig(value) {
|
|
60
|
+
return this.parseWithSchema(this.coolifyCredentialsSchema, value, "Invalid global config format.");
|
|
61
|
+
}
|
|
62
|
+
parseLocalBinding(value) {
|
|
63
|
+
return this.parseWithSchema(this.localBindingSchema, value, "Invalid local binding format.");
|
|
64
|
+
}
|
|
65
|
+
parseApplicationsPayload(value) {
|
|
66
|
+
return this.parseWithSchema(this.applicationsPayloadSchema, value, "Invalid applications payload received from Coolify.");
|
|
67
|
+
}
|
|
68
|
+
parseInfisicalExport(value) {
|
|
69
|
+
return this.parseWithSchema(this.infisicalExportSchema, value, "Infisical export payload must be a JSON object with string values.");
|
|
70
|
+
}
|
|
71
|
+
parseWithSchema(schema, value, fallbackMessage) {
|
|
72
|
+
const result = schema.safeParse(value);
|
|
73
|
+
if (!result.success) {
|
|
74
|
+
throw new Error(this.getFirstIssueMessage(result.error, fallbackMessage));
|
|
75
|
+
}
|
|
76
|
+
return result.data;
|
|
77
|
+
}
|
|
78
|
+
getFirstIssueMessage(error, fallbackMessage) {
|
|
79
|
+
return error.issues[0]?.message ?? fallbackMessage;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// ../shared/dist/coolify-client.js
|
|
84
|
+
var HttpCoolifyClient = class {
|
|
85
|
+
fetchFn;
|
|
86
|
+
validator;
|
|
87
|
+
constructor(fetchFn = fetch, validator = new ZodSchemaValidator()) {
|
|
88
|
+
this.fetchFn = fetchFn;
|
|
89
|
+
this.validator = validator;
|
|
90
|
+
}
|
|
91
|
+
async listApplications(config) {
|
|
92
|
+
const response = await this.fetchFn(this.buildApplicationsUrl(config.baseUrl), {
|
|
93
|
+
method: "GET",
|
|
94
|
+
headers: this.buildHeaders(config)
|
|
95
|
+
});
|
|
96
|
+
if (!response.ok) {
|
|
97
|
+
throw new Error(this.buildFetchErrorMessage("fetch applications", response));
|
|
98
|
+
}
|
|
99
|
+
return this.validator.parseApplicationsPayload(await response.json());
|
|
100
|
+
}
|
|
101
|
+
async syncApplicationEnvs(config, applicationId, environmentVariables) {
|
|
102
|
+
const response = await this.fetchFn(this.buildBulkEnvsUrl(config.baseUrl, applicationId), {
|
|
103
|
+
method: "PATCH",
|
|
104
|
+
headers: this.buildHeaders(config),
|
|
105
|
+
body: JSON.stringify({ data: environmentVariables })
|
|
106
|
+
});
|
|
107
|
+
if (!response.ok) {
|
|
108
|
+
throw new Error(this.buildFetchErrorMessage("sync application environment variables", response));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
buildApplicationsUrl(baseUrl) {
|
|
112
|
+
return new URL("/api/v1/applications", `${baseUrl}/`).toString();
|
|
113
|
+
}
|
|
114
|
+
buildBulkEnvsUrl(baseUrl, applicationId) {
|
|
115
|
+
return new URL(`/api/v1/applications/${applicationId}/envs/bulk`, `${baseUrl}/`).toString();
|
|
116
|
+
}
|
|
117
|
+
buildHeaders(config) {
|
|
118
|
+
return {
|
|
119
|
+
Accept: "application/json",
|
|
120
|
+
Authorization: `Bearer ${config.apiToken}`,
|
|
121
|
+
"Content-Type": "application/json"
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
buildFetchErrorMessage(action, response) {
|
|
125
|
+
return `Failed to ${action} in Coolify (${response.status} ${response.statusText}).`;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// ../shared/dist/infisical-export-parser.js
|
|
130
|
+
var InfisicalExportParser = class {
|
|
131
|
+
validator;
|
|
132
|
+
constructor(validator = new ZodSchemaValidator()) {
|
|
133
|
+
this.validator = validator;
|
|
134
|
+
}
|
|
135
|
+
parse(rawInput) {
|
|
136
|
+
return this.fromObject(this.parseJson(rawInput));
|
|
137
|
+
}
|
|
138
|
+
fromObject(payload) {
|
|
139
|
+
const exportPayload = this.validator.parseInfisicalExport(payload);
|
|
140
|
+
const environmentVariables = Object.entries(exportPayload).map(([key, value]) => ({
|
|
141
|
+
key,
|
|
142
|
+
value,
|
|
143
|
+
is_preview: false,
|
|
144
|
+
is_literal: false,
|
|
145
|
+
is_multiline: value.includes("\n"),
|
|
146
|
+
is_shown_once: false
|
|
147
|
+
}));
|
|
148
|
+
if (environmentVariables.length === 0) {
|
|
149
|
+
throw new Error("Infisical export payload does not contain any environment variables.");
|
|
150
|
+
}
|
|
151
|
+
return environmentVariables;
|
|
152
|
+
}
|
|
153
|
+
parseJson(rawInput) {
|
|
154
|
+
try {
|
|
155
|
+
return JSON.parse(rawInput);
|
|
156
|
+
} catch {
|
|
157
|
+
throw new Error("Infisical export input must be valid JSON.");
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
// src/config-store.ts
|
|
163
|
+
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
164
|
+
import { homedir } from "os";
|
|
165
|
+
import path from "path";
|
|
166
|
+
var FileConfigStore = class {
|
|
167
|
+
constructor(validator = new ZodSchemaValidator()) {
|
|
168
|
+
this.validator = validator;
|
|
169
|
+
}
|
|
170
|
+
validator;
|
|
171
|
+
async readGlobalConfig() {
|
|
172
|
+
try {
|
|
173
|
+
const contents = await readFile(this.getGlobalConfigPath(), "utf8");
|
|
174
|
+
return this.validator.parseGlobalConfig(JSON.parse(contents));
|
|
175
|
+
} catch (error) {
|
|
176
|
+
if (this.isMissingFileError(error)) {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
throw error;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
async saveGlobalConfig(config) {
|
|
183
|
+
const configPath = this.getGlobalConfigPath();
|
|
184
|
+
const normalizedConfig = this.validator.parseGlobalConfig(config);
|
|
185
|
+
await mkdir(path.dirname(configPath), { recursive: true });
|
|
186
|
+
await writeFile(configPath, `${JSON.stringify(normalizedConfig, null, 2)}
|
|
187
|
+
`, {
|
|
188
|
+
encoding: "utf8",
|
|
189
|
+
mode: 384
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
async readLocalBinding(cwd) {
|
|
193
|
+
try {
|
|
194
|
+
const contents = await readFile(this.getLocalConfigPath(cwd), "utf8");
|
|
195
|
+
return this.validator.parseLocalBinding(JSON.parse(contents));
|
|
196
|
+
} catch (error) {
|
|
197
|
+
if (this.isMissingFileError(error)) {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
throw error;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
async saveLocalBinding(cwd, binding) {
|
|
204
|
+
const configPath = this.getLocalConfigPath(cwd);
|
|
205
|
+
const normalizedBinding = this.validator.parseLocalBinding(binding);
|
|
206
|
+
await writeFile(configPath, `${JSON.stringify(normalizedBinding, null, 2)}
|
|
207
|
+
`, {
|
|
208
|
+
encoding: "utf8",
|
|
209
|
+
mode: 384
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
getGlobalConfigPath() {
|
|
213
|
+
return path.join(this.getGlobalConfigDirectory(), "config.json");
|
|
214
|
+
}
|
|
215
|
+
getLocalConfigPath(cwd) {
|
|
216
|
+
return path.join(cwd, ".coolsecrets.json");
|
|
217
|
+
}
|
|
218
|
+
getGlobalConfigDirectory() {
|
|
219
|
+
const xdgConfigHome = process.env.XDG_CONFIG_HOME;
|
|
220
|
+
if (xdgConfigHome) {
|
|
221
|
+
return path.join(xdgConfigHome, "coolsecrets");
|
|
222
|
+
}
|
|
223
|
+
return path.join(homedir(), ".config", "coolsecrets");
|
|
224
|
+
}
|
|
225
|
+
isMissingFileError(error) {
|
|
226
|
+
return error.code === "ENOENT";
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
// src/init-prompter.ts
|
|
231
|
+
import { input, password, select } from "@inquirer/prompts";
|
|
232
|
+
function formatApplicationLabel(application) {
|
|
233
|
+
return application.projectName ? `${application.projectName} / ${application.name}` : application.name;
|
|
234
|
+
}
|
|
235
|
+
var InteractiveInitPrompter = class {
|
|
236
|
+
async promptBaseUrl() {
|
|
237
|
+
return input({
|
|
238
|
+
message: "Coolify base URL",
|
|
239
|
+
validate: (value) => value.trim().length > 0 ? true : "Base URL is required."
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
async promptApiToken() {
|
|
243
|
+
return password({
|
|
244
|
+
message: "Coolify API token",
|
|
245
|
+
mask: "*",
|
|
246
|
+
validate: (value) => value.trim().length > 0 ? true : "API token is required."
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
async selectApplication(applications) {
|
|
250
|
+
return select({
|
|
251
|
+
message: "Select the Coolify application for this directory",
|
|
252
|
+
choices: applications.map((application) => ({
|
|
253
|
+
name: formatApplicationLabel(application),
|
|
254
|
+
value: application
|
|
255
|
+
})),
|
|
256
|
+
pageSize: 12
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
// src/init-command.ts
|
|
262
|
+
var InitCommand = class {
|
|
263
|
+
constructor(options, validator = new ZodSchemaValidator()) {
|
|
264
|
+
this.options = options;
|
|
265
|
+
this.validator = validator;
|
|
266
|
+
}
|
|
267
|
+
options;
|
|
268
|
+
validator;
|
|
269
|
+
async execute() {
|
|
270
|
+
const { credentials, shouldPersist } = await this.resolveCredentials();
|
|
271
|
+
const applications = await this.options.coolifyClient.listApplications(credentials);
|
|
272
|
+
if (applications.length === 0) {
|
|
273
|
+
throw new Error("No Coolify applications were found for the provided credentials.");
|
|
274
|
+
}
|
|
275
|
+
if (shouldPersist) {
|
|
276
|
+
await this.options.configStore.saveGlobalConfig(credentials);
|
|
277
|
+
}
|
|
278
|
+
const application = await this.options.prompter.selectApplication(applications);
|
|
279
|
+
await this.options.configStore.saveLocalBinding(this.options.cwd, {
|
|
280
|
+
applicationId: application.id,
|
|
281
|
+
applicationName: application.name,
|
|
282
|
+
projectName: application.projectName
|
|
283
|
+
});
|
|
284
|
+
this.options.stdout.write(
|
|
285
|
+
`Linked current directory to ${formatApplicationLabel(application)}.
|
|
286
|
+
`
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
async resolveCredentials() {
|
|
290
|
+
const existingConfig = await this.options.configStore.readGlobalConfig();
|
|
291
|
+
if (existingConfig) {
|
|
292
|
+
return {
|
|
293
|
+
credentials: existingConfig,
|
|
294
|
+
shouldPersist: false
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
return {
|
|
298
|
+
credentials: this.validator.normalizeCoolifyCredentials({
|
|
299
|
+
baseUrl: await this.options.prompter.promptBaseUrl(),
|
|
300
|
+
apiToken: await this.options.prompter.promptApiToken()
|
|
301
|
+
}),
|
|
302
|
+
shouldPersist: true
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
// src/read-stdin.ts
|
|
308
|
+
async function readStdin(stream = process.stdin) {
|
|
309
|
+
if ("isTTY" in stream && stream.isTTY) {
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
const chunks = [];
|
|
313
|
+
for await (const chunk of stream) {
|
|
314
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
|
|
315
|
+
}
|
|
316
|
+
const input2 = Buffer.concat(chunks).toString("utf8").trim();
|
|
317
|
+
return input2.length > 0 ? input2 : null;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// src/sync-command.ts
|
|
321
|
+
var SyncCommand = class _SyncCommand {
|
|
322
|
+
constructor(options, parser = new InfisicalExportParser()) {
|
|
323
|
+
this.options = options;
|
|
324
|
+
this.parser = parser;
|
|
325
|
+
}
|
|
326
|
+
options;
|
|
327
|
+
parser;
|
|
328
|
+
static noStdinMessage = [
|
|
329
|
+
"No input received from stdin.",
|
|
330
|
+
"Example: infisical export --env=prod --format=json | coolsecrets sync"
|
|
331
|
+
].join("\n");
|
|
332
|
+
async execute() {
|
|
333
|
+
const rawInput = await readStdin(this.options.stdin);
|
|
334
|
+
if (rawInput === null) {
|
|
335
|
+
throw new Error(_SyncCommand.noStdinMessage);
|
|
336
|
+
}
|
|
337
|
+
const environmentVariables = this.parser.parse(rawInput);
|
|
338
|
+
const credentials = await this.readGlobalConfig();
|
|
339
|
+
const binding = await this.readLocalBinding();
|
|
340
|
+
await this.options.coolifyClient.syncApplicationEnvs(
|
|
341
|
+
credentials,
|
|
342
|
+
binding.applicationId,
|
|
343
|
+
environmentVariables
|
|
344
|
+
);
|
|
345
|
+
this.options.stdout.write(
|
|
346
|
+
`Synced ${environmentVariables.length} environment variable${environmentVariables.length === 1 ? "" : "s"} to ${this.formatBindingLabel(binding)}.
|
|
347
|
+
`
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
async readGlobalConfig() {
|
|
351
|
+
const config = await this.options.configStore.readGlobalConfig();
|
|
352
|
+
if (config === null) {
|
|
353
|
+
throw new Error("Coolify credentials are not configured. Run `coolsecrets init` first.");
|
|
354
|
+
}
|
|
355
|
+
return config;
|
|
356
|
+
}
|
|
357
|
+
async readLocalBinding() {
|
|
358
|
+
const binding = await this.options.configStore.readLocalBinding(this.options.cwd);
|
|
359
|
+
if (binding === null) {
|
|
360
|
+
throw new Error(
|
|
361
|
+
"Current directory is not linked to a Coolify application. Run `coolsecrets init` first."
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
return binding;
|
|
365
|
+
}
|
|
366
|
+
formatBindingLabel(binding) {
|
|
367
|
+
return binding.projectName ? `${binding.projectName} / ${binding.applicationName}` : binding.applicationName;
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
// src/create-cli.ts
|
|
372
|
+
var CoolSecretsCli = class {
|
|
373
|
+
cwd;
|
|
374
|
+
stdin;
|
|
375
|
+
stdout;
|
|
376
|
+
stderr;
|
|
377
|
+
configStore;
|
|
378
|
+
coolifyClient;
|
|
379
|
+
prompter;
|
|
380
|
+
program;
|
|
381
|
+
constructor(options = {}) {
|
|
382
|
+
this.cwd = options.cwd ?? process.cwd();
|
|
383
|
+
this.stdin = options.stdin ?? process.stdin;
|
|
384
|
+
this.stdout = options.stdout ?? process.stdout;
|
|
385
|
+
this.stderr = options.stderr ?? process.stderr;
|
|
386
|
+
this.configStore = options.configStore ?? new FileConfigStore();
|
|
387
|
+
this.coolifyClient = options.coolifyClient ?? new HttpCoolifyClient();
|
|
388
|
+
this.prompter = options.prompter ?? new InteractiveInitPrompter();
|
|
389
|
+
this.program = this.createProgram();
|
|
390
|
+
}
|
|
391
|
+
getProgram() {
|
|
392
|
+
return this.program;
|
|
393
|
+
}
|
|
394
|
+
createProgram() {
|
|
395
|
+
const program = new Command().name("coolsecrets").description("CLI to sync secrets from stdin into Coolify applications").version("0.1.0").option("--init", "Initialize Coolify settings for the current directory").configureOutput({
|
|
396
|
+
writeOut: (message) => this.stdout.write(message),
|
|
397
|
+
writeErr: (message) => this.stderr.write(message)
|
|
398
|
+
}).showHelpAfterError().exitOverride();
|
|
399
|
+
this.configureInitCommand(program);
|
|
400
|
+
this.configureSyncCommand(program);
|
|
401
|
+
this.configureDefaultAction(program);
|
|
402
|
+
return program;
|
|
403
|
+
}
|
|
404
|
+
configureInitCommand(program) {
|
|
405
|
+
const initCommand = program.command("init").description("Initialize Coolify settings for the current directory");
|
|
406
|
+
initCommand.action(async () => {
|
|
407
|
+
await this.runInit(initCommand);
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
configureSyncCommand(program) {
|
|
411
|
+
const syncCommand = program.command("sync").description("Sync a piped JSON secrets payload into the linked Coolify application");
|
|
412
|
+
syncCommand.action(async () => {
|
|
413
|
+
await this.runSync(syncCommand);
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
configureDefaultAction(program) {
|
|
417
|
+
program.action(async () => {
|
|
418
|
+
if (program.opts().init) {
|
|
419
|
+
await this.runInit(program);
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
await this.runSync(program);
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
async runInit(command) {
|
|
426
|
+
try {
|
|
427
|
+
await new InitCommand({
|
|
428
|
+
cwd: this.cwd,
|
|
429
|
+
stdout: this.stdout,
|
|
430
|
+
configStore: this.configStore,
|
|
431
|
+
coolifyClient: this.coolifyClient,
|
|
432
|
+
prompter: this.prompter
|
|
433
|
+
}).execute();
|
|
434
|
+
} catch (error) {
|
|
435
|
+
command.error(error instanceof Error ? error.message : "Unexpected init error.", {
|
|
436
|
+
code: "coolsecrets.init",
|
|
437
|
+
exitCode: 1
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
async runSync(command) {
|
|
442
|
+
try {
|
|
443
|
+
await new SyncCommand({
|
|
444
|
+
cwd: this.cwd,
|
|
445
|
+
stdin: this.stdin,
|
|
446
|
+
stdout: this.stdout,
|
|
447
|
+
configStore: this.configStore,
|
|
448
|
+
coolifyClient: this.coolifyClient
|
|
449
|
+
}).execute();
|
|
450
|
+
} catch (error) {
|
|
451
|
+
command.error(error instanceof Error ? error.message : "Unexpected sync error.", {
|
|
452
|
+
code: "coolsecrets.sync",
|
|
453
|
+
exitCode: 1
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
function createCli(options = {}) {
|
|
459
|
+
return new CoolSecretsCli(options).getProgram();
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// src/index.ts
|
|
463
|
+
async function main() {
|
|
464
|
+
const program = createCli();
|
|
465
|
+
try {
|
|
466
|
+
await program.parseAsync(process.argv);
|
|
467
|
+
} catch (error) {
|
|
468
|
+
if (error instanceof CommanderError) {
|
|
469
|
+
process.exitCode = error.exitCode;
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
throw error;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
void main();
|
|
476
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/create-cli.ts","../../shared/src/schemas.ts","../../shared/src/coolify-client.ts","../../shared/src/infisical-export-parser.ts","../src/config-store.ts","../src/init-prompter.ts","../src/init-command.ts","../src/read-stdin.ts","../src/sync-command.ts"],"sourcesContent":["import { CommanderError } from 'commander';\n\nimport { createCli } from './create-cli.js';\n\nasync function main(): Promise<void> {\n const program = createCli();\n\n try {\n await program.parseAsync(process.argv);\n } catch (error) {\n if (error instanceof CommanderError) {\n process.exitCode = error.exitCode;\n return;\n }\n\n throw error;\n }\n}\n\nvoid main();\n","import { Command } from 'commander';\n\nimport { HttpCoolifyClient, type CoolifyClient } from '@coolsecrets/shared';\n\nimport { FileConfigStore, type ConfigStore } from './config-store.js';\nimport { InteractiveInitPrompter, type InitPrompter } from './init-prompter.js';\nimport { InitCommand } from './init-command.js';\nimport { SyncCommand } from './sync-command.js';\n\nexport interface CreateCliOptions {\n cwd?: string;\n stdin?: NodeJS.ReadableStream;\n stdout?: NodeJS.WritableStream;\n stderr?: NodeJS.WritableStream;\n configStore?: ConfigStore;\n coolifyClient?: CoolifyClient;\n prompter?: InitPrompter;\n}\n\nexport class CoolSecretsCli {\n private readonly cwd: string;\n private readonly stdin: NodeJS.ReadableStream;\n private readonly stdout: NodeJS.WritableStream;\n private readonly stderr: NodeJS.WritableStream;\n private readonly configStore: ConfigStore;\n private readonly coolifyClient: CoolifyClient;\n private readonly prompter: InitPrompter;\n private readonly program: Command;\n\n constructor(options: CreateCliOptions = {}) {\n this.cwd = options.cwd ?? process.cwd();\n this.stdin = options.stdin ?? process.stdin;\n this.stdout = options.stdout ?? process.stdout;\n this.stderr = options.stderr ?? process.stderr;\n this.configStore = options.configStore ?? new FileConfigStore();\n this.coolifyClient = options.coolifyClient ?? new HttpCoolifyClient();\n this.prompter = options.prompter ?? new InteractiveInitPrompter();\n this.program = this.createProgram();\n }\n\n public getProgram(): Command {\n return this.program;\n }\n\n private createProgram(): Command {\n const program = new Command()\n .name('coolsecrets')\n .description('CLI to sync secrets from stdin into Coolify applications')\n .version('0.1.0')\n .option('--init', 'Initialize Coolify settings for the current directory')\n .configureOutput({\n writeOut: (message) => this.stdout.write(message),\n writeErr: (message) => this.stderr.write(message),\n })\n .showHelpAfterError()\n .exitOverride();\n\n this.configureInitCommand(program);\n this.configureSyncCommand(program);\n this.configureDefaultAction(program);\n\n return program;\n }\n\n private configureInitCommand(program: Command): void {\n const initCommand = program\n .command('init')\n .description('Initialize Coolify settings for the current directory');\n\n initCommand.action(async () => {\n await this.runInit(initCommand);\n });\n }\n\n private configureSyncCommand(program: Command): void {\n const syncCommand = program\n .command('sync')\n .description('Sync a piped JSON secrets payload into the linked Coolify application');\n\n syncCommand.action(async () => {\n await this.runSync(syncCommand);\n });\n }\n\n private configureDefaultAction(program: Command): void {\n program.action(async () => {\n if (program.opts<{ init?: boolean }>().init) {\n await this.runInit(program);\n return;\n }\n\n await this.runSync(program);\n });\n }\n\n private async runInit(command: Command): Promise<void> {\n try {\n await new InitCommand({\n cwd: this.cwd,\n stdout: this.stdout,\n configStore: this.configStore,\n coolifyClient: this.coolifyClient,\n prompter: this.prompter,\n }).execute();\n } catch (error) {\n command.error(error instanceof Error ? error.message : 'Unexpected init error.', {\n code: 'coolsecrets.init',\n exitCode: 1,\n });\n }\n }\n\n private async runSync(command: Command): Promise<void> {\n try {\n await new SyncCommand({\n cwd: this.cwd,\n stdin: this.stdin,\n stdout: this.stdout,\n configStore: this.configStore,\n coolifyClient: this.coolifyClient,\n }).execute();\n } catch (error) {\n command.error(error instanceof Error ? error.message : 'Unexpected sync error.', {\n code: 'coolsecrets.sync',\n exitCode: 1,\n });\n }\n }\n}\n\nexport function createCli(options: CreateCliOptions = {}): Command {\n return new CoolSecretsCli(options).getProgram();\n}\n","import { z } from 'zod';\n\nimport type {\n CoolifyApplication,\n CoolifyCredentials,\n InfisicalExport,\n LocalBinding,\n} from './types.js';\n\nexport interface SchemaValidator {\n normalizeCoolifyCredentials(input: { baseUrl: string; apiToken: string }): CoolifyCredentials;\n parseGlobalConfig(value: unknown): CoolifyCredentials;\n parseLocalBinding(value: unknown): LocalBinding;\n parseApplicationsPayload(value: unknown): CoolifyApplication[];\n parseInfisicalExport(value: unknown): InfisicalExport;\n}\n\nexport class ZodSchemaValidator implements SchemaValidator {\n private readonly apiTokenSchema = z.string().trim().min(1, 'Coolify API token is required.');\n\n private readonly baseUrlSchema = z\n .string()\n .trim()\n .min(1, 'Coolify base URL is required.')\n .url('Coolify base URL must be a valid absolute URL.')\n .refine((value) => value.startsWith('http://') || value.startsWith('https://'), {\n message: 'Coolify base URL must use http or https.',\n })\n .transform((value) => value.replace(/\\/$/, ''));\n\n private readonly coolifyCredentialsSchema = z.object({\n baseUrl: this.baseUrlSchema,\n apiToken: this.apiTokenSchema,\n });\n\n private readonly localBindingSchema = z.object({\n applicationId: z.string().trim().min(1, 'Application ID is required.'),\n applicationName: z.string().trim().min(1, 'Application name is required.'),\n projectName: z.string().trim().min(1).nullable(),\n });\n\n private readonly infisicalExportSchema = z.record(\n z.string().trim().min(1, 'Environment variable keys must not be empty.'),\n z.string(),\n );\n\n private readonly rawCoolifyApplicationSchema = z\n .object({\n id: z.string().trim().min(1).optional(),\n uuid: z.string().trim().min(1).optional(),\n name: z.string().trim().min(1, 'Coolify application payload is missing a valid name.'),\n project: z\n .object({\n name: z.string().trim().min(1).optional(),\n })\n .partial()\n .nullish(),\n projectName: z.string().trim().min(1).optional(),\n project_name: z.string().trim().min(1).optional(),\n })\n .passthrough()\n .transform((value, context): CoolifyApplication => {\n const applicationId = value.id ?? value.uuid;\n\n if (!applicationId) {\n context.addIssue({\n code: 'custom',\n message: 'Coolify application payload is missing a valid id or uuid.',\n });\n return z.NEVER;\n }\n\n return {\n id: applicationId,\n name: value.name,\n projectName: value.project?.name ?? value.projectName ?? value.project_name ?? null,\n };\n });\n\n private readonly applicationsPayloadSchema = z\n .union([\n z.array(this.rawCoolifyApplicationSchema),\n z.object({\n data: z.array(this.rawCoolifyApplicationSchema),\n }),\n ])\n .transform((value) => (Array.isArray(value) ? value : value.data));\n\n public normalizeCoolifyCredentials(input: {\n baseUrl: string;\n apiToken: string;\n }): CoolifyCredentials {\n return this.parseWithSchema(\n this.coolifyCredentialsSchema,\n input,\n 'Invalid Coolify credentials.',\n );\n }\n\n public parseGlobalConfig(value: unknown): CoolifyCredentials {\n return this.parseWithSchema(this.coolifyCredentialsSchema, value, 'Invalid global config format.');\n }\n\n public parseLocalBinding(value: unknown): LocalBinding {\n return this.parseWithSchema(this.localBindingSchema, value, 'Invalid local binding format.');\n }\n\n public parseApplicationsPayload(value: unknown): CoolifyApplication[] {\n return this.parseWithSchema(\n this.applicationsPayloadSchema,\n value,\n 'Invalid applications payload received from Coolify.',\n );\n }\n\n public parseInfisicalExport(value: unknown): InfisicalExport {\n return this.parseWithSchema(\n this.infisicalExportSchema,\n value,\n 'Infisical export payload must be a JSON object with string values.',\n );\n }\n\n private parseWithSchema<TSchema extends z.ZodType>(\n schema: TSchema,\n value: unknown,\n fallbackMessage: string,\n ): z.infer<TSchema> {\n const result = schema.safeParse(value);\n\n if (!result.success) {\n throw new Error(this.getFirstIssueMessage(result.error, fallbackMessage));\n }\n\n return result.data;\n }\n\n private getFirstIssueMessage(error: z.ZodError, fallbackMessage: string): string {\n return error.issues[0]?.message ?? fallbackMessage;\n }\n}\n","import { ZodSchemaValidator, type SchemaValidator } from './schemas.js';\nimport type {\n CoolifyApplication,\n CoolifyApplicationEnvironmentVariable,\n CoolifyCredentials,\n} from './types.js';\n\nexport interface CoolifyClient {\n listApplications(config: CoolifyCredentials): Promise<CoolifyApplication[]>;\n syncApplicationEnvs(\n config: CoolifyCredentials,\n applicationId: string,\n environmentVariables: CoolifyApplicationEnvironmentVariable[],\n ): Promise<void>;\n}\n\nexport class HttpCoolifyClient implements CoolifyClient {\n constructor(\n private readonly fetchFn: typeof fetch = fetch,\n private readonly validator: SchemaValidator = new ZodSchemaValidator(),\n ) {}\n\n public async listApplications(config: CoolifyCredentials): Promise<CoolifyApplication[]> {\n const response = await this.fetchFn(this.buildApplicationsUrl(config.baseUrl), {\n method: 'GET',\n headers: this.buildHeaders(config),\n });\n\n if (!response.ok) {\n throw new Error(this.buildFetchErrorMessage('fetch applications', response));\n }\n\n return this.validator.parseApplicationsPayload(await response.json());\n }\n\n public async syncApplicationEnvs(\n config: CoolifyCredentials,\n applicationId: string,\n environmentVariables: CoolifyApplicationEnvironmentVariable[],\n ): Promise<void> {\n const response = await this.fetchFn(this.buildBulkEnvsUrl(config.baseUrl, applicationId), {\n method: 'PATCH',\n headers: this.buildHeaders(config),\n body: JSON.stringify({ data: environmentVariables }),\n });\n\n if (!response.ok) {\n throw new Error(this.buildFetchErrorMessage('sync application environment variables', response));\n }\n }\n\n private buildApplicationsUrl(baseUrl: string): string {\n return new URL('/api/v1/applications', `${baseUrl}/`).toString();\n }\n\n private buildBulkEnvsUrl(baseUrl: string, applicationId: string): string {\n return new URL(`/api/v1/applications/${applicationId}/envs/bulk`, `${baseUrl}/`).toString();\n }\n\n private buildHeaders(config: CoolifyCredentials): HeadersInit {\n return {\n Accept: 'application/json',\n Authorization: `Bearer ${config.apiToken}`,\n 'Content-Type': 'application/json',\n };\n }\n\n private buildFetchErrorMessage(action: string, response: Response): string {\n return `Failed to ${action} in Coolify (${response.status} ${response.statusText}).`;\n }\n}\n","import { ZodSchemaValidator, type SchemaValidator } from './schemas.js';\nimport type { CoolifyApplicationEnvironmentVariable } from './types.js';\n\nexport class InfisicalExportParser {\n constructor(private readonly validator: SchemaValidator = new ZodSchemaValidator()) {}\n\n public parse(rawInput: string): CoolifyApplicationEnvironmentVariable[] {\n return this.fromObject(this.parseJson(rawInput));\n }\n\n public fromObject(payload: unknown): CoolifyApplicationEnvironmentVariable[] {\n const exportPayload = this.validator.parseInfisicalExport(payload);\n const environmentVariables = Object.entries(exportPayload).map(([key, value]) => ({\n key,\n value,\n is_preview: false,\n is_literal: false,\n is_multiline: value.includes('\\n'),\n is_shown_once: false,\n }));\n\n if (environmentVariables.length === 0) {\n throw new Error('Infisical export payload does not contain any environment variables.');\n }\n\n return environmentVariables;\n }\n\n private parseJson(rawInput: string): unknown {\n try {\n return JSON.parse(rawInput);\n } catch {\n throw new Error('Infisical export input must be valid JSON.');\n }\n }\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport path from 'node:path';\n\nimport {\n ZodSchemaValidator,\n type CoolifyCredentials,\n type LocalBinding,\n type SchemaValidator,\n} from '@coolsecrets/shared';\n\nexport interface ConfigStore {\n readGlobalConfig(): Promise<CoolifyCredentials | null>;\n saveGlobalConfig(config: CoolifyCredentials): Promise<void>;\n readLocalBinding(cwd: string): Promise<LocalBinding | null>;\n saveLocalBinding(cwd: string, binding: LocalBinding): Promise<void>;\n}\n\nexport class FileConfigStore implements ConfigStore {\n constructor(private readonly validator: SchemaValidator = new ZodSchemaValidator()) {}\n\n public async readGlobalConfig(): Promise<CoolifyCredentials | null> {\n try {\n const contents = await readFile(this.getGlobalConfigPath(), 'utf8');\n return this.validator.parseGlobalConfig(JSON.parse(contents));\n } catch (error) {\n if (this.isMissingFileError(error)) {\n return null;\n }\n\n throw error;\n }\n }\n\n public async saveGlobalConfig(config: CoolifyCredentials): Promise<void> {\n const configPath = this.getGlobalConfigPath();\n const normalizedConfig = this.validator.parseGlobalConfig(config);\n\n await mkdir(path.dirname(configPath), { recursive: true });\n await writeFile(configPath, `${JSON.stringify(normalizedConfig, null, 2)}\\n`, {\n encoding: 'utf8',\n mode: 0o600,\n });\n }\n\n public async readLocalBinding(cwd: string): Promise<LocalBinding | null> {\n try {\n const contents = await readFile(this.getLocalConfigPath(cwd), 'utf8');\n return this.validator.parseLocalBinding(JSON.parse(contents));\n } catch (error) {\n if (this.isMissingFileError(error)) {\n return null;\n }\n\n throw error;\n }\n }\n\n public async saveLocalBinding(cwd: string, binding: LocalBinding): Promise<void> {\n const configPath = this.getLocalConfigPath(cwd);\n const normalizedBinding = this.validator.parseLocalBinding(binding);\n\n await writeFile(configPath, `${JSON.stringify(normalizedBinding, null, 2)}\\n`, {\n encoding: 'utf8',\n mode: 0o600,\n });\n }\n\n private getGlobalConfigPath(): string {\n return path.join(this.getGlobalConfigDirectory(), 'config.json');\n }\n\n private getLocalConfigPath(cwd: string): string {\n return path.join(cwd, '.coolsecrets.json');\n }\n\n private getGlobalConfigDirectory(): string {\n const xdgConfigHome = process.env.XDG_CONFIG_HOME;\n\n if (xdgConfigHome) {\n return path.join(xdgConfigHome, 'coolsecrets');\n }\n\n return path.join(homedir(), '.config', 'coolsecrets');\n }\n\n private isMissingFileError(error: unknown): boolean {\n return (error as NodeJS.ErrnoException).code === 'ENOENT';\n }\n}\n","import { input, password, select } from '@inquirer/prompts';\n\nimport type { CoolifyApplication } from '@coolsecrets/shared';\n\nexport interface InitPrompter {\n promptBaseUrl(): Promise<string>;\n promptApiToken(): Promise<string>;\n selectApplication(applications: CoolifyApplication[]): Promise<CoolifyApplication>;\n}\n\nexport function formatApplicationLabel(application: CoolifyApplication): string {\n return application.projectName\n ? `${application.projectName} / ${application.name}`\n : application.name;\n}\n\nexport class InteractiveInitPrompter implements InitPrompter {\n async promptBaseUrl(): Promise<string> {\n return input({\n message: 'Coolify base URL',\n validate: (value) => (value.trim().length > 0 ? true : 'Base URL is required.'),\n });\n }\n\n async promptApiToken(): Promise<string> {\n return password({\n message: 'Coolify API token',\n mask: '*',\n validate: (value) => (value.trim().length > 0 ? true : 'API token is required.'),\n });\n }\n\n async selectApplication(applications: CoolifyApplication[]): Promise<CoolifyApplication> {\n return select({\n message: 'Select the Coolify application for this directory',\n choices: applications.map((application) => ({\n name: formatApplicationLabel(application),\n value: application,\n })),\n pageSize: 12,\n });\n }\n}\n","import {\n ZodSchemaValidator,\n type CoolifyClient,\n type CoolifyCredentials,\n type SchemaValidator,\n} from '@coolsecrets/shared';\n\nimport type { ConfigStore } from './config-store.js';\nimport { formatApplicationLabel, type InitPrompter } from './init-prompter.js';\n\nexport interface RunInitOptions {\n cwd: string;\n stdout: NodeJS.WritableStream;\n configStore: ConfigStore;\n coolifyClient: CoolifyClient;\n prompter: InitPrompter;\n}\n\ninterface ResolvedCredentials {\n credentials: CoolifyCredentials;\n shouldPersist: boolean;\n}\n\nexport class InitCommand {\n constructor(\n private readonly options: RunInitOptions,\n private readonly validator: SchemaValidator = new ZodSchemaValidator(),\n ) {}\n\n public async execute(): Promise<void> {\n const { credentials, shouldPersist } = await this.resolveCredentials();\n const applications = await this.options.coolifyClient.listApplications(credentials);\n\n if (applications.length === 0) {\n throw new Error('No Coolify applications were found for the provided credentials.');\n }\n\n if (shouldPersist) {\n await this.options.configStore.saveGlobalConfig(credentials);\n }\n\n const application = await this.options.prompter.selectApplication(applications);\n\n await this.options.configStore.saveLocalBinding(this.options.cwd, {\n applicationId: application.id,\n applicationName: application.name,\n projectName: application.projectName,\n });\n\n this.options.stdout.write(\n `Linked current directory to ${formatApplicationLabel(application)}.\\n`,\n );\n }\n\n private async resolveCredentials(): Promise<ResolvedCredentials> {\n const existingConfig = await this.options.configStore.readGlobalConfig();\n\n if (existingConfig) {\n return {\n credentials: existingConfig,\n shouldPersist: false,\n };\n }\n\n return {\n credentials: this.validator.normalizeCoolifyCredentials({\n baseUrl: await this.options.prompter.promptBaseUrl(),\n apiToken: await this.options.prompter.promptApiToken(),\n }),\n shouldPersist: true,\n };\n }\n}\n\nexport async function runInit(options: RunInitOptions): Promise<void> {\n await new InitCommand(options).execute();\n}\n","export async function readStdin(\n stream: NodeJS.ReadableStream = process.stdin,\n): Promise<string | null> {\n if ('isTTY' in stream && stream.isTTY) {\n return null;\n }\n\n const chunks: Buffer[] = [];\n\n for await (const chunk of stream) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));\n }\n\n const input = Buffer.concat(chunks).toString('utf8').trim();\n\n return input.length > 0 ? input : null;\n}\n","import {\n InfisicalExportParser,\n type CoolifyClient,\n type CoolifyCredentials,\n type LocalBinding,\n} from '@coolsecrets/shared';\n\nimport type { ConfigStore } from './config-store.js';\nimport { readStdin } from './read-stdin.js';\n\nexport interface RunSyncOptions {\n cwd: string;\n stdin: NodeJS.ReadableStream;\n stdout: NodeJS.WritableStream;\n configStore: ConfigStore;\n coolifyClient: CoolifyClient;\n}\n\nexport class SyncCommand {\n private static readonly noStdinMessage = [\n 'No input received from stdin.',\n 'Example: infisical export --env=prod --format=json | coolsecrets sync',\n ].join('\\n');\n\n constructor(\n private readonly options: RunSyncOptions,\n private readonly parser: InfisicalExportParser = new InfisicalExportParser(),\n ) {}\n\n public async execute(): Promise<void> {\n const rawInput = await readStdin(this.options.stdin);\n\n if (rawInput === null) {\n throw new Error(SyncCommand.noStdinMessage);\n }\n\n const environmentVariables = this.parser.parse(rawInput);\n const credentials = await this.readGlobalConfig();\n const binding = await this.readLocalBinding();\n\n await this.options.coolifyClient.syncApplicationEnvs(\n credentials,\n binding.applicationId,\n environmentVariables,\n );\n\n this.options.stdout.write(\n `Synced ${environmentVariables.length} environment variable${environmentVariables.length === 1 ? '' : 's'} to ${this.formatBindingLabel(binding)}.\\n`,\n );\n }\n\n private async readGlobalConfig(): Promise<CoolifyCredentials> {\n const config = await this.options.configStore.readGlobalConfig();\n\n if (config === null) {\n throw new Error('Coolify credentials are not configured. Run `coolsecrets init` first.');\n }\n\n return config;\n }\n\n private async readLocalBinding(): Promise<LocalBinding> {\n const binding = await this.options.configStore.readLocalBinding(this.options.cwd);\n\n if (binding === null) {\n throw new Error(\n 'Current directory is not linked to a Coolify application. Run `coolsecrets init` first.',\n );\n }\n\n return binding;\n }\n\n private formatBindingLabel(binding: LocalBinding): string {\n return binding.projectName\n ? `${binding.projectName} / ${binding.applicationName}`\n : binding.applicationName;\n }\n}\n"],"mappings":"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","names":["input","input"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "coolsecrets",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI to sync secrets from stdin into Coolify applications",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"coolsecrets": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"cli",
|
|
18
|
+
"coolify",
|
|
19
|
+
"secrets",
|
|
20
|
+
"infisical",
|
|
21
|
+
"typescript"
|
|
22
|
+
],
|
|
23
|
+
"author": "",
|
|
24
|
+
"license": "ISC",
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=20.0.0"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@inquirer/prompts": "^8.4.3",
|
|
30
|
+
"commander": "^14.0.3",
|
|
31
|
+
"zod": "^4.4.3"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/node": "^25.9.1",
|
|
35
|
+
"tsup": "^8.5.1",
|
|
36
|
+
"tsx": "^4.22.3",
|
|
37
|
+
"typescript": "^6.0.3",
|
|
38
|
+
"vitest": "^4.1.7",
|
|
39
|
+
"@coolsecrets/shared": "0.1.0"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "tsup",
|
|
43
|
+
"dev": "tsx src/index.ts",
|
|
44
|
+
"start": "node dist/index.js",
|
|
45
|
+
"test": "vitest run",
|
|
46
|
+
"test:watch": "vitest",
|
|
47
|
+
"typecheck": "tsc -p tsconfig.json"
|
|
48
|
+
}
|
|
49
|
+
}
|