@instafy/cli 0.1.8 → 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/README.md +16 -9
- package/dist/api.js +9 -0
- package/dist/auth.js +392 -23
- package/dist/config.js +150 -6
- package/dist/errors.js +63 -0
- package/dist/git-credential.js +205 -0
- package/dist/git-setup.js +56 -0
- package/dist/git-wrapper.js +502 -0
- package/dist/git.js +11 -5
- package/dist/index.js +293 -108
- package/dist/org.js +9 -4
- package/dist/project-manifest.js +24 -0
- package/dist/project.js +254 -29
- package/dist/rathole.js +14 -10
- package/dist/runtime.js +11 -24
- package/dist/tunnel.js +272 -7
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -4,11 +4,13 @@ import { createRequire } from "node:module";
|
|
|
4
4
|
import kleur from "kleur";
|
|
5
5
|
import { runtimeStart, runtimeStatus, runtimeStop, runtimeToken, findProjectManifest, } from "./runtime.js";
|
|
6
6
|
import { login, logout } from "./auth.js";
|
|
7
|
-
import {
|
|
8
|
-
import { projectInit } from "./project.js";
|
|
9
|
-
import { runTunnelCommand } from "./tunnel.js";
|
|
7
|
+
import { runGitCredentialHelper } from "./git-credential.js";
|
|
8
|
+
import { projectInit, projectProfile } from "./project.js";
|
|
9
|
+
import { listTunnelSessions, startTunnelDetached, stopTunnelSession, tailTunnelLogs, runTunnelCommand } from "./tunnel.js";
|
|
10
10
|
import { requestControllerApi } from "./api.js";
|
|
11
11
|
import { configGet, configList, configPath, configSet, configUnset } from "./config-command.js";
|
|
12
|
+
import { getInstafyProfileConfigPath, listInstafyProfileNames, readInstafyProfileConfig } from "./config.js";
|
|
13
|
+
import { runInstafyGit, runInstafyGitSync } from "./git-wrapper.js";
|
|
12
14
|
export const program = new Command();
|
|
13
15
|
const require = createRequire(import.meta.url);
|
|
14
16
|
const pkg = require("../package.json");
|
|
@@ -39,7 +41,11 @@ program
|
|
|
39
41
|
.description("Log in and save an access token for future CLI commands")
|
|
40
42
|
.option("--studio-url <url>", "Studio web URL (default: staging or localhost)")
|
|
41
43
|
.option("--server-url <url>", "Instafy server/controller URL")
|
|
44
|
+
.option("--profile <name>", "Save token under a named profile (multi-account support)")
|
|
42
45
|
.option("--token <token>", "Provide token directly (skips prompt)")
|
|
46
|
+
.option("--email <email>", "Email for non-interactive login (requires SUPABASE_URL + SUPABASE_ANON_KEY)")
|
|
47
|
+
.option("--password <password>", "Password for non-interactive login (requires SUPABASE_URL + SUPABASE_ANON_KEY)")
|
|
48
|
+
.option("--no-git-setup", "Do not configure git credential helper")
|
|
43
49
|
.option("--no-store", "Do not save token to ~/.instafy/config.json")
|
|
44
50
|
.option("--json", "Output JSON")
|
|
45
51
|
.action(async (opts) => {
|
|
@@ -48,7 +54,11 @@ program
|
|
|
48
54
|
controllerUrl: opts.serverUrl,
|
|
49
55
|
studioUrl: opts.studioUrl,
|
|
50
56
|
token: opts.token,
|
|
57
|
+
email: opts.email,
|
|
58
|
+
password: opts.password,
|
|
59
|
+
gitSetup: opts.gitSetup,
|
|
51
60
|
noStore: opts.store === false,
|
|
61
|
+
profile: opts.profile,
|
|
52
62
|
json: opts.json,
|
|
53
63
|
});
|
|
54
64
|
}
|
|
@@ -60,10 +70,94 @@ program
|
|
|
60
70
|
program
|
|
61
71
|
.command("logout")
|
|
62
72
|
.description("Clear the saved CLI access token")
|
|
73
|
+
.option("--profile <name>", "Clear the token for a named profile")
|
|
63
74
|
.option("--json", "Output JSON")
|
|
64
75
|
.action(async (opts) => {
|
|
65
76
|
try {
|
|
66
|
-
await logout({ json: opts.json });
|
|
77
|
+
await logout({ json: opts.json, profile: opts.profile });
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
console.error(kleur.red(String(error)));
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
const projectCommand = program
|
|
85
|
+
.command("project")
|
|
86
|
+
.description("Create/link and manage Instafy projects");
|
|
87
|
+
projectCommand.action(() => projectCommand.outputHelp());
|
|
88
|
+
const projectInitCommand = projectCommand
|
|
89
|
+
.command("init")
|
|
90
|
+
.description("Create an Instafy project and link this folder (.instafy/project.json)")
|
|
91
|
+
.option("--path <dir>", "Directory where the manifest should be written (default: cwd)");
|
|
92
|
+
addServerUrlOptions(projectInitCommand);
|
|
93
|
+
projectInitCommand
|
|
94
|
+
.option("--access-token <token>", "Instafy or Supabase access token")
|
|
95
|
+
.option("--profile <name>", "CLI profile to use when running commands in this folder")
|
|
96
|
+
.option("--project-type <type>", "Project type (customer|sandbox)")
|
|
97
|
+
.option("--org-id <uuid>", "Optional organization id")
|
|
98
|
+
.option("--org-name <name>", "Optional organization name")
|
|
99
|
+
.option("--org-slug <slug>", "Optional organization slug")
|
|
100
|
+
.option("--owner-user-id <uuid>", "Explicit owner user id (defaults to caller)")
|
|
101
|
+
.option("--json", "Output JSON")
|
|
102
|
+
.action(async (opts) => {
|
|
103
|
+
try {
|
|
104
|
+
await projectInit({
|
|
105
|
+
path: opts.path,
|
|
106
|
+
controllerUrl: opts.serverUrl ?? opts.controllerUrl,
|
|
107
|
+
accessToken: opts.accessToken,
|
|
108
|
+
profile: opts.profile,
|
|
109
|
+
projectType: opts.projectType,
|
|
110
|
+
orgId: opts.orgId,
|
|
111
|
+
orgName: opts.orgName,
|
|
112
|
+
orgSlug: opts.orgSlug,
|
|
113
|
+
ownerUserId: opts.ownerUserId,
|
|
114
|
+
json: opts.json,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
console.error(kleur.red(String(error)));
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
projectCommand
|
|
123
|
+
.command("profile")
|
|
124
|
+
.description("Get/set the CLI profile for this folder (.instafy/project.json)")
|
|
125
|
+
.argument("[profile]", "Profile name to set (omit to print current)")
|
|
126
|
+
.option("--unset", "Clear the configured profile for this folder")
|
|
127
|
+
.option("--path <dir>", "Directory to search for the manifest (default: cwd)")
|
|
128
|
+
.option("--json", "Output JSON")
|
|
129
|
+
.action(async (profile, opts) => {
|
|
130
|
+
try {
|
|
131
|
+
projectProfile({
|
|
132
|
+
profile,
|
|
133
|
+
unset: opts.unset,
|
|
134
|
+
path: opts.path,
|
|
135
|
+
json: opts.json,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
console.error(kleur.red(String(error)));
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
const projectListCommand = projectCommand
|
|
144
|
+
.command("list")
|
|
145
|
+
.description("List projects for your account")
|
|
146
|
+
.option("--access-token <token>", "Instafy or Supabase access token");
|
|
147
|
+
addServerUrlOptions(projectListCommand);
|
|
148
|
+
projectListCommand
|
|
149
|
+
.option("--org-id <uuid>", "Filter by organization id")
|
|
150
|
+
.option("--org-slug <slug>", "Filter by organization slug")
|
|
151
|
+
.option("--json", "Output JSON")
|
|
152
|
+
.action(async (opts) => {
|
|
153
|
+
try {
|
|
154
|
+
await (await import("./project.js")).listProjects({
|
|
155
|
+
controllerUrl: opts.serverUrl ?? opts.controllerUrl,
|
|
156
|
+
accessToken: opts.accessToken,
|
|
157
|
+
orgId: opts.orgId,
|
|
158
|
+
orgSlug: opts.orgSlug,
|
|
159
|
+
json: opts.json,
|
|
160
|
+
});
|
|
67
161
|
}
|
|
68
162
|
catch (error) {
|
|
69
163
|
console.error(kleur.red(String(error)));
|
|
@@ -71,6 +165,7 @@ program
|
|
|
71
165
|
}
|
|
72
166
|
});
|
|
73
167
|
const configCommand = program.command("config").description("Get/set saved CLI configuration");
|
|
168
|
+
configCommand.action(() => configCommand.outputHelp());
|
|
74
169
|
configCommand
|
|
75
170
|
.command("path")
|
|
76
171
|
.description("Print the config file path")
|
|
@@ -140,8 +235,12 @@ configCommand
|
|
|
140
235
|
process.exit(1);
|
|
141
236
|
}
|
|
142
237
|
});
|
|
143
|
-
const
|
|
144
|
-
.command("runtime
|
|
238
|
+
const runtimeCommand = program
|
|
239
|
+
.command("runtime")
|
|
240
|
+
.description("Start/stop the local Instafy runtime");
|
|
241
|
+
runtimeCommand.action(() => runtimeCommand.outputHelp());
|
|
242
|
+
const runtimeStartCommand = runtimeCommand
|
|
243
|
+
.command("start")
|
|
145
244
|
.description("Start a local runtime for this project")
|
|
146
245
|
.option("--project <id>", "Project UUID");
|
|
147
246
|
addServerUrlOptions(runtimeStartCommand);
|
|
@@ -180,8 +279,8 @@ runtimeStartCommand
|
|
|
180
279
|
process.exit(1);
|
|
181
280
|
}
|
|
182
281
|
});
|
|
183
|
-
|
|
184
|
-
.command("
|
|
282
|
+
runtimeCommand
|
|
283
|
+
.command("status")
|
|
185
284
|
.description("Show runtime health")
|
|
186
285
|
.option("--json", "Output status as JSON")
|
|
187
286
|
.action(async (opts) => {
|
|
@@ -193,8 +292,8 @@ program
|
|
|
193
292
|
process.exit(1);
|
|
194
293
|
}
|
|
195
294
|
});
|
|
196
|
-
|
|
197
|
-
.command("
|
|
295
|
+
runtimeCommand
|
|
296
|
+
.command("stop")
|
|
198
297
|
.description("Stop the local Instafy runtime")
|
|
199
298
|
.option("--json", "Output result as JSON")
|
|
200
299
|
.action(async (opts) => {
|
|
@@ -206,8 +305,8 @@ program
|
|
|
206
305
|
process.exit(1);
|
|
207
306
|
}
|
|
208
307
|
});
|
|
209
|
-
const runtimeTokenCommand =
|
|
210
|
-
.command("
|
|
308
|
+
const runtimeTokenCommand = runtimeCommand
|
|
309
|
+
.command("token")
|
|
211
310
|
.description("Mint a runtime access token")
|
|
212
311
|
.option("--project <id>", "Project UUID (defaults to .instafy/project.json)");
|
|
213
312
|
addServerUrlOptions(runtimeTokenCommand);
|
|
@@ -222,7 +321,7 @@ runtimeTokenCommand
|
|
|
222
321
|
? opts.project.trim()
|
|
223
322
|
: findProjectManifest(process.cwd()).manifest?.projectId ?? null;
|
|
224
323
|
if (!project) {
|
|
225
|
-
throw new Error("No project configured. Run `instafy project
|
|
324
|
+
throw new Error("No project configured. Run `instafy project init` or pass --project.");
|
|
226
325
|
}
|
|
227
326
|
await runtimeToken({
|
|
228
327
|
project,
|
|
@@ -238,101 +337,140 @@ runtimeTokenCommand
|
|
|
238
337
|
process.exit(1);
|
|
239
338
|
}
|
|
240
339
|
});
|
|
241
|
-
|
|
242
|
-
.command("git
|
|
243
|
-
.description("
|
|
244
|
-
.
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
.
|
|
251
|
-
.
|
|
252
|
-
.option("--
|
|
340
|
+
program
|
|
341
|
+
.command("git")
|
|
342
|
+
.description("Run git against the Instafy canonical checkout (.instafy/.git)")
|
|
343
|
+
.allowUnknownOption(true);
|
|
344
|
+
const tunnelCommand = program
|
|
345
|
+
.command("tunnel")
|
|
346
|
+
.description("Create and manage shareable tunnels");
|
|
347
|
+
tunnelCommand.action(() => tunnelCommand.outputHelp());
|
|
348
|
+
const tunnelStartCommand = tunnelCommand
|
|
349
|
+
.command("start")
|
|
350
|
+
.description("Create a shareable tunnel URL for a local port (defaults to detached)")
|
|
351
|
+
.option("--port <port>", "Local port to expose (default 3000)")
|
|
352
|
+
.option("--project <id>", "Project UUID (defaults to .instafy/project.json or PROJECT_ID)");
|
|
353
|
+
addServerUrlOptions(tunnelStartCommand);
|
|
354
|
+
addAccessTokenOptions(tunnelStartCommand, "Instafy access token (defaults to saved `instafy login` token)");
|
|
355
|
+
addServiceTokenOptions(tunnelStartCommand, "Instafy service token (advanced)");
|
|
356
|
+
tunnelStartCommand
|
|
357
|
+
.option("--no-detach", "Run in foreground until interrupted")
|
|
358
|
+
.option("--rathole-bin <path>", "Path to rathole binary (or set RATHOLE_BIN)")
|
|
359
|
+
.option("--log-file <path>", "Write tunnel logs to a file (default: ~/.instafy/cli-tunnel-logs/*)")
|
|
360
|
+
.option("--json", "Output JSON")
|
|
253
361
|
.action(async (opts) => {
|
|
254
362
|
try {
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
363
|
+
const port = opts.port ? Number(opts.port) : undefined;
|
|
364
|
+
const controllerToken = opts.serviceToken ??
|
|
365
|
+
opts.controllerToken ??
|
|
366
|
+
opts.accessToken ??
|
|
367
|
+
opts.controllerAccessToken;
|
|
368
|
+
if (opts.detach === false) {
|
|
369
|
+
await runTunnelCommand({
|
|
370
|
+
project: opts.project,
|
|
371
|
+
controllerUrl: opts.serverUrl ?? opts.controllerUrl,
|
|
372
|
+
controllerToken,
|
|
373
|
+
port,
|
|
374
|
+
ratholeBin: opts.ratholeBin,
|
|
375
|
+
});
|
|
376
|
+
return;
|
|
265
377
|
}
|
|
266
|
-
await
|
|
267
|
-
project,
|
|
378
|
+
const started = await startTunnelDetached({
|
|
379
|
+
project: opts.project,
|
|
268
380
|
controllerUrl: opts.serverUrl ?? opts.controllerUrl,
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
ttlSeconds,
|
|
274
|
-
json: opts.json,
|
|
381
|
+
controllerToken,
|
|
382
|
+
port,
|
|
383
|
+
ratholeBin: opts.ratholeBin,
|
|
384
|
+
logFile: opts.logFile,
|
|
275
385
|
});
|
|
386
|
+
if (opts.json) {
|
|
387
|
+
console.log(JSON.stringify(started, null, 2));
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
console.log(kleur.green(`Tunnel started: ${started.url} (tunnelId=${started.tunnelId})`));
|
|
391
|
+
console.log(kleur.gray(`pid=${started.pid} · port=${started.localPort}`));
|
|
392
|
+
console.log(kleur.gray(`log: ${started.logFile}`));
|
|
393
|
+
console.log("");
|
|
394
|
+
console.log("Next:");
|
|
395
|
+
console.log(`- ${kleur.cyan(`instafy tunnel list`)}`);
|
|
396
|
+
console.log(`- ${kleur.cyan(`instafy tunnel logs ${started.tunnelId} --follow`)}`);
|
|
397
|
+
console.log(`- ${kleur.cyan(`instafy tunnel stop ${started.tunnelId}`)}`);
|
|
398
|
+
if (process.platform !== "win32") {
|
|
399
|
+
console.log(kleur.gray(`(or) tail -n 200 -f ${started.logFile}`));
|
|
400
|
+
}
|
|
276
401
|
}
|
|
277
402
|
catch (error) {
|
|
278
403
|
console.error(kleur.red(String(error)));
|
|
279
404
|
process.exit(1);
|
|
280
405
|
}
|
|
281
406
|
});
|
|
282
|
-
|
|
283
|
-
.command("
|
|
284
|
-
.description("
|
|
285
|
-
.option("--
|
|
286
|
-
addServerUrlOptions(projectInitCommand);
|
|
287
|
-
projectInitCommand
|
|
288
|
-
.option("--access-token <token>", "Instafy or Supabase access token")
|
|
289
|
-
.option("--project-type <type>", "Project type (customer|sandbox)")
|
|
290
|
-
.option("--org-id <uuid>", "Optional organization id")
|
|
291
|
-
.option("--org-name <name>", "Optional organization name")
|
|
292
|
-
.option("--org-slug <slug>", "Optional organization slug")
|
|
293
|
-
.option("--owner-user-id <uuid>", "Explicit owner user id (defaults to caller)")
|
|
407
|
+
tunnelCommand
|
|
408
|
+
.command("list")
|
|
409
|
+
.description("List local tunnel sessions started by this CLI")
|
|
410
|
+
.option("--all", "Include stopped/stale tunnels")
|
|
294
411
|
.option("--json", "Output JSON")
|
|
295
412
|
.action(async (opts) => {
|
|
296
413
|
try {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
414
|
+
const tunnels = listTunnelSessions({ all: Boolean(opts.all), json: Boolean(opts.json) });
|
|
415
|
+
if (opts.json) {
|
|
416
|
+
console.log(JSON.stringify(tunnels, null, 2));
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
if (tunnels.length === 0) {
|
|
420
|
+
console.log(kleur.yellow("No tunnels found."));
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
for (const tunnel of tunnels) {
|
|
424
|
+
console.log(`${tunnel.tunnelId} · ${tunnel.url} · port=${tunnel.localPort} · pid=${tunnel.pid}`);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
catch (error) {
|
|
428
|
+
console.error(kleur.red(String(error)));
|
|
429
|
+
process.exit(1);
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
tunnelCommand
|
|
433
|
+
.command("stop")
|
|
434
|
+
.description("Stop a local tunnel session and revoke it")
|
|
435
|
+
.argument("[tunnelId]", "Tunnel ID (defaults to the only active tunnel)")
|
|
436
|
+
.option("--server-url <url>", "Instafy server URL")
|
|
437
|
+
.option("--access-token <token>", "Instafy access token (defaults to saved `instafy login` token)")
|
|
438
|
+
.option("--service-token <token>", "Instafy service token (advanced)")
|
|
439
|
+
.option("--json", "Output JSON")
|
|
440
|
+
.action(async (tunnelId, opts) => {
|
|
441
|
+
try {
|
|
442
|
+
const result = await stopTunnelSession({
|
|
443
|
+
tunnelId,
|
|
444
|
+
controllerUrl: opts.serverUrl,
|
|
445
|
+
controllerToken: opts.serviceToken ?? opts.accessToken,
|
|
306
446
|
json: opts.json,
|
|
307
447
|
});
|
|
448
|
+
if (opts.json) {
|
|
449
|
+
console.log(JSON.stringify(result, null, 2));
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
console.log(kleur.green(`Tunnel stopped: ${result.tunnelId}`));
|
|
308
453
|
}
|
|
309
454
|
catch (error) {
|
|
310
455
|
console.error(kleur.red(String(error)));
|
|
311
456
|
process.exit(1);
|
|
312
457
|
}
|
|
313
458
|
});
|
|
314
|
-
const tunnelCommand = program
|
|
315
|
-
.command("tunnel")
|
|
316
|
-
.description("Create a shareable tunnel URL for a local port")
|
|
317
|
-
.option("--port <port>", "Local port to expose (default 3000)")
|
|
318
|
-
.option("--project <id>", "Project UUID (defaults to .instafy/project.json or PROJECT_ID)");
|
|
319
|
-
addServerUrlOptions(tunnelCommand);
|
|
320
|
-
addAccessTokenOptions(tunnelCommand, "Instafy access token (defaults to saved `instafy login` token)");
|
|
321
|
-
addServiceTokenOptions(tunnelCommand, "Instafy service token (advanced)");
|
|
322
459
|
tunnelCommand
|
|
323
|
-
.
|
|
324
|
-
.
|
|
460
|
+
.command("logs")
|
|
461
|
+
.description("Show logs for a local tunnel session")
|
|
462
|
+
.argument("[tunnelId]", "Tunnel ID (defaults to the only active tunnel)")
|
|
463
|
+
.option("--lines <n>", "Number of lines to show", "200")
|
|
464
|
+
.option("--follow", "Follow log output (like tail -f)")
|
|
465
|
+
.option("--json", "Output JSON")
|
|
466
|
+
.action(async (tunnelId, opts) => {
|
|
325
467
|
try {
|
|
326
|
-
const
|
|
327
|
-
await
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
opts.accessToken ??
|
|
333
|
-
opts.controllerAccessToken,
|
|
334
|
-
port,
|
|
335
|
-
ratholeBin: opts.ratholeBin,
|
|
468
|
+
const lines = typeof opts.lines === "string" ? Number(opts.lines) : undefined;
|
|
469
|
+
await tailTunnelLogs({
|
|
470
|
+
tunnelId,
|
|
471
|
+
lines,
|
|
472
|
+
follow: Boolean(opts.follow),
|
|
473
|
+
json: Boolean(opts.json),
|
|
336
474
|
});
|
|
337
475
|
}
|
|
338
476
|
catch (error) {
|
|
@@ -340,8 +478,12 @@ tunnelCommand
|
|
|
340
478
|
process.exit(1);
|
|
341
479
|
}
|
|
342
480
|
});
|
|
343
|
-
const
|
|
344
|
-
.command("org
|
|
481
|
+
const orgCommand = program
|
|
482
|
+
.command("org")
|
|
483
|
+
.description("Organization utilities");
|
|
484
|
+
orgCommand.action(() => orgCommand.outputHelp());
|
|
485
|
+
const orgListCommand = orgCommand
|
|
486
|
+
.command("list")
|
|
345
487
|
.description("List organizations for your account");
|
|
346
488
|
addServerUrlOptions(orgListCommand);
|
|
347
489
|
orgListCommand
|
|
@@ -360,24 +502,42 @@ orgListCommand
|
|
|
360
502
|
process.exit(1);
|
|
361
503
|
}
|
|
362
504
|
});
|
|
363
|
-
const
|
|
364
|
-
.command("
|
|
365
|
-
.description("
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
.
|
|
370
|
-
.option("--org-slug <slug>", "Filter by organization slug")
|
|
505
|
+
const profileCommand = program
|
|
506
|
+
.command("profile")
|
|
507
|
+
.description("Manage saved CLI profiles (~/.instafy/profiles)");
|
|
508
|
+
profileCommand.action(() => profileCommand.outputHelp());
|
|
509
|
+
profileCommand
|
|
510
|
+
.command("list")
|
|
511
|
+
.description("List saved CLI profiles")
|
|
371
512
|
.option("--json", "Output JSON")
|
|
372
513
|
.action(async (opts) => {
|
|
373
514
|
try {
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
515
|
+
const names = listInstafyProfileNames();
|
|
516
|
+
const profiles = names.map((name) => {
|
|
517
|
+
const config = readInstafyProfileConfig(name);
|
|
518
|
+
return {
|
|
519
|
+
name,
|
|
520
|
+
path: getInstafyProfileConfigPath(name),
|
|
521
|
+
controllerUrl: config.controllerUrl ?? null,
|
|
522
|
+
studioUrl: config.studioUrl ?? null,
|
|
523
|
+
accessTokenSet: Boolean(config.accessToken),
|
|
524
|
+
updatedAt: config.updatedAt ?? null,
|
|
525
|
+
};
|
|
380
526
|
});
|
|
527
|
+
if (opts.json) {
|
|
528
|
+
console.log(JSON.stringify({ profiles }, null, 2));
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
if (profiles.length === 0) {
|
|
532
|
+
console.log(kleur.yellow("No profiles found."));
|
|
533
|
+
console.log(`Create one with: ${kleur.cyan("instafy login --profile <name>")}`);
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
console.log(kleur.green("Instafy CLI profiles"));
|
|
537
|
+
for (const profile of profiles) {
|
|
538
|
+
const token = profile.accessTokenSet ? kleur.green("token") : kleur.yellow("no-token");
|
|
539
|
+
console.log(`- ${kleur.cyan(profile.name)} (${token})`);
|
|
540
|
+
}
|
|
381
541
|
}
|
|
382
542
|
catch (error) {
|
|
383
543
|
console.error(kleur.red(String(error)));
|
|
@@ -415,23 +575,27 @@ function configureApiCommand(command, method) {
|
|
|
415
575
|
}
|
|
416
576
|
});
|
|
417
577
|
}
|
|
418
|
-
const
|
|
419
|
-
.command("api:
|
|
578
|
+
const apiCommand = program
|
|
579
|
+
.command("api", { hidden: true })
|
|
580
|
+
.description("Advanced: authenticated requests to the controller API");
|
|
581
|
+
apiCommand.action(() => apiCommand.outputHelp());
|
|
582
|
+
const apiGetCommand = apiCommand
|
|
583
|
+
.command("get")
|
|
420
584
|
.description("Advanced: authenticated GET request to the controller API")
|
|
421
585
|
.argument("<path>", "API path (or full URL), e.g. /conversations/<id>/messages?limit=50");
|
|
422
586
|
configureApiCommand(apiGetCommand, "GET");
|
|
423
|
-
const apiPostCommand =
|
|
424
|
-
.command("
|
|
587
|
+
const apiPostCommand = apiCommand
|
|
588
|
+
.command("post")
|
|
425
589
|
.description("Advanced: authenticated POST request to the controller API")
|
|
426
590
|
.argument("<path>", "API path (or full URL)");
|
|
427
591
|
configureApiCommand(apiPostCommand, "POST");
|
|
428
|
-
const apiPatchCommand =
|
|
429
|
-
.command("
|
|
592
|
+
const apiPatchCommand = apiCommand
|
|
593
|
+
.command("patch")
|
|
430
594
|
.description("Advanced: authenticated PATCH request to the controller API")
|
|
431
595
|
.argument("<path>", "API path (or full URL)");
|
|
432
596
|
configureApiCommand(apiPatchCommand, "PATCH");
|
|
433
|
-
const apiDeleteCommand =
|
|
434
|
-
.command("
|
|
597
|
+
const apiDeleteCommand = apiCommand
|
|
598
|
+
.command("delete")
|
|
435
599
|
.description("Advanced: authenticated DELETE request to the controller API")
|
|
436
600
|
.argument("<path>", "API path (or full URL)");
|
|
437
601
|
configureApiCommand(apiDeleteCommand, "DELETE");
|
|
@@ -440,6 +604,27 @@ export async function runCli(argv = process.argv) {
|
|
|
440
604
|
program.outputHelp();
|
|
441
605
|
return;
|
|
442
606
|
}
|
|
607
|
+
const args = argv.slice(2);
|
|
608
|
+
if (args[0] === "git") {
|
|
609
|
+
if (args[1] === "credential") {
|
|
610
|
+
try {
|
|
611
|
+
await runGitCredentialHelper(args[2] ?? "");
|
|
612
|
+
}
|
|
613
|
+
catch (error) {
|
|
614
|
+
console.error(String(error));
|
|
615
|
+
process.exitCode = 1;
|
|
616
|
+
}
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
if (args[1] === "sync") {
|
|
620
|
+
const code = await runInstafyGitSync(args.slice(2));
|
|
621
|
+
process.exitCode = code;
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
const code = runInstafyGit(args.slice(1));
|
|
625
|
+
process.exitCode = code;
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
443
628
|
await program.parseAsync(argv);
|
|
444
629
|
}
|
|
445
630
|
if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
|
package/dist/org.js
CHANGED
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
import kleur from "kleur";
|
|
2
2
|
import { resolveControllerUrl, resolveUserAccessToken } from "./config.js";
|
|
3
|
-
|
|
4
|
-
return new Error("Login required. Run `instafy login` (recommended) or pass --access-token / set SUPABASE_ACCESS_TOKEN.");
|
|
5
|
-
}
|
|
3
|
+
import { formatAuthRejectedError, formatAuthRequiredError } from "./errors.js";
|
|
6
4
|
export async function listOrganizations(params) {
|
|
7
5
|
const controllerUrl = resolveControllerUrl({ controllerUrl: params.controllerUrl ?? null });
|
|
8
6
|
const token = resolveUserAccessToken({ accessToken: params.accessToken ?? null });
|
|
9
7
|
if (!token) {
|
|
10
|
-
throw formatAuthRequiredError();
|
|
8
|
+
throw formatAuthRequiredError({ retryCommand: "instafy org list" });
|
|
11
9
|
}
|
|
12
10
|
const response = await fetch(`${controllerUrl}/orgs`, {
|
|
13
11
|
headers: { authorization: `Bearer ${token}` },
|
|
14
12
|
});
|
|
15
13
|
if (!response.ok) {
|
|
16
14
|
const text = await response.text().catch(() => "");
|
|
15
|
+
if (response.status === 401 || response.status === 403) {
|
|
16
|
+
throw formatAuthRejectedError({
|
|
17
|
+
status: response.status,
|
|
18
|
+
responseBody: text,
|
|
19
|
+
retryCommand: "instafy org list",
|
|
20
|
+
});
|
|
21
|
+
}
|
|
17
22
|
throw new Error(`Organization list failed (${response.status} ${response.statusText}): ${text}`);
|
|
18
23
|
}
|
|
19
24
|
const body = (await response.json());
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export function findProjectManifest(startDir) {
|
|
4
|
+
let current = path.resolve(startDir);
|
|
5
|
+
const root = path.parse(current).root;
|
|
6
|
+
while (true) {
|
|
7
|
+
const candidate = path.join(current, ".instafy", "project.json");
|
|
8
|
+
if (existsSync(candidate)) {
|
|
9
|
+
try {
|
|
10
|
+
const parsed = JSON.parse(readFileSync(candidate, "utf8"));
|
|
11
|
+
if (parsed?.projectId) {
|
|
12
|
+
return { manifest: parsed, path: candidate };
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
// ignore malformed manifest
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
if (current === root)
|
|
20
|
+
break;
|
|
21
|
+
current = path.dirname(current);
|
|
22
|
+
}
|
|
23
|
+
return { manifest: null, path: null };
|
|
24
|
+
}
|