@getjack/jack 0.1.2 → 0.1.3
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 +54 -47
- package/src/commands/agents.ts +145 -10
- package/src/commands/down.ts +110 -102
- package/src/commands/feedback.ts +189 -0
- package/src/commands/init.ts +8 -12
- package/src/commands/login.ts +88 -0
- package/src/commands/logout.ts +14 -0
- package/src/commands/logs.ts +21 -0
- package/src/commands/mcp.ts +134 -7
- package/src/commands/new.ts +43 -17
- package/src/commands/open.ts +13 -6
- package/src/commands/projects.ts +269 -143
- package/src/commands/secrets.ts +413 -0
- package/src/commands/services.ts +96 -123
- package/src/commands/ship.ts +5 -1
- package/src/commands/whoami.ts +31 -0
- package/src/index.ts +218 -144
- package/src/lib/agent-files.ts +34 -0
- package/src/lib/agents.ts +390 -22
- package/src/lib/asset-hash.ts +50 -0
- package/src/lib/auth/client.ts +115 -0
- package/src/lib/auth/constants.ts +5 -0
- package/src/lib/auth/guard.ts +57 -0
- package/src/lib/auth/index.ts +18 -0
- package/src/lib/auth/store.ts +54 -0
- package/src/lib/binding-validator.ts +136 -0
- package/src/lib/build-helper.ts +211 -0
- package/src/lib/cloudflare-api.ts +24 -0
- package/src/lib/config.ts +5 -6
- package/src/lib/control-plane.ts +295 -0
- package/src/lib/debug.ts +3 -1
- package/src/lib/deploy-mode.ts +93 -0
- package/src/lib/deploy-upload.ts +92 -0
- package/src/lib/errors.ts +2 -0
- package/src/lib/github.ts +31 -1
- package/src/lib/hooks.ts +4 -12
- package/src/lib/intent.ts +88 -0
- package/src/lib/jsonc.ts +125 -0
- package/src/lib/local-paths.test.ts +902 -0
- package/src/lib/local-paths.ts +258 -0
- package/src/lib/managed-deploy.ts +175 -0
- package/src/lib/managed-down.ts +159 -0
- package/src/lib/mcp-config.ts +55 -34
- package/src/lib/names.ts +9 -29
- package/src/lib/project-operations.ts +676 -249
- package/src/lib/project-resolver.ts +476 -0
- package/src/lib/registry.ts +76 -37
- package/src/lib/resources.ts +196 -0
- package/src/lib/schema.ts +30 -1
- package/src/lib/storage/file-filter.ts +1 -0
- package/src/lib/storage/index.ts +5 -1
- package/src/lib/telemetry.ts +14 -0
- package/src/lib/tty.ts +15 -0
- package/src/lib/zip-packager.ts +255 -0
- package/src/mcp/resources/index.ts +8 -2
- package/src/mcp/server.ts +32 -4
- package/src/mcp/tools/index.ts +35 -13
- package/src/mcp/types.ts +6 -0
- package/src/mcp/utils.ts +1 -1
- package/src/templates/index.ts +42 -4
- package/src/templates/types.ts +13 -0
- package/templates/CLAUDE.md +166 -0
- package/templates/api/.jack.json +4 -0
- package/templates/api/bun.lock +1 -0
- package/templates/api/wrangler.jsonc +5 -0
- package/templates/hello/.jack.json +28 -0
- package/templates/hello/package.json +10 -0
- package/templates/hello/src/index.ts +11 -0
- package/templates/hello/tsconfig.json +11 -0
- package/templates/hello/wrangler.jsonc +5 -0
- package/templates/miniapp/.jack.json +15 -4
- package/templates/miniapp/bun.lock +135 -40
- package/templates/miniapp/index.html +1 -0
- package/templates/miniapp/package.json +3 -1
- package/templates/miniapp/public/.well-known/farcaster.json +7 -5
- package/templates/miniapp/public/icon.png +0 -0
- package/templates/miniapp/public/og.png +0 -0
- package/templates/miniapp/schema.sql +8 -0
- package/templates/miniapp/src/App.tsx +254 -3
- package/templates/miniapp/src/components/ShareSheet.tsx +147 -0
- package/templates/miniapp/src/hooks/useAI.ts +35 -0
- package/templates/miniapp/src/hooks/useGuestbook.ts +11 -1
- package/templates/miniapp/src/hooks/useShare.ts +76 -0
- package/templates/miniapp/src/index.css +15 -0
- package/templates/miniapp/src/lib/api.ts +2 -1
- package/templates/miniapp/src/worker.ts +515 -1
- package/templates/miniapp/wrangler.jsonc +15 -3
- package/LICENSE +0 -190
- package/README.md +0 -55
- package/src/commands/cloud.ts +0 -230
- package/templates/api/wrangler.toml +0 -3
package/package.json
CHANGED
|
@@ -1,49 +1,56 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
2
|
+
"name": "@getjack/jack",
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"description": "Ship before you forget why you started. The vibecoder's deployment CLI.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"jack": "./src/index.ts"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"src",
|
|
12
|
+
"templates"
|
|
13
|
+
],
|
|
14
|
+
"engines": {
|
|
15
|
+
"bun": ">=1.0.0"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "https://github.com/getjack-org/jack"
|
|
20
|
+
},
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"cli",
|
|
26
|
+
"deploy",
|
|
27
|
+
"vibecoding",
|
|
28
|
+
"vibecode",
|
|
29
|
+
"shipping",
|
|
30
|
+
"edge",
|
|
31
|
+
"serverless",
|
|
32
|
+
"ai-agent",
|
|
33
|
+
"claude",
|
|
34
|
+
"cursor"
|
|
35
|
+
],
|
|
36
|
+
"scripts": {
|
|
37
|
+
"dev": "bun run src/index.ts",
|
|
38
|
+
"build": "bun build --compile src/index.ts --outfile dist/jack",
|
|
39
|
+
"lint": "biome check .",
|
|
40
|
+
"format": "biome format --write ."
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/archiver": "^7.0.0",
|
|
44
|
+
"bun-types": "latest"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@inquirer/prompts": "^7.0.0",
|
|
48
|
+
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
49
|
+
"archiver": "^7.0.1",
|
|
50
|
+
"human-id": "^4.1.3",
|
|
51
|
+
"meow": "^14.0.0",
|
|
52
|
+
"posthog-node": "^5.17.4",
|
|
53
|
+
"yocto-spinner": "^1.0.0",
|
|
54
|
+
"zod": "^4.2.1"
|
|
55
|
+
}
|
|
49
56
|
}
|
package/src/commands/agents.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { regenerateAgentFiles } from "../lib/agent-files.ts";
|
|
1
4
|
import {
|
|
2
5
|
AGENT_REGISTRY,
|
|
3
6
|
addAgent,
|
|
4
7
|
disableAgent,
|
|
5
8
|
enableAgent,
|
|
9
|
+
getActiveAgents,
|
|
6
10
|
getAgentDefinition,
|
|
7
11
|
getPreferredAgent,
|
|
8
12
|
pathExists,
|
|
@@ -12,12 +16,24 @@ import {
|
|
|
12
16
|
updateAgent,
|
|
13
17
|
} from "../lib/agents.ts";
|
|
14
18
|
import { readConfig } from "../lib/config.ts";
|
|
15
|
-
import { error, info, item, output as outputSpinner, success } from "../lib/output.ts";
|
|
19
|
+
import { error, info, item, output as outputSpinner, success, warn } from "../lib/output.ts";
|
|
20
|
+
import { getProject } from "../lib/registry.ts";
|
|
21
|
+
import { getProjectNameFromDir } from "../lib/storage/index.ts";
|
|
22
|
+
import { resolveTemplate } from "../templates/index.ts";
|
|
23
|
+
import type { Template } from "../templates/types.ts";
|
|
16
24
|
|
|
17
25
|
/**
|
|
18
26
|
* Main agents command - handles all agent management
|
|
19
27
|
*/
|
|
20
|
-
|
|
28
|
+
interface AgentsOptions {
|
|
29
|
+
project?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export default async function agents(
|
|
33
|
+
subcommand?: string,
|
|
34
|
+
args: string[] = [],
|
|
35
|
+
options: AgentsOptions = {},
|
|
36
|
+
): Promise<void> {
|
|
21
37
|
if (!subcommand) {
|
|
22
38
|
return await listAgents();
|
|
23
39
|
}
|
|
@@ -35,9 +51,11 @@ export default async function agents(subcommand?: string, args: string[] = []):
|
|
|
35
51
|
return await disableAgentCommand(args);
|
|
36
52
|
case "prefer":
|
|
37
53
|
return await preferAgentCommand(args);
|
|
54
|
+
case "refresh":
|
|
55
|
+
return await refreshAgentFilesCommand(options);
|
|
38
56
|
default:
|
|
39
57
|
error(`Unknown subcommand: ${subcommand}`);
|
|
40
|
-
info("Available: scan, add, remove, enable, disable, prefer");
|
|
58
|
+
info("Available: scan, add, remove, enable, disable, prefer, refresh");
|
|
41
59
|
process.exit(1);
|
|
42
60
|
}
|
|
43
61
|
}
|
|
@@ -76,7 +94,7 @@ async function listAgents(): Promise<void> {
|
|
|
76
94
|
console.error("");
|
|
77
95
|
}
|
|
78
96
|
|
|
79
|
-
info("Commands: jack agents scan | add | remove | enable | disable | prefer");
|
|
97
|
+
info("Commands: jack agents scan | add | remove | enable | disable | prefer | refresh");
|
|
80
98
|
}
|
|
81
99
|
|
|
82
100
|
/**
|
|
@@ -89,6 +107,7 @@ async function scanAndPrompt(): Promise<void> {
|
|
|
89
107
|
|
|
90
108
|
if (detectionResult.detected.length === 0) {
|
|
91
109
|
info("No agents detected");
|
|
110
|
+
await listAgents();
|
|
92
111
|
return;
|
|
93
112
|
}
|
|
94
113
|
|
|
@@ -98,6 +117,7 @@ async function scanAndPrompt(): Promise<void> {
|
|
|
98
117
|
|
|
99
118
|
if (newAgents.length === 0) {
|
|
100
119
|
success("No new agents found");
|
|
120
|
+
await listAgents();
|
|
101
121
|
return;
|
|
102
122
|
}
|
|
103
123
|
|
|
@@ -119,13 +139,16 @@ async function scanAndPrompt(): Promise<void> {
|
|
|
119
139
|
console.error("");
|
|
120
140
|
success("New agents enabled");
|
|
121
141
|
info("Future projects will include context files for these agents");
|
|
142
|
+
await listAgents();
|
|
122
143
|
}
|
|
123
144
|
|
|
124
145
|
function getFlagValues(args: string[], flag: string): string[] {
|
|
125
146
|
const values: string[] = [];
|
|
126
147
|
for (let i = 0; i < args.length; i++) {
|
|
127
|
-
|
|
128
|
-
|
|
148
|
+
const current = args[i];
|
|
149
|
+
const next = args[i + 1];
|
|
150
|
+
if (current === flag && next) {
|
|
151
|
+
values.push(next);
|
|
129
152
|
i++;
|
|
130
153
|
}
|
|
131
154
|
}
|
|
@@ -174,7 +197,7 @@ async function addAgentCommand(args: string[]): Promise<void> {
|
|
|
174
197
|
type: "cli" as const,
|
|
175
198
|
command: customCommand,
|
|
176
199
|
args: customArgs.length ? customArgs : undefined,
|
|
177
|
-
|
|
200
|
+
}
|
|
178
201
|
: undefined;
|
|
179
202
|
|
|
180
203
|
await addAgent(agentId, { launch: launchOverride });
|
|
@@ -190,9 +213,7 @@ async function addAgentCommand(args: string[]): Promise<void> {
|
|
|
190
213
|
if (err instanceof Error) {
|
|
191
214
|
error(err.message);
|
|
192
215
|
if (err.message.includes("Could not detect")) {
|
|
193
|
-
info(
|
|
194
|
-
`Specify launch manually: jack agents add ${agentId} --command /path/to/command`,
|
|
195
|
-
);
|
|
216
|
+
info(`Specify launch manually: jack agents add ${agentId} --command /path/to/command`);
|
|
196
217
|
}
|
|
197
218
|
}
|
|
198
219
|
process.exit(1);
|
|
@@ -303,3 +324,117 @@ async function preferAgentCommand(args: string[]): Promise<void> {
|
|
|
303
324
|
process.exit(1);
|
|
304
325
|
}
|
|
305
326
|
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Refresh agent context files from template
|
|
330
|
+
*/
|
|
331
|
+
async function refreshAgentFilesCommand(options: AgentsOptions = {}): Promise<void> {
|
|
332
|
+
const projectDir = process.cwd();
|
|
333
|
+
let projectName: string;
|
|
334
|
+
let project = null;
|
|
335
|
+
|
|
336
|
+
if (options.project) {
|
|
337
|
+
// When --project is specified, we still need to run from that project's directory
|
|
338
|
+
// since localPath is no longer stored in the registry
|
|
339
|
+
projectName = options.project;
|
|
340
|
+
project = await getProject(projectName);
|
|
341
|
+
|
|
342
|
+
if (!project) {
|
|
343
|
+
error(`Project "${projectName}" not found in registry`);
|
|
344
|
+
info("List projects with: jack projects list");
|
|
345
|
+
process.exit(1);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Verify the current directory matches the project
|
|
349
|
+
try {
|
|
350
|
+
const cwdProjectName = await getProjectNameFromDir(projectDir);
|
|
351
|
+
if (cwdProjectName !== projectName) {
|
|
352
|
+
error(`Current directory is not the project "${projectName}"`);
|
|
353
|
+
info(`Run this command from the ${projectName} project directory`);
|
|
354
|
+
process.exit(1);
|
|
355
|
+
}
|
|
356
|
+
} catch {
|
|
357
|
+
error(`Current directory is not a valid project`);
|
|
358
|
+
info(`Run this command from the ${projectName} project directory`);
|
|
359
|
+
process.exit(1);
|
|
360
|
+
}
|
|
361
|
+
} else {
|
|
362
|
+
// 1. Detect project name from wrangler config
|
|
363
|
+
outputSpinner.start("Detecting project...");
|
|
364
|
+
try {
|
|
365
|
+
projectName = await getProjectNameFromDir(projectDir);
|
|
366
|
+
} catch {
|
|
367
|
+
outputSpinner.stop();
|
|
368
|
+
error("Could not determine project");
|
|
369
|
+
info("Run this command from a project directory, or use --project <name>");
|
|
370
|
+
process.exit(1);
|
|
371
|
+
}
|
|
372
|
+
outputSpinner.stop();
|
|
373
|
+
|
|
374
|
+
// 2. Get project from registry to find template origin
|
|
375
|
+
project = await getProject(projectName);
|
|
376
|
+
if (!project) {
|
|
377
|
+
error(`Project "${projectName}" not found in registry`);
|
|
378
|
+
info("List projects with: jack projects list");
|
|
379
|
+
process.exit(1);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (!project?.template) {
|
|
384
|
+
error("No template lineage found for this project");
|
|
385
|
+
info("This project was created before lineage tracking was added.");
|
|
386
|
+
info("Re-create the project with `jack new` to enable refresh.");
|
|
387
|
+
process.exit(1);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// 3. Resolve template (fetch if GitHub, load if builtin)
|
|
391
|
+
outputSpinner.start("Loading template...");
|
|
392
|
+
let template: Template;
|
|
393
|
+
try {
|
|
394
|
+
template = await resolveTemplate(project.template.name);
|
|
395
|
+
} catch (err) {
|
|
396
|
+
outputSpinner.stop();
|
|
397
|
+
error(`Failed to load template: ${project.template.name}`);
|
|
398
|
+
if (err instanceof Error) {
|
|
399
|
+
info(err.message);
|
|
400
|
+
}
|
|
401
|
+
process.exit(1);
|
|
402
|
+
}
|
|
403
|
+
outputSpinner.stop();
|
|
404
|
+
|
|
405
|
+
// 4. Backup existing files
|
|
406
|
+
const agentsMdPath = join(projectDir, "AGENTS.md");
|
|
407
|
+
if (existsSync(agentsMdPath)) {
|
|
408
|
+
const backupPath = `${agentsMdPath}.backup`;
|
|
409
|
+
const content = await Bun.file(agentsMdPath).text();
|
|
410
|
+
await Bun.write(backupPath, content);
|
|
411
|
+
success("Backed up AGENTS.md → AGENTS.md.backup");
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const claudeMdPath = join(projectDir, "CLAUDE.md");
|
|
415
|
+
if (existsSync(claudeMdPath)) {
|
|
416
|
+
const backupPath = `${claudeMdPath}.backup`;
|
|
417
|
+
const content = await Bun.file(claudeMdPath).text();
|
|
418
|
+
await Bun.write(backupPath, content);
|
|
419
|
+
success("Backed up CLAUDE.md → CLAUDE.md.backup");
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// 5. Get active agents
|
|
423
|
+
const activeAgents = await getActiveAgents();
|
|
424
|
+
if (activeAgents.length === 0) {
|
|
425
|
+
warn("No active agents configured");
|
|
426
|
+
info("Run: jack agents scan");
|
|
427
|
+
process.exit(1);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// 6. Regenerate agent files
|
|
431
|
+
const updatedFiles = await regenerateAgentFiles(projectDir, projectName, template, activeAgents);
|
|
432
|
+
|
|
433
|
+
console.error("");
|
|
434
|
+
success("Refreshed agent context files:");
|
|
435
|
+
for (const file of updatedFiles) {
|
|
436
|
+
item(file);
|
|
437
|
+
}
|
|
438
|
+
console.error("");
|
|
439
|
+
info("Review changes: git diff AGENTS.md");
|
|
440
|
+
}
|
package/src/commands/down.ts
CHANGED
|
@@ -1,63 +1,55 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
1
|
import { join } from "node:path";
|
|
3
|
-
import { select } from "@inquirer/prompts";
|
|
4
2
|
import {
|
|
5
3
|
checkWorkerExists,
|
|
6
4
|
deleteDatabase,
|
|
7
5
|
deleteWorker,
|
|
8
6
|
exportDatabase,
|
|
9
7
|
} from "../lib/cloudflare-api.ts";
|
|
8
|
+
import { fetchProjectResources } from "../lib/control-plane.ts";
|
|
9
|
+
import { promptSelect } from "../lib/hooks.ts";
|
|
10
|
+
import { managedDown } from "../lib/managed-down.ts";
|
|
10
11
|
import { error, info, item, output, success, warn } from "../lib/output.ts";
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
getProjectDatabaseName,
|
|
15
|
-
updateProject,
|
|
16
|
-
updateProjectDatabase,
|
|
17
|
-
} from "../lib/registry.ts";
|
|
12
|
+
import { resolveProject } from "../lib/project-resolver.ts";
|
|
13
|
+
import { type Project, getProject, updateProject } from "../lib/registry.ts";
|
|
14
|
+
import { parseWranglerResources } from "../lib/resources.ts";
|
|
18
15
|
import { deleteCloudProject, getProjectNameFromDir } from "../lib/storage/index.ts";
|
|
19
16
|
|
|
20
17
|
/**
|
|
21
|
-
*
|
|
18
|
+
* Resolve database name for a project.
|
|
19
|
+
* For managed projects: fetch from control plane.
|
|
20
|
+
* For BYO projects: parse from wrangler.jsonc in cwd.
|
|
22
21
|
*/
|
|
23
|
-
async function
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
async function resolveDatabaseName(project: Project, projectName: string): Promise<string | null> {
|
|
23
|
+
// For managed projects, fetch from control plane
|
|
24
|
+
if (project.deploy_mode === "managed" && project.remote?.project_id) {
|
|
25
|
+
try {
|
|
26
|
+
const resources = await fetchProjectResources(project.remote.project_id);
|
|
27
|
+
const d1 = resources.find((r) => r.resource_type === "d1");
|
|
28
|
+
return d1?.resource_name || null;
|
|
29
|
+
} catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
28
32
|
}
|
|
29
33
|
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const config = JSON.parse(jsonContent);
|
|
38
|
-
if (config.d1_databases?.[0]?.database_name) {
|
|
39
|
-
return config.d1_databases[0].database_name;
|
|
40
|
-
}
|
|
41
|
-
} catch {
|
|
42
|
-
// Ignore parse errors
|
|
43
|
-
}
|
|
34
|
+
// For BYO, parse from wrangler config in cwd
|
|
35
|
+
try {
|
|
36
|
+
let cwdProjectName: string | null = null;
|
|
37
|
+
try {
|
|
38
|
+
cwdProjectName = await getProjectNameFromDir(process.cwd());
|
|
39
|
+
} catch {
|
|
40
|
+
cwdProjectName = null;
|
|
44
41
|
}
|
|
45
42
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const content = await Bun.file(tomlPath).text();
|
|
50
|
-
const match = content.match(/database_name\s*=\s*"([^"]+)"/);
|
|
51
|
-
if (match?.[1]) {
|
|
52
|
-
return match[1];
|
|
53
|
-
}
|
|
54
|
-
} catch {
|
|
55
|
-
// Ignore read errors
|
|
56
|
-
}
|
|
43
|
+
if (!cwdProjectName || cwdProjectName !== projectName) {
|
|
44
|
+
warn(`Run this command from the ${projectName} project directory to manage its database.`);
|
|
45
|
+
return null;
|
|
57
46
|
}
|
|
58
|
-
}
|
|
59
47
|
|
|
60
|
-
|
|
48
|
+
const resources = await parseWranglerResources(process.cwd());
|
|
49
|
+
return resources.d1?.name || null;
|
|
50
|
+
} catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
61
53
|
}
|
|
62
54
|
|
|
63
55
|
export interface DownFlags {
|
|
@@ -78,13 +70,58 @@ export default async function down(projectName?: string, flags: DownFlags = {}):
|
|
|
78
70
|
}
|
|
79
71
|
}
|
|
80
72
|
|
|
81
|
-
//
|
|
73
|
+
// Resolve project from all sources (registry + control plane)
|
|
74
|
+
const resolved = await resolveProject(name);
|
|
75
|
+
|
|
76
|
+
// Check if found only on control plane (orphaned managed project)
|
|
77
|
+
if (resolved?.sources.controlPlane && !resolved.sources.registry) {
|
|
78
|
+
console.error("");
|
|
79
|
+
info(`Found "${name}" on jack cloud, linking locally...`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Get the registry project (may have been created by resolver cache)
|
|
82
83
|
const project = await getProject(name);
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
|
|
85
|
+
if (!resolved && !project) {
|
|
86
|
+
// Not found anywhere
|
|
87
|
+
warn(`Project '${name}' not found`);
|
|
85
88
|
info("Will attempt to undeploy if deployed");
|
|
86
89
|
}
|
|
87
90
|
|
|
91
|
+
// Check if this is a managed project (either from resolved or registry)
|
|
92
|
+
const isManaged =
|
|
93
|
+
resolved?.remote?.projectId ||
|
|
94
|
+
(project?.deploy_mode === "managed" && project.remote?.project_id);
|
|
95
|
+
|
|
96
|
+
if (isManaged) {
|
|
97
|
+
// Build project object for managedDown if we only have resolved data
|
|
98
|
+
const managedProject: Project = project || {
|
|
99
|
+
workerUrl: resolved?.url || null,
|
|
100
|
+
createdAt: resolved?.createdAt || new Date().toISOString(),
|
|
101
|
+
lastDeployed: resolved?.updatedAt || null,
|
|
102
|
+
status: resolved?.status === "live" ? "live" : "build_failed",
|
|
103
|
+
deploy_mode: "managed",
|
|
104
|
+
remote:
|
|
105
|
+
resolved?.remote && resolved.url
|
|
106
|
+
? {
|
|
107
|
+
project_id: resolved.remote.projectId,
|
|
108
|
+
project_slug: resolved.slug,
|
|
109
|
+
org_id: resolved.remote.orgId,
|
|
110
|
+
runjack_url: resolved.url,
|
|
111
|
+
}
|
|
112
|
+
: undefined,
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// Route to managed deletion flow
|
|
116
|
+
const deleteSuccess = await managedDown(managedProject, name, flags);
|
|
117
|
+
if (!deleteSuccess) {
|
|
118
|
+
process.exit(0); // User cancelled
|
|
119
|
+
}
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Continue with existing BYO flow...
|
|
124
|
+
|
|
88
125
|
// Check if worker exists
|
|
89
126
|
output.start("Checking deployment...");
|
|
90
127
|
const workerExists = await checkWorkerExists(name);
|
|
@@ -117,7 +154,7 @@ export default async function down(projectName?: string, flags: DownFlags = {}):
|
|
|
117
154
|
|
|
118
155
|
console.error("");
|
|
119
156
|
success(`'${name}' undeployed`);
|
|
120
|
-
info("Databases and
|
|
157
|
+
info("Databases and backups were not affected");
|
|
121
158
|
console.error("");
|
|
122
159
|
return;
|
|
123
160
|
}
|
|
@@ -128,23 +165,18 @@ export default async function down(projectName?: string, flags: DownFlags = {}):
|
|
|
128
165
|
if (project?.workerUrl) {
|
|
129
166
|
item(`URL: ${project.workerUrl}`);
|
|
130
167
|
}
|
|
131
|
-
const dbName = project ? await
|
|
168
|
+
const dbName = project ? await resolveDatabaseName(project, name) : null;
|
|
132
169
|
if (dbName) {
|
|
133
170
|
item(`Database: ${dbName}`);
|
|
134
171
|
}
|
|
135
172
|
console.error("");
|
|
136
173
|
|
|
137
174
|
// Confirm undeploy
|
|
138
|
-
console.error("
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
{ name: "2. No", value: "no" },
|
|
144
|
-
],
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
if (action === "no") {
|
|
175
|
+
console.error("");
|
|
176
|
+
info("Undeploy this project?");
|
|
177
|
+
const action = await promptSelect(["Yes", "No"]);
|
|
178
|
+
|
|
179
|
+
if (action !== 0) {
|
|
148
180
|
info("Cancelled");
|
|
149
181
|
return;
|
|
150
182
|
}
|
|
@@ -157,16 +189,11 @@ export default async function down(projectName?: string, flags: DownFlags = {}):
|
|
|
157
189
|
info(`Found database: ${dbName}`);
|
|
158
190
|
|
|
159
191
|
// Ask if they want to export first
|
|
160
|
-
console.error("
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
choices: [
|
|
164
|
-
{ name: "1. Yes", value: "yes" },
|
|
165
|
-
{ name: "2. No", value: "no" },
|
|
166
|
-
],
|
|
167
|
-
});
|
|
192
|
+
console.error("");
|
|
193
|
+
info(`Export database '${dbName}' before deleting?`);
|
|
194
|
+
const exportAction = await promptSelect(["Yes", "No"]);
|
|
168
195
|
|
|
169
|
-
if (exportAction ===
|
|
196
|
+
if (exportAction === 0) {
|
|
170
197
|
const exportPath = join(process.cwd(), `${dbName}-backup.sql`);
|
|
171
198
|
output.start(`Exporting database to ${exportPath}...`);
|
|
172
199
|
try {
|
|
@@ -176,15 +203,10 @@ export default async function down(projectName?: string, flags: DownFlags = {}):
|
|
|
176
203
|
} catch (err) {
|
|
177
204
|
output.stop();
|
|
178
205
|
error(`Failed to export database: ${err instanceof Error ? err.message : String(err)}`);
|
|
179
|
-
console.error("
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
{ name: "1. Yes", value: "yes" },
|
|
184
|
-
{ name: "2. No", value: "no" },
|
|
185
|
-
],
|
|
186
|
-
});
|
|
187
|
-
if (continueAction === "no") {
|
|
206
|
+
console.error("");
|
|
207
|
+
info("Continue without exporting?");
|
|
208
|
+
const continueAction = await promptSelect(["Yes", "No"]);
|
|
209
|
+
if (continueAction !== 0) {
|
|
188
210
|
info("Cancelled");
|
|
189
211
|
return;
|
|
190
212
|
}
|
|
@@ -192,31 +214,20 @@ export default async function down(projectName?: string, flags: DownFlags = {}):
|
|
|
192
214
|
}
|
|
193
215
|
|
|
194
216
|
// Ask if they want to delete the database
|
|
195
|
-
console.error("
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
choices: [
|
|
199
|
-
{ name: "1. Yes", value: "yes" },
|
|
200
|
-
{ name: "2. No", value: "no" },
|
|
201
|
-
],
|
|
202
|
-
});
|
|
217
|
+
console.error("");
|
|
218
|
+
info(`Delete database '${dbName}'?`);
|
|
219
|
+
const deleteAction = await promptSelect(["Yes", "No"]);
|
|
203
220
|
|
|
204
|
-
shouldDeleteDb = deleteAction ===
|
|
221
|
+
shouldDeleteDb = deleteAction === 0;
|
|
205
222
|
}
|
|
206
223
|
|
|
207
|
-
// Handle
|
|
224
|
+
// Handle backup deletion
|
|
208
225
|
let shouldDeleteR2 = false;
|
|
209
226
|
if (project) {
|
|
210
227
|
console.error("");
|
|
211
|
-
|
|
212
|
-
const deleteR2Action = await
|
|
213
|
-
|
|
214
|
-
choices: [
|
|
215
|
-
{ name: "1. Yes", value: "yes" },
|
|
216
|
-
{ name: "2. No", value: "no" },
|
|
217
|
-
],
|
|
218
|
-
});
|
|
219
|
-
shouldDeleteR2 = deleteR2Action === "yes";
|
|
228
|
+
info("Delete backup for this project?");
|
|
229
|
+
const deleteR2Action = await promptSelect(["Yes", "No"]);
|
|
230
|
+
shouldDeleteR2 = deleteR2Action === 0;
|
|
220
231
|
}
|
|
221
232
|
|
|
222
233
|
// Execute deletions
|
|
@@ -243,9 +254,6 @@ export default async function down(projectName?: string, flags: DownFlags = {}):
|
|
|
243
254
|
await deleteDatabase(dbName);
|
|
244
255
|
output.stop();
|
|
245
256
|
success(`Database '${dbName}' deleted`);
|
|
246
|
-
|
|
247
|
-
// Update registry
|
|
248
|
-
await updateProjectDatabase(name, null);
|
|
249
257
|
} catch (err) {
|
|
250
258
|
output.stop();
|
|
251
259
|
warn(
|
|
@@ -254,20 +262,20 @@ export default async function down(projectName?: string, flags: DownFlags = {}):
|
|
|
254
262
|
}
|
|
255
263
|
}
|
|
256
264
|
|
|
257
|
-
// Delete
|
|
265
|
+
// Delete backup if requested
|
|
258
266
|
if (shouldDeleteR2) {
|
|
259
|
-
output.start("Deleting
|
|
267
|
+
output.start("Deleting backup...");
|
|
260
268
|
try {
|
|
261
269
|
const deleted = await deleteCloudProject(name);
|
|
262
270
|
output.stop();
|
|
263
271
|
if (deleted) {
|
|
264
|
-
success("
|
|
272
|
+
success("Backup deleted");
|
|
265
273
|
} else {
|
|
266
|
-
warn("No
|
|
274
|
+
warn("No backup found or already deleted");
|
|
267
275
|
}
|
|
268
276
|
} catch (err) {
|
|
269
277
|
output.stop();
|
|
270
|
-
warn(`Failed to delete
|
|
278
|
+
warn(`Failed to delete backup: ${err instanceof Error ? err.message : String(err)}`);
|
|
271
279
|
}
|
|
272
280
|
}
|
|
273
281
|
|