@getjack/jack 0.1.7 → 0.1.9
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/package.json +5 -2
- package/src/commands/down.ts +20 -3
- package/src/commands/login.ts +9 -9
- package/src/commands/mcp.ts +17 -1
- package/src/commands/new.ts +1 -0
- package/src/commands/projects.ts +48 -44
- package/src/lib/agent-files.ts +0 -2
- package/src/lib/build-helper.ts +6 -6
- package/src/lib/config-generator.ts +13 -0
- package/src/lib/config.ts +2 -1
- package/src/lib/hooks.ts +239 -101
- package/src/lib/json-edit.ts +56 -0
- package/src/lib/mcp-config.ts +2 -1
- package/src/lib/output.ts +83 -12
- package/src/lib/paths-index.test.ts +7 -7
- package/src/lib/project-detection.ts +19 -0
- package/src/lib/project-operations.ts +28 -21
- package/src/lib/project-resolver.ts +4 -1
- package/src/lib/secrets.ts +1 -2
- package/src/lib/telemetry-config.ts +3 -3
- package/src/lib/telemetry.ts +29 -0
- package/src/mcp/test-utils.ts +113 -0
- package/src/templates/index.ts +1 -1
- package/src/templates/types.ts +17 -0
- package/templates/CLAUDE.md +21 -1
- package/templates/miniapp/.jack.json +31 -2
- package/templates/miniapp/index.html +0 -1
- package/templates/miniapp/public/.well-known/farcaster.json +15 -15
- package/templates/miniapp/src/worker.ts +27 -4
package/package.json
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getjack/jack",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Ship before you forget why you started. The vibecoder's deployment CLI.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
8
|
"jack": "./src/index.ts"
|
|
9
9
|
},
|
|
10
|
-
"files": [
|
|
10
|
+
"files": [
|
|
11
|
+
"src",
|
|
12
|
+
"templates"
|
|
13
|
+
],
|
|
11
14
|
"engines": {
|
|
12
15
|
"bun": ">=1.0.0"
|
|
13
16
|
},
|
package/src/commands/down.ts
CHANGED
|
@@ -62,6 +62,7 @@ export interface DownFlags {
|
|
|
62
62
|
export default async function down(projectName?: string, flags: DownFlags = {}): Promise<void> {
|
|
63
63
|
try {
|
|
64
64
|
// Get project name
|
|
65
|
+
const hasExplicitName = Boolean(projectName);
|
|
65
66
|
let name = projectName;
|
|
66
67
|
if (!name) {
|
|
67
68
|
try {
|
|
@@ -74,10 +75,12 @@ export default async function down(projectName?: string, flags: DownFlags = {}):
|
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
// Resolve project from all sources (local link + control plane)
|
|
77
|
-
const resolved = await resolveProject(name
|
|
78
|
+
const resolved = await resolveProject(name, {
|
|
79
|
+
preferLocalLink: !hasExplicitName,
|
|
80
|
+
});
|
|
78
81
|
|
|
79
|
-
// Read local project link
|
|
80
|
-
const link = await readProjectLink(process.cwd());
|
|
82
|
+
// Read local project link (only when no explicit name provided)
|
|
83
|
+
const link = hasExplicitName ? null : await readProjectLink(process.cwd());
|
|
81
84
|
|
|
82
85
|
// Check if found only on control plane (orphaned managed project)
|
|
83
86
|
if (resolved?.sources.controlPlane && !resolved.sources.filesystem) {
|
|
@@ -85,6 +88,20 @@ export default async function down(projectName?: string, flags: DownFlags = {}):
|
|
|
85
88
|
info(`Found "${name}" on jack cloud, linking locally...`);
|
|
86
89
|
}
|
|
87
90
|
|
|
91
|
+
|
|
92
|
+
// Guard against mismatched resolutions when an explicit name is provided
|
|
93
|
+
if (hasExplicitName && resolved) {
|
|
94
|
+
const matches =
|
|
95
|
+
name === resolved.slug ||
|
|
96
|
+
name === resolved.name ||
|
|
97
|
+
name === resolved.remote?.projectId;
|
|
98
|
+
if (!matches) {
|
|
99
|
+
error(`Refusing to undeploy '${name}' because it resolves to '${resolved.slug}'.`);
|
|
100
|
+
info("Use the exact slug/name shown by 'jack info' and try again.");
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
88
105
|
if (!resolved && !link) {
|
|
89
106
|
// Not found anywhere
|
|
90
107
|
warn(`Project '${name}' not found`);
|
package/src/commands/login.ts
CHANGED
|
@@ -6,7 +6,8 @@ import {
|
|
|
6
6
|
getCurrentUserProfile,
|
|
7
7
|
setUsername,
|
|
8
8
|
} from "../lib/control-plane.ts";
|
|
9
|
-
import { error, info, spinner, success, warn } from "../lib/output.ts";
|
|
9
|
+
import { celebrate, error, info, spinner, success, warn } from "../lib/output.ts";
|
|
10
|
+
import { identifyUser } from "../lib/telemetry.ts";
|
|
10
11
|
|
|
11
12
|
interface LoginOptions {
|
|
12
13
|
/** Skip the initial "Logging in..." message (used when called from auto-login) */
|
|
@@ -31,13 +32,7 @@ export default async function login(options: LoginOptions = {}): Promise<void> {
|
|
|
31
32
|
process.exit(1);
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
console.error(" ┌────────────────────────────────────┐");
|
|
36
|
-
console.error(" │ │");
|
|
37
|
-
console.error(` │ Your code: ${deviceAuth.user_code.padEnd(12)} │`);
|
|
38
|
-
console.error(" │ │");
|
|
39
|
-
console.error(" └────────────────────────────────────┘");
|
|
40
|
-
console.error("");
|
|
35
|
+
celebrate("Your code:", [deviceAuth.user_code]);
|
|
41
36
|
info(`Opening ${deviceAuth.verification_uri} in your browser...`);
|
|
42
37
|
console.error("");
|
|
43
38
|
|
|
@@ -73,8 +68,12 @@ export default async function login(options: LoginOptions = {}): Promise<void> {
|
|
|
73
68
|
};
|
|
74
69
|
await saveCredentials(creds);
|
|
75
70
|
|
|
71
|
+
// Link user identity for cross-platform analytics
|
|
72
|
+
await identifyUser(tokens.user.id, { email: tokens.user.email });
|
|
73
|
+
|
|
76
74
|
console.error("");
|
|
77
|
-
|
|
75
|
+
const displayName = tokens.user.first_name || "Logged in";
|
|
76
|
+
success(tokens.user.first_name ? `Welcome back, ${displayName}` : displayName);
|
|
78
77
|
|
|
79
78
|
// Prompt for username if not set
|
|
80
79
|
await promptForUsername(tokens.user.email);
|
|
@@ -209,3 +208,4 @@ function normalizeToUsername(input: string): string {
|
|
|
209
208
|
.replace(/^-+|-+$/g, "")
|
|
210
209
|
.slice(0, 39);
|
|
211
210
|
}
|
|
211
|
+
|
package/src/commands/mcp.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
+
import { rm, mkdtemp } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
2
6
|
import { error, info, success } from "../lib/output.ts";
|
|
3
7
|
import { startMcpServer } from "../mcp/server.ts";
|
|
4
8
|
|
|
9
|
+
const cliRoot = fileURLToPath(new URL("../..", import.meta.url));
|
|
10
|
+
|
|
5
11
|
interface McpOptions {
|
|
6
12
|
project?: string;
|
|
7
13
|
debug?: boolean;
|
|
@@ -32,10 +38,19 @@ export default async function mcp(subcommand?: string, options: McpOptions = {})
|
|
|
32
38
|
* Test MCP server by spawning it and sending test requests
|
|
33
39
|
*/
|
|
34
40
|
async function testMcpServer(): Promise<void> {
|
|
41
|
+
const configDir = await mkdtemp(join(tmpdir(), "jack-config-"));
|
|
42
|
+
|
|
35
43
|
info("Testing MCP server...\n");
|
|
36
44
|
|
|
37
|
-
const proc = spawn("
|
|
45
|
+
const proc = spawn("bun", ["run", "src/index.ts", "mcp", "serve"], {
|
|
38
46
|
stdio: ["pipe", "pipe", "pipe"],
|
|
47
|
+
cwd: cliRoot,
|
|
48
|
+
env: {
|
|
49
|
+
...process.env,
|
|
50
|
+
CI: "1",
|
|
51
|
+
JACK_TELEMETRY_DISABLED: "1",
|
|
52
|
+
JACK_CONFIG_DIR: configDir,
|
|
53
|
+
},
|
|
39
54
|
});
|
|
40
55
|
|
|
41
56
|
const results: { test: string; passed: boolean; error?: string }[] = [];
|
|
@@ -126,6 +141,7 @@ async function testMcpServer(): Promise<void> {
|
|
|
126
141
|
error(` ✗ Error: ${errorMsg}`);
|
|
127
142
|
} finally {
|
|
128
143
|
proc.kill();
|
|
144
|
+
await rm(configDir, { recursive: true, force: true });
|
|
129
145
|
}
|
|
130
146
|
|
|
131
147
|
// Summary
|
package/src/commands/new.ts
CHANGED
package/src/commands/projects.ts
CHANGED
|
@@ -18,11 +18,7 @@ import {
|
|
|
18
18
|
sortByUpdated,
|
|
19
19
|
toListItems,
|
|
20
20
|
} from "../lib/project-list.ts";
|
|
21
|
-
import {
|
|
22
|
-
cleanupStaleProjects,
|
|
23
|
-
getProjectStatus,
|
|
24
|
-
scanStaleProjects,
|
|
25
|
-
} from "../lib/project-operations.ts";
|
|
21
|
+
import { cleanupStaleProjects, scanStaleProjects } from "../lib/project-operations.ts";
|
|
26
22
|
import {
|
|
27
23
|
type ResolvedProject,
|
|
28
24
|
listAllProjects,
|
|
@@ -104,7 +100,7 @@ async function listProjects(args: string[]): Promise<void> {
|
|
|
104
100
|
const cloudOnly = args.includes("--cloud");
|
|
105
101
|
|
|
106
102
|
// Fetch all projects from registry and control plane
|
|
107
|
-
outputSpinner.start("
|
|
103
|
+
outputSpinner.start("Loading projects...");
|
|
108
104
|
const projects: ResolvedProject[] = await listAllProjects();
|
|
109
105
|
outputSpinner.stop();
|
|
110
106
|
|
|
@@ -154,6 +150,7 @@ async function listProjects(args: string[]): Promise<void> {
|
|
|
154
150
|
function renderGroupedView(items: ProjectListItem[]): void {
|
|
155
151
|
const groups = groupProjects(items);
|
|
156
152
|
const total = items.length;
|
|
153
|
+
const CLOUD_LIMIT = 5;
|
|
157
154
|
|
|
158
155
|
// Build consistent tag color map across all projects
|
|
159
156
|
const tagColorMap = buildTagColorMap(items);
|
|
@@ -178,7 +175,6 @@ function renderGroupedView(items: ProjectListItem[]): void {
|
|
|
178
175
|
|
|
179
176
|
// Section 3: Cloud-only (show last N by updatedAt)
|
|
180
177
|
if (groups.cloudOnly.length > 0) {
|
|
181
|
-
const CLOUD_LIMIT = 5;
|
|
182
178
|
const sorted = sortByUpdated(groups.cloudOnly);
|
|
183
179
|
|
|
184
180
|
console.error("");
|
|
@@ -191,9 +187,14 @@ function renderGroupedView(items: ProjectListItem[]): void {
|
|
|
191
187
|
);
|
|
192
188
|
}
|
|
193
189
|
|
|
194
|
-
// Footer hint
|
|
190
|
+
// Footer hint - only show --all hint if there are hidden cloud projects
|
|
195
191
|
console.error("");
|
|
196
|
-
|
|
192
|
+
const hasHiddenCloudProjects = groups.cloudOnly.length > CLOUD_LIMIT;
|
|
193
|
+
if (hasHiddenCloudProjects) {
|
|
194
|
+
info(`jack ls --all to see all ${groups.cloudOnly.length} cloud projects`);
|
|
195
|
+
} else {
|
|
196
|
+
info("jack ls --status error to filter, --json for machine output");
|
|
197
|
+
}
|
|
197
198
|
console.error("");
|
|
198
199
|
}
|
|
199
200
|
|
|
@@ -247,6 +248,7 @@ function renderFlatTable(items: ProjectListItem[]): void {
|
|
|
247
248
|
* Show detailed project info
|
|
248
249
|
*/
|
|
249
250
|
async function infoProject(args: string[]): Promise<void> {
|
|
251
|
+
const hasExplicitName = Boolean(args[0]);
|
|
250
252
|
let name = args[0];
|
|
251
253
|
|
|
252
254
|
// If no name provided, try to get from cwd
|
|
@@ -261,80 +263,82 @@ async function infoProject(args: string[]): Promise<void> {
|
|
|
261
263
|
}
|
|
262
264
|
}
|
|
263
265
|
|
|
264
|
-
//
|
|
266
|
+
// Resolve project using the same pattern as down.ts
|
|
265
267
|
outputSpinner.start("Fetching project info...");
|
|
266
|
-
const
|
|
268
|
+
const resolved = await resolveProject(name, {
|
|
269
|
+
preferLocalLink: !hasExplicitName,
|
|
270
|
+
includeResources: true,
|
|
271
|
+
});
|
|
267
272
|
outputSpinner.stop();
|
|
268
273
|
|
|
269
|
-
|
|
270
|
-
|
|
274
|
+
// Guard against mismatched resolutions when an explicit name is provided
|
|
275
|
+
if (hasExplicitName && resolved) {
|
|
276
|
+
const matches =
|
|
277
|
+
name === resolved.slug || name === resolved.name || name === resolved.remote?.projectId;
|
|
278
|
+
if (!matches) {
|
|
279
|
+
error(`Project '${name}' resolves to '${resolved.slug}'.`);
|
|
280
|
+
info("Use the exact slug/name and try again.");
|
|
281
|
+
process.exit(1);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (!resolved) {
|
|
286
|
+
error(`Project "${name}" not found`);
|
|
271
287
|
info("List projects with: jack projects list");
|
|
272
288
|
process.exit(1);
|
|
273
289
|
}
|
|
274
290
|
|
|
275
291
|
console.error("");
|
|
276
|
-
info(`Project: ${
|
|
292
|
+
info(`Project: ${resolved.name}`);
|
|
277
293
|
console.error("");
|
|
278
294
|
|
|
279
295
|
// Status section
|
|
280
296
|
const statuses: string[] = [];
|
|
281
|
-
if (
|
|
297
|
+
if (resolved.sources.filesystem) {
|
|
282
298
|
statuses.push("local");
|
|
283
299
|
}
|
|
284
|
-
if (status
|
|
300
|
+
if (resolved.status === "live") {
|
|
285
301
|
statuses.push("deployed");
|
|
286
302
|
}
|
|
287
|
-
if (status.backedUp) {
|
|
288
|
-
statuses.push("backup");
|
|
289
|
-
}
|
|
290
303
|
|
|
291
304
|
item(`Status: ${statuses.join(", ") || "none"}`);
|
|
292
305
|
console.error("");
|
|
293
306
|
|
|
294
307
|
// Workspace info (only shown if running from project directory)
|
|
295
|
-
if (
|
|
296
|
-
item(`Workspace path: ${
|
|
308
|
+
if (resolved.localPath) {
|
|
309
|
+
item(`Workspace path: ${resolved.localPath}`);
|
|
297
310
|
console.error("");
|
|
298
311
|
}
|
|
299
312
|
|
|
300
313
|
// Deployment info
|
|
301
|
-
if (
|
|
302
|
-
item(`Worker URL: ${
|
|
303
|
-
}
|
|
304
|
-
if (status.lastDeployed) {
|
|
305
|
-
item(`Last deployed: ${new Date(status.lastDeployed).toLocaleString()}`);
|
|
314
|
+
if (resolved.url) {
|
|
315
|
+
item(`Worker URL: ${resolved.url}`);
|
|
306
316
|
}
|
|
307
|
-
if (
|
|
308
|
-
|
|
317
|
+
if (resolved.updatedAt) {
|
|
318
|
+
item(`Last deployed: ${new Date(resolved.updatedAt).toLocaleString()}`);
|
|
309
319
|
}
|
|
310
|
-
|
|
311
|
-
// Backup info
|
|
312
|
-
if (status.backedUp && status.backupFiles !== null) {
|
|
313
|
-
item(`Backup: ${status.backupFiles} files`);
|
|
314
|
-
if (status.backupLastSync) {
|
|
315
|
-
item(`Last synced: ${new Date(status.backupLastSync).toLocaleString()}`);
|
|
316
|
-
}
|
|
320
|
+
if (resolved.status === "live") {
|
|
317
321
|
console.error("");
|
|
318
322
|
}
|
|
319
323
|
|
|
320
324
|
// Account info
|
|
321
|
-
if (
|
|
322
|
-
item(`Account ID: ${
|
|
325
|
+
if (resolved.remote?.orgId) {
|
|
326
|
+
item(`Account ID: ${resolved.remote.orgId}`);
|
|
323
327
|
}
|
|
324
|
-
if (
|
|
325
|
-
item(`Worker ID: ${
|
|
328
|
+
if (resolved.slug) {
|
|
329
|
+
item(`Worker ID: ${resolved.slug}`);
|
|
326
330
|
}
|
|
327
331
|
console.error("");
|
|
328
332
|
|
|
329
333
|
// Resources
|
|
330
|
-
if (
|
|
331
|
-
item(`Database: ${
|
|
334
|
+
if (resolved.resources?.d1?.name) {
|
|
335
|
+
item(`Database: ${resolved.resources.d1.name}`);
|
|
332
336
|
console.error("");
|
|
333
337
|
}
|
|
334
338
|
|
|
335
339
|
// Timestamps
|
|
336
|
-
if (
|
|
337
|
-
item(`Created: ${new Date(
|
|
340
|
+
if (resolved.createdAt) {
|
|
341
|
+
item(`Created: ${new Date(resolved.createdAt).toLocaleString()}`);
|
|
338
342
|
}
|
|
339
343
|
console.error("");
|
|
340
344
|
}
|
|
@@ -406,7 +410,7 @@ async function removeProjectEntry(args: string[]): Promise<void> {
|
|
|
406
410
|
}
|
|
407
411
|
|
|
408
412
|
// Use resolver to find project anywhere (registry OR control plane)
|
|
409
|
-
outputSpinner.start("
|
|
413
|
+
outputSpinner.start("Finding project...");
|
|
410
414
|
const project = await resolveProject(name);
|
|
411
415
|
outputSpinner.stop();
|
|
412
416
|
|
package/src/lib/agent-files.ts
CHANGED
|
@@ -21,7 +21,6 @@ This project is deployed to Cloudflare Workers using jack:
|
|
|
21
21
|
\`\`\`bash
|
|
22
22
|
jack ship # Deploy to Cloudflare Workers
|
|
23
23
|
jack logs # Stream production logs
|
|
24
|
-
jack dev # Start local development server
|
|
25
24
|
\`\`\`
|
|
26
25
|
|
|
27
26
|
All deployment is handled by jack. Never run \`wrangler\` commands directly.
|
|
@@ -42,7 +41,6 @@ See [AGENTS.md](./AGENTS.md) for complete project context and deployment instruc
|
|
|
42
41
|
|
|
43
42
|
- **Deploy**: \`jack ship\` - Deploy to Cloudflare Workers
|
|
44
43
|
- **Logs**: \`jack logs\` - Stream production logs
|
|
45
|
-
- **Dev**: \`jack dev\` - Start local development server
|
|
46
44
|
|
|
47
45
|
## Important
|
|
48
46
|
|
package/src/lib/build-helper.ts
CHANGED
|
@@ -172,19 +172,19 @@ export async function buildProject(options: BuildOptions): Promise<BuildOutput>
|
|
|
172
172
|
// Check if OpenNext build is needed (Next.js + Cloudflare)
|
|
173
173
|
const hasOpenNext = await needsOpenNextBuild(projectPath);
|
|
174
174
|
if (hasOpenNext) {
|
|
175
|
-
reporter?.start("Building...");
|
|
175
|
+
reporter?.start("Building assets...");
|
|
176
176
|
await runOpenNextBuild(projectPath);
|
|
177
177
|
reporter?.stop();
|
|
178
|
-
reporter?.success("Built");
|
|
178
|
+
reporter?.success("Built assets");
|
|
179
179
|
}
|
|
180
180
|
|
|
181
181
|
// Check if Vite build is needed and run it (skip if OpenNext already built)
|
|
182
182
|
const hasVite = await needsViteBuild(projectPath);
|
|
183
183
|
if (hasVite && !hasOpenNext) {
|
|
184
|
-
reporter?.start("Building...");
|
|
184
|
+
reporter?.start("Building assets...");
|
|
185
185
|
await runViteBuild(projectPath);
|
|
186
186
|
reporter?.stop();
|
|
187
|
-
reporter?.success("Built");
|
|
187
|
+
reporter?.success("Built assets");
|
|
188
188
|
}
|
|
189
189
|
|
|
190
190
|
// Create unique temp directory for build output
|
|
@@ -193,7 +193,7 @@ export async function buildProject(options: BuildOptions): Promise<BuildOutput>
|
|
|
193
193
|
await mkdir(outDir, { recursive: true });
|
|
194
194
|
|
|
195
195
|
// Run wrangler dry-run to build without deploying
|
|
196
|
-
reporter?.start("
|
|
196
|
+
reporter?.start("Bundling runtime...");
|
|
197
197
|
|
|
198
198
|
const dryRunResult = await $`wrangler deploy --dry-run --outdir=${outDir}`
|
|
199
199
|
.cwd(projectPath)
|
|
@@ -215,7 +215,7 @@ export async function buildProject(options: BuildOptions): Promise<BuildOutput>
|
|
|
215
215
|
}
|
|
216
216
|
|
|
217
217
|
reporter?.stop();
|
|
218
|
-
reporter?.success("
|
|
218
|
+
reporter?.success("Bundled runtime");
|
|
219
219
|
|
|
220
220
|
const entrypoint = await resolveEntrypoint(outDir, config.main);
|
|
221
221
|
|
|
@@ -33,6 +33,19 @@ export function generateWranglerConfig(
|
|
|
33
33
|
};
|
|
34
34
|
|
|
35
35
|
case "vite":
|
|
36
|
+
// Check if this is a Vite + Worker hybrid (has entryPoint)
|
|
37
|
+
if (entryPoint) {
|
|
38
|
+
// Hybrid mode: Vite frontend + custom Worker backend
|
|
39
|
+
return {
|
|
40
|
+
name: projectName,
|
|
41
|
+
main: entryPoint,
|
|
42
|
+
compatibility_date: COMPATIBILITY_DATE,
|
|
43
|
+
assets: {
|
|
44
|
+
directory: "./dist",
|
|
45
|
+
binding: "ASSETS",
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
36
49
|
// Pure Vite SPAs use assets-only mode (no worker entry)
|
|
37
50
|
// Cloudflare auto-generates a worker that serves static files
|
|
38
51
|
return {
|
package/src/lib/config.ts
CHANGED
|
@@ -40,7 +40,8 @@ export interface JackConfig {
|
|
|
40
40
|
sync?: SyncConfig;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
const DEFAULT_CONFIG_DIR = join(homedir(), ".config", "jack");
|
|
44
|
+
export const CONFIG_DIR = process.env.JACK_CONFIG_DIR ?? DEFAULT_CONFIG_DIR;
|
|
44
45
|
export const CONFIG_PATH = join(CONFIG_DIR, "config.json");
|
|
45
46
|
|
|
46
47
|
/**
|