@getjack/jack 0.1.2 → 0.1.4
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/README.md +77 -29
- 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/src/commands/cloud.ts +0 -230
- package/templates/api/wrangler.toml +0 -3
package/src/commands/services.ts
CHANGED
|
@@ -1,74 +1,80 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
1
|
import { join } from "node:path";
|
|
3
|
-
import {
|
|
2
|
+
import { fetchProjectResources } from "../lib/control-plane.ts";
|
|
4
3
|
import { formatSize } from "../lib/format.ts";
|
|
4
|
+
import { promptSelect } from "../lib/hooks.ts";
|
|
5
5
|
import { error, info, item, output as outputSpinner, success, warn } from "../lib/output.ts";
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
getProject,
|
|
9
|
-
getProjectDatabaseName,
|
|
10
|
-
updateProjectDatabase,
|
|
11
|
-
} from "../lib/registry.ts";
|
|
6
|
+
import { getProject } from "../lib/registry.ts";
|
|
7
|
+
import { parseWranglerResources } from "../lib/resources.ts";
|
|
12
8
|
import {
|
|
13
9
|
deleteDatabase,
|
|
14
10
|
exportDatabase,
|
|
15
11
|
generateExportFilename,
|
|
16
|
-
getDatabaseInfo,
|
|
12
|
+
getDatabaseInfo as getWranglerDatabaseInfo,
|
|
17
13
|
} from "../lib/services/db.ts";
|
|
18
14
|
import { getProjectNameFromDir } from "../lib/storage/index.ts";
|
|
19
15
|
|
|
20
16
|
/**
|
|
21
|
-
*
|
|
22
|
-
* Fallback when registry doesn't have the info
|
|
17
|
+
* Database info from control plane or wrangler config
|
|
23
18
|
*/
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const content = await Bun.file(jsoncPath).text();
|
|
30
|
-
// Remove comments for JSON parsing (simple approach)
|
|
31
|
-
const jsonContent = content.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
|
|
32
|
-
const config = JSON.parse(jsonContent);
|
|
33
|
-
if (config.d1_databases?.[0]?.database_name) {
|
|
34
|
-
return config.d1_databases[0].database_name;
|
|
35
|
-
}
|
|
36
|
-
} catch {
|
|
37
|
-
// Ignore parse errors
|
|
38
|
-
}
|
|
39
|
-
}
|
|
19
|
+
interface ResolvedDatabaseInfo {
|
|
20
|
+
name: string;
|
|
21
|
+
id?: string;
|
|
22
|
+
source: "control-plane" | "wrangler";
|
|
23
|
+
}
|
|
40
24
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (match?.[1]) {
|
|
49
|
-
return match[1];
|
|
50
|
-
}
|
|
51
|
-
} catch {
|
|
52
|
-
// Ignore read errors
|
|
25
|
+
async function ensureLocalProjectContext(projectName: string): Promise<void> {
|
|
26
|
+
try {
|
|
27
|
+
const cwdProjectName = await getProjectNameFromDir(process.cwd());
|
|
28
|
+
if (cwdProjectName !== projectName) {
|
|
29
|
+
error(`Current directory is not the "${projectName}" project`);
|
|
30
|
+
info(`Run this command from the ${projectName} project directory`);
|
|
31
|
+
process.exit(1);
|
|
53
32
|
}
|
|
33
|
+
} catch {
|
|
34
|
+
error("Could not determine project from current directory");
|
|
35
|
+
info(`Run this command from the ${projectName} project directory`);
|
|
36
|
+
process.exit(1);
|
|
54
37
|
}
|
|
55
|
-
|
|
56
|
-
return null;
|
|
57
38
|
}
|
|
58
39
|
|
|
59
40
|
/**
|
|
60
|
-
* Get database
|
|
41
|
+
* Get database info for a project.
|
|
42
|
+
* For managed: fetch from control plane
|
|
43
|
+
* For BYO: parse from wrangler.jsonc
|
|
61
44
|
*/
|
|
62
|
-
async function
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
45
|
+
async function resolveDatabaseInfo(projectName: string): Promise<ResolvedDatabaseInfo | null> {
|
|
46
|
+
const project = await getProject(projectName);
|
|
47
|
+
|
|
48
|
+
// For managed projects, fetch from control plane
|
|
49
|
+
if (project?.deploy_mode === "managed" && project.remote?.project_id) {
|
|
50
|
+
try {
|
|
51
|
+
const resources = await fetchProjectResources(project.remote.project_id);
|
|
52
|
+
const d1 = resources.find((r) => r.resource_type === "d1");
|
|
53
|
+
if (d1) {
|
|
54
|
+
return {
|
|
55
|
+
name: d1.resource_name,
|
|
56
|
+
id: d1.provider_id,
|
|
57
|
+
source: "control-plane",
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
} catch {
|
|
61
|
+
// Fall through to wrangler parsing
|
|
62
|
+
}
|
|
67
63
|
}
|
|
68
64
|
|
|
69
|
-
//
|
|
70
|
-
|
|
71
|
-
|
|
65
|
+
// For BYO or fallback, parse from wrangler config
|
|
66
|
+
try {
|
|
67
|
+
await ensureLocalProjectContext(projectName);
|
|
68
|
+
const resources = await parseWranglerResources(process.cwd());
|
|
69
|
+
if (resources.d1) {
|
|
70
|
+
return {
|
|
71
|
+
name: resources.d1.name,
|
|
72
|
+
id: resources.d1.id,
|
|
73
|
+
source: "wrangler",
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
} catch {
|
|
77
|
+
// No database found
|
|
72
78
|
}
|
|
73
79
|
|
|
74
80
|
return null;
|
|
@@ -147,29 +153,23 @@ async function resolveProjectName(options: ServiceOptions): Promise<string> {
|
|
|
147
153
|
*/
|
|
148
154
|
async function dbInfo(options: ServiceOptions): Promise<void> {
|
|
149
155
|
const projectName = await resolveProjectName(options);
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
if (!project) {
|
|
153
|
-
error(`Project "${projectName}" not found in registry`);
|
|
154
|
-
info("List projects with: jack projects list");
|
|
155
|
-
process.exit(1);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const dbName = await resolveDbName(project);
|
|
156
|
+
const dbInfo = await resolveDatabaseInfo(projectName);
|
|
159
157
|
|
|
160
|
-
if (!
|
|
158
|
+
if (!dbInfo) {
|
|
161
159
|
console.error("");
|
|
162
|
-
|
|
160
|
+
error("No database found for this project.");
|
|
161
|
+
info("For managed projects, create a database with: jack services db create");
|
|
162
|
+
info("For BYO projects, add d1_databases to your wrangler.jsonc");
|
|
163
163
|
console.error("");
|
|
164
164
|
return;
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
-
// Fetch database info
|
|
167
|
+
// Fetch detailed database info via wrangler
|
|
168
168
|
outputSpinner.start("Fetching database info...");
|
|
169
|
-
const
|
|
169
|
+
const wranglerDbInfo = await getWranglerDatabaseInfo(dbInfo.name);
|
|
170
170
|
outputSpinner.stop();
|
|
171
171
|
|
|
172
|
-
if (!
|
|
172
|
+
if (!wranglerDbInfo) {
|
|
173
173
|
console.error("");
|
|
174
174
|
error("Database not found");
|
|
175
175
|
info("It may have been deleted");
|
|
@@ -179,11 +179,14 @@ async function dbInfo(options: ServiceOptions): Promise<void> {
|
|
|
179
179
|
|
|
180
180
|
// Display info
|
|
181
181
|
console.error("");
|
|
182
|
-
success(`Database: ${
|
|
182
|
+
success(`Database: ${wranglerDbInfo.name}`);
|
|
183
183
|
console.error("");
|
|
184
|
-
item(`Size: ${formatSize(
|
|
185
|
-
item(`Tables: ${
|
|
186
|
-
item(`ID: ${dbInfo.id}`);
|
|
184
|
+
item(`Size: ${formatSize(wranglerDbInfo.sizeBytes)}`);
|
|
185
|
+
item(`Tables: ${wranglerDbInfo.numTables}`);
|
|
186
|
+
item(`ID: ${dbInfo.id || wranglerDbInfo.id}`);
|
|
187
|
+
if (dbInfo.source === "control-plane") {
|
|
188
|
+
item("Source: managed (jack cloud)");
|
|
189
|
+
}
|
|
187
190
|
console.error("");
|
|
188
191
|
}
|
|
189
192
|
|
|
@@ -192,42 +195,27 @@ async function dbInfo(options: ServiceOptions): Promise<void> {
|
|
|
192
195
|
*/
|
|
193
196
|
async function dbExport(options: ServiceOptions): Promise<void> {
|
|
194
197
|
const projectName = await resolveProjectName(options);
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
if (!project) {
|
|
198
|
-
error(`Project "${projectName}" not found in registry`);
|
|
199
|
-
info("List projects with: jack projects list");
|
|
200
|
-
process.exit(1);
|
|
201
|
-
}
|
|
198
|
+
const dbInfo = await resolveDatabaseInfo(projectName);
|
|
202
199
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
if (!dbName) {
|
|
200
|
+
if (!dbInfo) {
|
|
206
201
|
console.error("");
|
|
207
|
-
|
|
202
|
+
error("No database found for this project.");
|
|
203
|
+
info("For managed projects, create a database with: jack services db create");
|
|
204
|
+
info("For BYO projects, add d1_databases to your wrangler.jsonc");
|
|
208
205
|
console.error("");
|
|
209
206
|
return;
|
|
210
207
|
}
|
|
211
208
|
|
|
212
209
|
// Generate filename
|
|
213
|
-
const filename = generateExportFilename(
|
|
214
|
-
|
|
215
|
-
// Determine output directory (project dir if in it, cwd otherwise)
|
|
216
|
-
let outputDir = process.cwd();
|
|
217
|
-
if (project.localPath && existsSync(project.localPath)) {
|
|
218
|
-
// Check if we're in the project directory or subdirectory
|
|
219
|
-
const cwd = process.cwd();
|
|
220
|
-
if (cwd === project.localPath || cwd.startsWith(`${project.localPath}/`)) {
|
|
221
|
-
outputDir = project.localPath;
|
|
222
|
-
}
|
|
223
|
-
}
|
|
210
|
+
const filename = generateExportFilename(dbInfo.name);
|
|
224
211
|
|
|
225
|
-
|
|
212
|
+
// Export to current directory
|
|
213
|
+
const outputPath = join(process.cwd(), filename);
|
|
226
214
|
|
|
227
215
|
// Export
|
|
228
216
|
outputSpinner.start("Exporting database...");
|
|
229
217
|
try {
|
|
230
|
-
await exportDatabase(
|
|
218
|
+
await exportDatabase(dbInfo.name, outputPath);
|
|
231
219
|
outputSpinner.stop();
|
|
232
220
|
|
|
233
221
|
console.error("");
|
|
@@ -246,50 +234,38 @@ async function dbExport(options: ServiceOptions): Promise<void> {
|
|
|
246
234
|
*/
|
|
247
235
|
async function dbDelete(options: ServiceOptions): Promise<void> {
|
|
248
236
|
const projectName = await resolveProjectName(options);
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
if (!project) {
|
|
252
|
-
error(`Project "${projectName}" not found in registry`);
|
|
253
|
-
info("List projects with: jack projects list");
|
|
254
|
-
process.exit(1);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
const dbName = await resolveDbName(project);
|
|
237
|
+
const dbInfo = await resolveDatabaseInfo(projectName);
|
|
258
238
|
|
|
259
|
-
if (!
|
|
239
|
+
if (!dbInfo) {
|
|
260
240
|
console.error("");
|
|
261
|
-
|
|
241
|
+
error("No database found for this project.");
|
|
242
|
+
info("For managed projects, create a database with: jack services db create");
|
|
243
|
+
info("For BYO projects, add d1_databases to your wrangler.jsonc");
|
|
262
244
|
console.error("");
|
|
263
245
|
return;
|
|
264
246
|
}
|
|
265
247
|
|
|
266
|
-
// Get database info to show what will be deleted
|
|
248
|
+
// Get detailed database info to show what will be deleted
|
|
267
249
|
outputSpinner.start("Fetching database info...");
|
|
268
|
-
const
|
|
250
|
+
const wranglerDbInfo = await getWranglerDatabaseInfo(dbInfo.name);
|
|
269
251
|
outputSpinner.stop();
|
|
270
252
|
|
|
271
253
|
// Show what will be deleted
|
|
272
254
|
console.error("");
|
|
273
|
-
info(`Database: ${
|
|
274
|
-
if (
|
|
275
|
-
item(`Size: ${formatSize(
|
|
276
|
-
item(`Tables: ${
|
|
255
|
+
info(`Database: ${dbInfo.name}`);
|
|
256
|
+
if (wranglerDbInfo) {
|
|
257
|
+
item(`Size: ${formatSize(wranglerDbInfo.sizeBytes)}`);
|
|
258
|
+
item(`Tables: ${wranglerDbInfo.numTables}`);
|
|
277
259
|
}
|
|
278
260
|
console.error("");
|
|
279
261
|
warn("This will permanently delete the database and all its data");
|
|
280
262
|
console.error("");
|
|
281
263
|
|
|
282
264
|
// Confirm deletion
|
|
283
|
-
console.error(
|
|
284
|
-
const
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
{ name: "1. Yes", value: "yes" },
|
|
288
|
-
{ name: "2. No", value: "no" },
|
|
289
|
-
],
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
if (action === "no") {
|
|
265
|
+
console.error(` Delete database '${dbInfo.name}'?\n`);
|
|
266
|
+
const choice = await promptSelect(["Yes", "No"]);
|
|
267
|
+
|
|
268
|
+
if (choice !== 0) {
|
|
293
269
|
info("Cancelled");
|
|
294
270
|
return;
|
|
295
271
|
}
|
|
@@ -297,12 +273,9 @@ async function dbDelete(options: ServiceOptions): Promise<void> {
|
|
|
297
273
|
// Delete database
|
|
298
274
|
outputSpinner.start("Deleting database...");
|
|
299
275
|
try {
|
|
300
|
-
await deleteDatabase(
|
|
276
|
+
await deleteDatabase(dbInfo.name);
|
|
301
277
|
outputSpinner.stop();
|
|
302
278
|
|
|
303
|
-
// Update registry (set db to null in services structure)
|
|
304
|
-
await updateProjectDatabase(projectName, null);
|
|
305
|
-
|
|
306
279
|
console.error("");
|
|
307
280
|
success("Database deleted");
|
|
308
281
|
console.error("");
|
package/src/commands/ship.ts
CHANGED
|
@@ -2,7 +2,9 @@ import { getErrorDetails } from "../lib/errors.ts";
|
|
|
2
2
|
import { output, spinner } from "../lib/output.ts";
|
|
3
3
|
import { deployProject } from "../lib/project-operations.ts";
|
|
4
4
|
|
|
5
|
-
export default async function ship(
|
|
5
|
+
export default async function ship(
|
|
6
|
+
options: { managed?: boolean; byo?: boolean } = {},
|
|
7
|
+
): Promise<void> {
|
|
6
8
|
const isCi = process.env.CI === "true" || process.env.CI === "1";
|
|
7
9
|
try {
|
|
8
10
|
const result = await deployProject({
|
|
@@ -20,6 +22,8 @@ export default async function ship(): Promise<void> {
|
|
|
20
22
|
interactive: !isCi,
|
|
21
23
|
includeSecrets: true,
|
|
22
24
|
includeSync: true,
|
|
25
|
+
managed: options.managed,
|
|
26
|
+
byo: options.byo,
|
|
23
27
|
});
|
|
24
28
|
|
|
25
29
|
if (!result.workerUrl && result.deployOutput) {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { getCredentials } from "../lib/auth/store.ts";
|
|
2
|
+
import { info, item, success } from "../lib/output.ts";
|
|
3
|
+
|
|
4
|
+
export default async function whoami(): Promise<void> {
|
|
5
|
+
const creds = await getCredentials();
|
|
6
|
+
|
|
7
|
+
if (!creds) {
|
|
8
|
+
info("Not logged in");
|
|
9
|
+
info("Run 'jack login' to sign in");
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
console.error("");
|
|
14
|
+
success("Logged in");
|
|
15
|
+
item(`Email: ${creds.user.email}`);
|
|
16
|
+
item(`ID: ${creds.user.id}`);
|
|
17
|
+
|
|
18
|
+
if (creds.user.first_name) {
|
|
19
|
+
item(`Name: ${creds.user.first_name}${creds.user.last_name ? ` ${creds.user.last_name}` : ""}`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const expiresIn = creds.expires_at - Math.floor(Date.now() / 1000);
|
|
23
|
+
if (expiresIn > 0) {
|
|
24
|
+
const hours = Math.floor(expiresIn / 3600);
|
|
25
|
+
const minutes = Math.floor((expiresIn % 3600) / 60);
|
|
26
|
+
item(`Token expires: ${hours}h ${minutes}m`);
|
|
27
|
+
} else {
|
|
28
|
+
item("Token: expired (will refresh on next request)");
|
|
29
|
+
}
|
|
30
|
+
console.error("");
|
|
31
|
+
}
|