@getjack/jack 0.1.28 → 0.1.30
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 +1 -1
- package/src/commands/cd.ts +163 -0
- package/src/commands/clone.ts +112 -68
- package/src/commands/domain.ts +506 -0
- package/src/commands/domains.ts +215 -0
- package/src/commands/down.ts +18 -12
- package/src/commands/hack.ts +185 -8
- package/src/commands/init.ts +52 -1
- package/src/commands/link.ts +25 -43
- package/src/commands/logs.ts +2 -2
- package/src/commands/mcp.ts +74 -3
- package/src/commands/new.ts +48 -54
- package/src/commands/projects.ts +53 -10
- package/src/commands/secrets.ts +5 -1
- package/src/commands/services.ts +16 -4
- package/src/commands/shell-init.ts +43 -0
- package/src/commands/ship.ts +2 -11
- package/src/commands/skills.ts +335 -0
- package/src/commands/update.ts +31 -0
- package/src/commands/upgrade.ts +14 -0
- package/src/index.ts +116 -24
- package/src/lib/agent-integration.ts +1 -2
- package/src/lib/agents.ts +2 -2
- package/src/lib/auth/login-flow.ts +1 -1
- package/src/lib/clone-core.ts +252 -0
- package/src/lib/config.ts +22 -0
- package/src/lib/control-plane.ts +31 -5
- package/src/lib/fuzzy.ts +93 -0
- package/src/lib/managed-deploy.ts +4 -1
- package/src/lib/managed-down.ts +20 -5
- package/src/lib/output.ts +90 -9
- package/src/lib/picker.ts +406 -0
- package/src/lib/project-detection.ts +5 -2
- package/src/lib/project-list.ts +66 -5
- package/src/lib/project-operations.ts +68 -6
- package/src/lib/prompts.ts +1 -1
- package/src/lib/services/db-execute.ts +8 -1
- package/src/lib/services/db-list.ts +4 -1
- package/src/lib/services/domain-operations.ts +379 -0
- package/src/lib/services/storage-config.ts +1 -5
- package/src/lib/services/storage-delete.ts +1 -1
- package/src/lib/services/storage-info.ts +2 -4
- package/src/lib/services/vectorize-config.ts +1 -5
- package/src/lib/services/vectorize-create.ts +3 -1
- package/src/lib/shell-integration.ts +202 -0
- package/src/lib/telemetry-config.ts +50 -4
- package/src/lib/telemetry.ts +71 -2
- package/src/lib/version-check.ts +1 -3
- package/src/lib/wrangler-config.test.ts +2 -2
- package/src/lib/wrangler-config.ts +1 -1
- package/src/lib/zip-packager.ts +1 -3
- package/src/mcp/tools/index.ts +261 -7
- package/src/templates/index.ts +10 -1
- package/templates/ai-chat/.jack.json +1 -5
- package/templates/ai-chat/public/chat.js +130 -130
- package/templates/ai-chat/src/index.ts +9 -13
- package/templates/ai-chat/src/jack-ai.ts +6 -2
- package/templates/saas/.jack.json +6 -1
- package/templates/saas/src/auth.ts +8 -4
- package/templates/saas/src/client/App.tsx +22 -7
- package/templates/saas/src/client/components/ProtectedRoute.tsx +9 -2
- package/templates/saas/src/client/components/ThemeToggle.tsx +1 -6
- package/templates/saas/src/client/components/ui/accordion.tsx +1 -1
- package/templates/saas/src/client/components/ui/alert-dialog.tsx +2 -2
- package/templates/saas/src/client/components/ui/alert.tsx +2 -2
- package/templates/saas/src/client/components/ui/avatar.tsx +1 -1
- package/templates/saas/src/client/components/ui/badge.tsx +2 -2
- package/templates/saas/src/client/components/ui/breadcrumb.tsx +1 -1
- package/templates/saas/src/client/components/ui/button-group.tsx +2 -2
- package/templates/saas/src/client/components/ui/button.tsx +2 -2
- package/templates/saas/src/client/components/ui/card.tsx +1 -1
- package/templates/saas/src/client/components/ui/carousel.tsx +2 -2
- package/templates/saas/src/client/components/ui/checkbox.tsx +1 -1
- package/templates/saas/src/client/components/ui/command.tsx +2 -2
- package/templates/saas/src/client/components/ui/context-menu.tsx +1 -1
- package/templates/saas/src/client/components/ui/dialog.tsx +1 -1
- package/templates/saas/src/client/components/ui/drawer.tsx +1 -1
- package/templates/saas/src/client/components/ui/dropdown-menu.tsx +1 -1
- package/templates/saas/src/client/components/ui/empty.tsx +1 -1
- package/templates/saas/src/client/components/ui/field.tsx +2 -2
- package/templates/saas/src/client/components/ui/form.tsx +5 -5
- package/templates/saas/src/client/components/ui/hover-card.tsx +1 -1
- package/templates/saas/src/client/components/ui/input-group.tsx +3 -3
- package/templates/saas/src/client/components/ui/input-otp.tsx +1 -1
- package/templates/saas/src/client/components/ui/input.tsx +1 -1
- package/templates/saas/src/client/components/ui/item.tsx +3 -3
- package/templates/saas/src/client/components/ui/label.tsx +1 -1
- package/templates/saas/src/client/components/ui/menubar.tsx +1 -1
- package/templates/saas/src/client/components/ui/navigation-menu.tsx +1 -1
- package/templates/saas/src/client/components/ui/pagination.tsx +2 -2
- package/templates/saas/src/client/components/ui/popover.tsx +1 -1
- package/templates/saas/src/client/components/ui/progress.tsx +1 -1
- package/templates/saas/src/client/components/ui/radio-group.tsx +1 -1
- package/templates/saas/src/client/components/ui/resizable.tsx +1 -1
- package/templates/saas/src/client/components/ui/scroll-area.tsx +1 -1
- package/templates/saas/src/client/components/ui/select.tsx +1 -1
- package/templates/saas/src/client/components/ui/separator.tsx +1 -1
- package/templates/saas/src/client/components/ui/sheet.tsx +1 -1
- package/templates/saas/src/client/components/ui/sidebar.tsx +4 -4
- package/templates/saas/src/client/components/ui/slider.tsx +1 -1
- package/templates/saas/src/client/components/ui/switch.tsx +1 -1
- package/templates/saas/src/client/components/ui/table.tsx +1 -1
- package/templates/saas/src/client/components/ui/tabs.tsx +1 -1
- package/templates/saas/src/client/components/ui/textarea.tsx +1 -1
- package/templates/saas/src/client/components/ui/toggle-group.tsx +3 -3
- package/templates/saas/src/client/components/ui/toggle.tsx +2 -2
- package/templates/saas/src/client/components/ui/tooltip.tsx +1 -1
- package/templates/saas/src/client/hooks/useSubscription.ts +5 -4
- package/templates/saas/src/client/lib/auth-client.ts +1 -1
- package/templates/saas/src/client/lib/plans.ts +1 -6
- package/templates/saas/src/client/lib/utils.ts +1 -1
- package/templates/saas/src/client/main.tsx +1 -1
- package/templates/saas/src/client/pages/DashboardPage.tsx +41 -9
- package/templates/saas/src/client/pages/ForgotPasswordPage.tsx +11 -2
- package/templates/saas/src/client/pages/HomePage.tsx +11 -2
- package/templates/saas/src/client/pages/LoginPage.tsx +11 -2
- package/templates/saas/src/client/pages/PricingPage.tsx +20 -10
- package/templates/saas/src/client/pages/ResetPasswordPage.tsx +14 -11
- package/templates/saas/src/client/pages/SignupPage.tsx +11 -2
- package/templates/saas/src/index.ts +28 -19
- package/templates/saas/vite.config.ts +1 -1
- package/templates/semantic-search/.jack.json +1 -5
- package/templates/semantic-search/src/index.ts +8 -4
- package/templates/semantic-search/src/jack-ai.ts +6 -2
- package/templates/semantic-search/src/jack-vectorize.ts +5 -1
package/src/commands/down.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { mkdirSync } from "node:fs";
|
|
1
2
|
import { join } from "node:path";
|
|
2
3
|
import { getAuthState } from "../lib/auth/index.ts";
|
|
3
4
|
import {
|
|
@@ -6,6 +7,7 @@ import {
|
|
|
6
7
|
deleteWorker,
|
|
7
8
|
exportDatabase,
|
|
8
9
|
} from "../lib/cloudflare-api.ts";
|
|
10
|
+
import { getJackHome } from "../lib/config.ts";
|
|
9
11
|
import { fetchProjectResources } from "../lib/control-plane.ts";
|
|
10
12
|
import { promptSelect } from "../lib/hooks.ts";
|
|
11
13
|
import { managedDown } from "../lib/managed-down.ts";
|
|
@@ -88,7 +90,7 @@ export default async function down(projectName?: string, flags: DownFlags = {}):
|
|
|
88
90
|
// Check if found only on control plane (orphaned managed project)
|
|
89
91
|
if (resolved?.sources.controlPlane && !resolved.sources.filesystem) {
|
|
90
92
|
console.error("");
|
|
91
|
-
info(`Found "${name}" on jack cloud
|
|
93
|
+
info(`Found "${name}" on jack cloud`);
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
// Guard against mismatched resolutions when an explicit name is provided
|
|
@@ -115,6 +117,7 @@ export default async function down(projectName?: string, flags: DownFlags = {}):
|
|
|
115
117
|
// Get the project ID from link or resolved data
|
|
116
118
|
const projectId = link?.project_id || resolved?.remote?.projectId;
|
|
117
119
|
const runjackUrl = resolved?.url || null;
|
|
120
|
+
const localPath = resolved?.localPath || null;
|
|
118
121
|
|
|
119
122
|
if (!projectId) {
|
|
120
123
|
error("Cannot determine project ID for managed deletion");
|
|
@@ -122,18 +125,19 @@ export default async function down(projectName?: string, flags: DownFlags = {}):
|
|
|
122
125
|
}
|
|
123
126
|
|
|
124
127
|
// Route to managed deletion flow
|
|
125
|
-
const deleteSuccess = await managedDown({ projectId, runjackUrl }, name, flags);
|
|
128
|
+
const deleteSuccess = await managedDown({ projectId, runjackUrl, localPath }, name, flags);
|
|
126
129
|
if (!deleteSuccess) {
|
|
127
130
|
process.exit(0); // User cancelled
|
|
128
131
|
}
|
|
129
132
|
|
|
130
|
-
// Clean up local tracking state
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
133
|
+
// Clean up local tracking state (only if project has local path)
|
|
134
|
+
if (resolved?.localPath) {
|
|
135
|
+
try {
|
|
136
|
+
await unlinkProject(resolved.localPath);
|
|
137
|
+
await unregisterPath(projectId, resolved.localPath);
|
|
138
|
+
} catch {
|
|
139
|
+
// Non-fatal: local cleanup failed but cloud deletion succeeded
|
|
140
|
+
}
|
|
137
141
|
}
|
|
138
142
|
return;
|
|
139
143
|
}
|
|
@@ -203,8 +207,10 @@ export default async function down(projectName?: string, flags: DownFlags = {}):
|
|
|
203
207
|
// Auto-export database if it exists (no prompt)
|
|
204
208
|
let exportPath: string | null = null;
|
|
205
209
|
if (dbName) {
|
|
206
|
-
|
|
207
|
-
|
|
210
|
+
const backupDir = resolved?.localPath ?? join(getJackHome(), name);
|
|
211
|
+
mkdirSync(backupDir, { recursive: true });
|
|
212
|
+
exportPath = join(backupDir, `${dbName}-backup.sql`);
|
|
213
|
+
output.start("Exporting database...");
|
|
208
214
|
try {
|
|
209
215
|
await exportDatabase(dbName, exportPath);
|
|
210
216
|
output.stop();
|
|
@@ -267,7 +273,7 @@ export default async function down(projectName?: string, flags: DownFlags = {}):
|
|
|
267
273
|
console.error("");
|
|
268
274
|
success(`Undeployed '${name}'`);
|
|
269
275
|
if (exportPath) {
|
|
270
|
-
info(`
|
|
276
|
+
info(`Database backup: ${exportPath}`);
|
|
271
277
|
}
|
|
272
278
|
console.error("");
|
|
273
279
|
} catch (err) {
|
package/src/commands/hack.ts
CHANGED
|
@@ -1,18 +1,195 @@
|
|
|
1
|
+
const reset = "\x1b[0m";
|
|
2
|
+
const bright = "\x1b[1m";
|
|
3
|
+
const dim = "\x1b[2m";
|
|
4
|
+
const cyan = "\x1b[38;2;0;255;255m";
|
|
5
|
+
const dimCyan = "\x1b[38;2;0;180;180m";
|
|
6
|
+
const green = "\x1b[38;2;0;200;0m";
|
|
7
|
+
|
|
8
|
+
const matrixChars = "░▒▓█▀▄■□◆◇●◐◑◒◓ヲアウエオカキクケコ";
|
|
9
|
+
|
|
10
|
+
function randomChar(): string {
|
|
11
|
+
return matrixChars[Math.floor(Math.random() * matrixChars.length)];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function sleep(ms: number) {
|
|
15
|
+
await new Promise((r) => setTimeout(r, ms));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async function type(text: string, delay = 30) {
|
|
19
|
+
for (const char of text) {
|
|
20
|
+
process.stdout.write(char);
|
|
21
|
+
await sleep(delay);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function typeLine(text: string, delay = 30) {
|
|
26
|
+
await type(text, delay);
|
|
27
|
+
console.log();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function matrixDecode(text: string, indent = "") {
|
|
31
|
+
const frames = 30;
|
|
32
|
+
const delay = 50;
|
|
33
|
+
const len = text.length;
|
|
34
|
+
|
|
35
|
+
for (let frame = 0; frame <= frames; frame++) {
|
|
36
|
+
const resolvedCount = Math.floor((frame / frames) * len);
|
|
37
|
+
let line = indent;
|
|
38
|
+
|
|
39
|
+
for (let i = 0; i < len; i++) {
|
|
40
|
+
if (i < resolvedCount) {
|
|
41
|
+
line += `${bright}${cyan}${text[i]}${reset}`;
|
|
42
|
+
} else {
|
|
43
|
+
line += `${dimCyan}${randomChar()}${reset}`;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
process.stdout.write(`\x1b[2K\r${line}`);
|
|
48
|
+
await sleep(delay);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
process.stdout.write(`\x1b[2K\r${indent}${bright}${cyan}${text}${reset}\n`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function bootSequence() {
|
|
55
|
+
const modulePool = [
|
|
56
|
+
"consciousness.ko",
|
|
57
|
+
"cyberspace.ko",
|
|
58
|
+
"deploy.ko",
|
|
59
|
+
"neural.ko",
|
|
60
|
+
"ice-breaker.ko",
|
|
61
|
+
"daemon.ko",
|
|
62
|
+
"matrix.ko",
|
|
63
|
+
"decrypt.ko",
|
|
64
|
+
"intrusion.ko",
|
|
65
|
+
"phantom.ko",
|
|
66
|
+
];
|
|
67
|
+
const modules = pickRandom(modulePool, 3);
|
|
68
|
+
|
|
69
|
+
console.log();
|
|
70
|
+
console.log(`${dim}JACK OS v1.0${reset}`);
|
|
71
|
+
console.log(`${dim}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${reset}`);
|
|
72
|
+
await sleep(300);
|
|
73
|
+
|
|
74
|
+
await typeLine(`${green}Initializing...${reset}`, 20);
|
|
75
|
+
for (const mod of modules) {
|
|
76
|
+
await type(` ${dim}├─${reset} ${mod}`, 15);
|
|
77
|
+
await sleep(100 + Math.random() * 200);
|
|
78
|
+
console.log(` ${green}[OK]${reset}`);
|
|
79
|
+
}
|
|
80
|
+
await sleep(200);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function pickRandom<T>(arr: T[], count: number): T[] {
|
|
84
|
+
const shuffled = [...arr].sort(() => Math.random() - 0.5);
|
|
85
|
+
return shuffled.slice(0, count);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function traceRoute() {
|
|
89
|
+
const username = process.env.USER || process.env.USERNAME || "user";
|
|
90
|
+
|
|
91
|
+
const serverPool = [
|
|
92
|
+
"node.chiba.city",
|
|
93
|
+
"relay.freeside.orbital",
|
|
94
|
+
"proxy.night.city",
|
|
95
|
+
"vault.zaibatsu.corp",
|
|
96
|
+
"node.sprawl.net",
|
|
97
|
+
"relay.tessier-ashpool.ice",
|
|
98
|
+
"cache.construct.sim",
|
|
99
|
+
"gate.zion.cluster",
|
|
100
|
+
"hub.screaming.fist",
|
|
101
|
+
"core.wintermute.ai",
|
|
102
|
+
"edge.straylight.run",
|
|
103
|
+
"sync.maelstrom.net",
|
|
104
|
+
];
|
|
105
|
+
|
|
106
|
+
const midHops = pickRandom(serverPool, 3);
|
|
107
|
+
const hops = [
|
|
108
|
+
["localhost", "0.1"],
|
|
109
|
+
[`${username}.meat.space`, String(10 + Math.floor(Math.random() * 20))],
|
|
110
|
+
[midHops[0], String(50 + Math.floor(Math.random() * 50))],
|
|
111
|
+
[midHops[1], String(100 + Math.floor(Math.random() * 100))],
|
|
112
|
+
[midHops[2], "███"],
|
|
113
|
+
];
|
|
114
|
+
|
|
115
|
+
console.log();
|
|
116
|
+
await typeLine(`${green}Tracing route...${reset}`, 20);
|
|
117
|
+
|
|
118
|
+
for (let i = 0; i < hops.length; i++) {
|
|
119
|
+
const [host, ms] = hops[i];
|
|
120
|
+
await type(` ${dim}${i + 1}${reset} ${host}`, 10);
|
|
121
|
+
await sleep(150 + Math.random() * 300);
|
|
122
|
+
console.log(` ${dimCyan}${ms}ms${reset}`);
|
|
123
|
+
}
|
|
124
|
+
await sleep(200);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async function sshConnect(target: string) {
|
|
128
|
+
console.log();
|
|
129
|
+
await typeLine(`${green}Connecting to ${target}:22...${reset}`, 15);
|
|
130
|
+
await sleep(300);
|
|
131
|
+
await type(`${dim}RSA fingerprint: ${reset}${dimCyan}`);
|
|
132
|
+
for (let i = 0; i < 12; i++) {
|
|
133
|
+
process.stdout.write(randomChar());
|
|
134
|
+
await sleep(30);
|
|
135
|
+
}
|
|
136
|
+
console.log(`${reset}`);
|
|
137
|
+
await sleep(400);
|
|
138
|
+
console.log(`${bright}${green}ACCESS GRANTED${reset}`);
|
|
139
|
+
await sleep(500);
|
|
140
|
+
}
|
|
141
|
+
|
|
1
142
|
export default async function hack(): Promise<void> {
|
|
2
143
|
const quotes = [
|
|
144
|
+
// Sneakers (1992)
|
|
145
|
+
"The world isn't run by weapons anymore. It's run by ones and zeroes.",
|
|
146
|
+
"No more secrets.",
|
|
3
147
|
// Gibson - Neuromancer
|
|
4
148
|
"The sky above the port was the color of television, tuned to a dead channel.",
|
|
5
149
|
"Cyberspace. A consensual hallucination.",
|
|
6
150
|
// Gibson - various
|
|
7
151
|
"The future is already here — it's just not evenly distributed.",
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
"
|
|
13
|
-
//
|
|
14
|
-
"
|
|
152
|
+
// Cyberpunk 2077 - Johnny Silverhand
|
|
153
|
+
"Wake the fuck up, Samurai. We have a city to burn.",
|
|
154
|
+
// Hackers (1995)
|
|
155
|
+
"Mess with the best, die like the rest.",
|
|
156
|
+
"Hack the planet!",
|
|
157
|
+
// Hacker wisdom
|
|
158
|
+
"Playfully doing something difficult, whether useful or not, that is hacking.",
|
|
159
|
+
"There's nothing more permanent than a temporary hack.",
|
|
160
|
+
// jack philosophy - from SPIRIT.md
|
|
161
|
+
"Context-switching to dashboards is violence.",
|
|
162
|
+
"GUIs are for browsing. CLIs are for flow.",
|
|
163
|
+
"The best infrastructure is invisible.",
|
|
164
|
+
"Every friction point is a creative thought lost.",
|
|
165
|
+
"Create and ship before your first commit.",
|
|
166
|
+
"Don't punish exploration. Creation is free.",
|
|
15
167
|
];
|
|
16
168
|
const quote = quotes[Math.floor(Math.random() * quotes.length)];
|
|
17
|
-
|
|
169
|
+
|
|
170
|
+
const serverPool = [
|
|
171
|
+
"node.chiba.city",
|
|
172
|
+
"relay.freeside.orbital",
|
|
173
|
+
"proxy.night.city",
|
|
174
|
+
"vault.zaibatsu.corp",
|
|
175
|
+
"node.sprawl.net",
|
|
176
|
+
"relay.tessier-ashpool.ice",
|
|
177
|
+
"cache.construct.sim",
|
|
178
|
+
"gate.zion.cluster",
|
|
179
|
+
"hub.screaming.fist",
|
|
180
|
+
"core.wintermute.ai",
|
|
181
|
+
"edge.straylight.run",
|
|
182
|
+
"sync.maelstrom.net",
|
|
183
|
+
];
|
|
184
|
+
const target = serverPool[Math.floor(Math.random() * serverPool.length)];
|
|
185
|
+
|
|
186
|
+
await bootSequence();
|
|
187
|
+
await traceRoute();
|
|
188
|
+
await sshConnect(target);
|
|
189
|
+
|
|
190
|
+
console.log();
|
|
191
|
+
console.log(`${dim}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${reset}`);
|
|
192
|
+
console.log();
|
|
193
|
+
await matrixDecode(`"${quote}"`);
|
|
194
|
+
console.log();
|
|
18
195
|
}
|
package/src/commands/init.ts
CHANGED
|
@@ -5,8 +5,19 @@ import {
|
|
|
5
5
|
updateAgent,
|
|
6
6
|
} from "../lib/agents.ts";
|
|
7
7
|
import { readConfig, writeConfig } from "../lib/config.ts";
|
|
8
|
+
import { promptSelect } from "../lib/hooks.ts";
|
|
8
9
|
import { getAppDisplayName, installMcpConfigsToAllApps, saveMcpConfig } from "../lib/mcp-config.ts";
|
|
9
10
|
import { info, item, spinner, success } from "../lib/output.ts";
|
|
11
|
+
import {
|
|
12
|
+
detectShell,
|
|
13
|
+
getRcFileName,
|
|
14
|
+
getRcFilePath,
|
|
15
|
+
getShellFileDisplayPath,
|
|
16
|
+
getShellName,
|
|
17
|
+
hasLegacyInstall,
|
|
18
|
+
install as installShellIntegration,
|
|
19
|
+
isInstalled as isShellIntegrationInstalled,
|
|
20
|
+
} from "../lib/shell-integration.ts";
|
|
10
21
|
import { ensureAuth, ensureWrangler, isAuthenticated } from "../lib/wrangler.ts";
|
|
11
22
|
|
|
12
23
|
export async function isInitialized(): Promise<boolean> {
|
|
@@ -122,7 +133,47 @@ export default async function init(options: InitOptions = {}): Promise<void> {
|
|
|
122
133
|
}
|
|
123
134
|
}
|
|
124
135
|
|
|
125
|
-
// Step 5:
|
|
136
|
+
// Step 5: Shell integration
|
|
137
|
+
const shell = detectShell();
|
|
138
|
+
const rcFile = getRcFilePath(shell);
|
|
139
|
+
|
|
140
|
+
if (rcFile && shell !== "unknown") {
|
|
141
|
+
const alreadyInstalled = isShellIntegrationInstalled(rcFile);
|
|
142
|
+
const hasLegacy = hasLegacyInstall(rcFile);
|
|
143
|
+
|
|
144
|
+
if (alreadyInstalled && !hasLegacy) {
|
|
145
|
+
// Already installed
|
|
146
|
+
} else if (hasLegacy) {
|
|
147
|
+
console.error("");
|
|
148
|
+
info("Upgrading shell integration...");
|
|
149
|
+
try {
|
|
150
|
+
const result = installShellIntegration(rcFile);
|
|
151
|
+
if (result.migrated) {
|
|
152
|
+
success(`Upgraded to ${getShellFileDisplayPath()}`);
|
|
153
|
+
info(`Restart your terminal or run: source ~/${getRcFileName(rcFile)}`);
|
|
154
|
+
}
|
|
155
|
+
} catch {
|
|
156
|
+
info("Could not upgrade shell integration (non-critical)");
|
|
157
|
+
}
|
|
158
|
+
} else {
|
|
159
|
+
console.error("");
|
|
160
|
+
info("Enable 'jack cd' and 'jack new' to auto-change directories?");
|
|
161
|
+
const choice = await promptSelect(["Yes", "No"]);
|
|
162
|
+
|
|
163
|
+
if (choice === 0) {
|
|
164
|
+
try {
|
|
165
|
+
installShellIntegration(rcFile);
|
|
166
|
+
success(`Added to ~/${getRcFileName(rcFile)}`);
|
|
167
|
+
info(`Restart your terminal or run: source ~/${getRcFileName(rcFile)}`);
|
|
168
|
+
} catch {
|
|
169
|
+
info("Could not update shell config (non-critical)");
|
|
170
|
+
info('Add manually: eval "$(jack shell-init)"');
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Step 6: Save config (preserve existing agents, just update init status)
|
|
126
177
|
const existingConfig = await readConfig();
|
|
127
178
|
await writeConfig({
|
|
128
179
|
version: 1,
|
package/src/commands/link.ts
CHANGED
|
@@ -9,14 +9,10 @@
|
|
|
9
9
|
|
|
10
10
|
import { existsSync } from "node:fs";
|
|
11
11
|
import { isLoggedIn } from "../lib/auth/index.ts";
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
type ManagedProject,
|
|
15
|
-
findProjectBySlug,
|
|
16
|
-
listManagedProjects,
|
|
17
|
-
} from "../lib/control-plane.ts";
|
|
12
|
+
import { findProjectById, findProjectBySlug } from "../lib/control-plane.ts";
|
|
18
13
|
import { error, info, output, success } from "../lib/output.ts";
|
|
19
14
|
import { registerPath } from "../lib/paths-index.ts";
|
|
15
|
+
import { pickProject, requireTTY } from "../lib/picker.ts";
|
|
20
16
|
import { generateByoProjectId, linkProject, readProjectLink } from "../lib/project-link.ts";
|
|
21
17
|
|
|
22
18
|
export interface LinkFlags {
|
|
@@ -27,8 +23,17 @@ export default async function link(projectName?: string, flags: LinkFlags = {}):
|
|
|
27
23
|
// Check if already linked
|
|
28
24
|
const existingLink = await readProjectLink(process.cwd());
|
|
29
25
|
if (existingLink) {
|
|
26
|
+
// Try to look up project name for better UX
|
|
27
|
+
let projectDisplay = existingLink.project_id;
|
|
28
|
+
if (existingLink.deploy_mode === "managed") {
|
|
29
|
+
const project = await findProjectById(existingLink.project_id);
|
|
30
|
+
if (project) {
|
|
31
|
+
projectDisplay = project.slug;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
30
35
|
error("This directory is already linked");
|
|
31
|
-
info(`
|
|
36
|
+
info(`Linked to: ${projectDisplay}`);
|
|
32
37
|
info("To re-link, first run: jack unlink");
|
|
33
38
|
process.exit(1);
|
|
34
39
|
}
|
|
@@ -105,51 +110,28 @@ export default async function link(projectName?: string, flags: LinkFlags = {}):
|
|
|
105
110
|
return;
|
|
106
111
|
}
|
|
107
112
|
|
|
108
|
-
// Interactive mode -
|
|
109
|
-
|
|
110
|
-
let projects: ManagedProject[] = [];
|
|
111
|
-
try {
|
|
112
|
-
projects = await listManagedProjects();
|
|
113
|
-
} catch (err) {
|
|
114
|
-
output.stop();
|
|
115
|
-
error("Failed to load projects");
|
|
116
|
-
if (err instanceof Error) {
|
|
117
|
-
info(err.message);
|
|
118
|
-
}
|
|
119
|
-
process.exit(1);
|
|
120
|
-
}
|
|
121
|
-
output.stop();
|
|
122
|
-
|
|
123
|
-
if (projects.length === 0) {
|
|
124
|
-
error("No projects found");
|
|
125
|
-
info("Create one with: jack new");
|
|
126
|
-
info("Or link to your Cloudflare account: jack link --byo");
|
|
127
|
-
process.exit(1);
|
|
128
|
-
}
|
|
113
|
+
// Interactive mode - use fuzzy picker with cloud-only projects
|
|
114
|
+
requireTTY();
|
|
129
115
|
|
|
130
|
-
|
|
131
|
-
const choice = await promptSelectValue(
|
|
132
|
-
"Select a project to link:",
|
|
133
|
-
projects.map((p) => ({
|
|
134
|
-
value: p.id,
|
|
135
|
-
label: `${p.slug} (${p.status})`,
|
|
136
|
-
})),
|
|
137
|
-
);
|
|
116
|
+
const result = await pickProject({ cloudOnly: true });
|
|
138
117
|
|
|
139
|
-
if (
|
|
118
|
+
if (result.action === "cancel") {
|
|
140
119
|
info("Cancelled");
|
|
141
120
|
process.exit(0);
|
|
142
121
|
}
|
|
143
122
|
|
|
144
|
-
const selected =
|
|
145
|
-
|
|
146
|
-
|
|
123
|
+
const selected = result.project;
|
|
124
|
+
|
|
125
|
+
// Need project ID - fetch from control plane by slug
|
|
126
|
+
const project = await findProjectBySlug(selected.name);
|
|
127
|
+
if (!project) {
|
|
128
|
+
error(`Could not find project: ${selected.name}`);
|
|
147
129
|
process.exit(1);
|
|
148
130
|
}
|
|
149
131
|
|
|
150
132
|
output.start("Linking project...");
|
|
151
|
-
await linkProject(process.cwd(),
|
|
152
|
-
await registerPath(
|
|
133
|
+
await linkProject(process.cwd(), project.id, "managed");
|
|
134
|
+
await registerPath(project.id, process.cwd());
|
|
153
135
|
output.stop();
|
|
154
|
-
success(`Linked to: ${selected.
|
|
136
|
+
success(`Linked to: ${selected.name}`);
|
|
155
137
|
}
|
package/src/commands/logs.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
-
import { output } from "../lib/output.ts";
|
|
3
|
-
import { getDeployMode, getProjectId } from "../lib/project-link.ts";
|
|
4
2
|
import { authFetch } from "../lib/auth/index.ts";
|
|
5
3
|
import { getControlApiUrl, startLogSession } from "../lib/control-plane.ts";
|
|
4
|
+
import { output } from "../lib/output.ts";
|
|
5
|
+
import { getDeployMode, getProjectId } from "../lib/project-link.ts";
|
|
6
6
|
|
|
7
7
|
// Lines containing these strings will be filtered out
|
|
8
8
|
const FILTERED_PATTERNS = ["⛅️ wrangler"];
|
package/src/commands/mcp.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
-
import {
|
|
2
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
APP_MCP_CONFIGS,
|
|
8
|
+
getAppDisplayName,
|
|
9
|
+
installMcpConfigToApp,
|
|
10
|
+
isAppInstalled,
|
|
11
|
+
} from "../lib/mcp-config.ts";
|
|
12
|
+
import { error, info, item, success } from "../lib/output.ts";
|
|
7
13
|
import { startMcpServer } from "../mcp/server.ts";
|
|
8
14
|
|
|
9
15
|
const cliRoot = fileURLToPath(new URL("../..", import.meta.url));
|
|
@@ -27,13 +33,78 @@ export default async function mcp(subcommand?: string, options: McpOptions = {})
|
|
|
27
33
|
return;
|
|
28
34
|
}
|
|
29
35
|
|
|
30
|
-
|
|
36
|
+
if (subcommand === "install") {
|
|
37
|
+
await installMcpConfig();
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
error("Unknown subcommand. Use: jack mcp serve, jack mcp install, or jack mcp test");
|
|
31
42
|
info("Usage:");
|
|
32
43
|
info(" jack mcp serve [--project /path] [--debug] Start MCP server");
|
|
44
|
+
info(" jack mcp install Install/repair MCP config for AI agents");
|
|
33
45
|
info(" jack mcp test Test MCP server connectivity");
|
|
34
46
|
process.exit(1);
|
|
35
47
|
}
|
|
36
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Install or repair MCP configuration for all detected apps
|
|
51
|
+
*/
|
|
52
|
+
async function installMcpConfig(): Promise<void> {
|
|
53
|
+
info("Installing jack MCP server configuration...\n");
|
|
54
|
+
|
|
55
|
+
const installed: string[] = [];
|
|
56
|
+
const skipped: string[] = [];
|
|
57
|
+
const failed: string[] = [];
|
|
58
|
+
|
|
59
|
+
for (const appId of Object.keys(APP_MCP_CONFIGS)) {
|
|
60
|
+
const displayName = getAppDisplayName(appId);
|
|
61
|
+
|
|
62
|
+
if (!isAppInstalled(appId)) {
|
|
63
|
+
skipped.push(displayName);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
const result = await installMcpConfigToApp(appId);
|
|
69
|
+
if (result) {
|
|
70
|
+
installed.push(displayName);
|
|
71
|
+
} else {
|
|
72
|
+
failed.push(displayName);
|
|
73
|
+
}
|
|
74
|
+
} catch {
|
|
75
|
+
failed.push(displayName);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Report results
|
|
80
|
+
if (installed.length > 0) {
|
|
81
|
+
success(`Installed to ${installed.length} app(s):`);
|
|
82
|
+
for (const app of installed) {
|
|
83
|
+
item(` ${app}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (skipped.length > 0) {
|
|
88
|
+
info(`\nSkipped (not installed):`);
|
|
89
|
+
for (const app of skipped) {
|
|
90
|
+
item(` ${app}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (failed.length > 0) {
|
|
95
|
+
error(`\nFailed to install:`);
|
|
96
|
+
for (const app of failed) {
|
|
97
|
+
item(` ${app}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (installed.length > 0) {
|
|
102
|
+
info("\nRestart your AI agent (Claude Code, Claude Desktop) to use jack MCP tools.");
|
|
103
|
+
} else if (failed.length === 0 && skipped.length > 0) {
|
|
104
|
+
info("\nNo supported AI agents detected. Install Claude Code or Claude Desktop first.");
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
37
108
|
/**
|
|
38
109
|
* Test MCP server by spawning it and sending test requests
|
|
39
110
|
*/
|