@secondlayer/cli 3.0.0 → 3.1.1
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 +128 -0
- package/dist/cli.js +62 -173
- package/dist/cli.js.map +7 -8
- package/package.json +5 -4
package/README.md
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# @secondlayer/cli
|
|
2
|
+
|
|
3
|
+
The Secondlayer CLI — one binary for dedicated Stacks indexing, real-time
|
|
4
|
+
subgraphs, and per-tenant hosting lifecycle.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
bun add -g @secondlayer/cli
|
|
8
|
+
sl --version
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quickstart
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
sl login # magic-link auth, session cached at ~/.secondlayer/session.json
|
|
15
|
+
sl project create my-app # scaffold a project
|
|
16
|
+
sl project use my-app # bind cwd to the project (writes ./.secondlayer/project)
|
|
17
|
+
sl instance create --plan launch # provision dedicated Postgres + API + processor
|
|
18
|
+
sl subgraphs deploy ./x.ts # deploy to your instance
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Command surface
|
|
22
|
+
|
|
23
|
+
### Auth (top-level)
|
|
24
|
+
|
|
25
|
+
| Command | What it does |
|
|
26
|
+
|---|---|
|
|
27
|
+
| `sl login` | Magic-link email → 6-digit code → writes session to `~/.secondlayer/session.json` |
|
|
28
|
+
| `sl logout` | Revokes the session and clears the local file |
|
|
29
|
+
| `sl whoami` | Prints account, active project, instance URL, trial days left |
|
|
30
|
+
|
|
31
|
+
### Project
|
|
32
|
+
|
|
33
|
+
Projects are the unit that binds a working directory to a dedicated instance.
|
|
34
|
+
Binding is **per-directory** — `.secondlayer/project` in cwd takes precedence
|
|
35
|
+
over the global default at `~/.secondlayer/config.json:defaultProject`. The
|
|
36
|
+
walk-up stops at `.git` (never crosses repos).
|
|
37
|
+
|
|
38
|
+
| Command | What it does |
|
|
39
|
+
|---|---|
|
|
40
|
+
| `sl project create [name]` | Scaffold a new project on the platform |
|
|
41
|
+
| `sl project list` | List all projects for the account |
|
|
42
|
+
| `sl project use <slug>` | Write `./.secondlayer/project` — binds cwd to that project |
|
|
43
|
+
| `sl project current` | Prints the resolved slug + the file it was read from |
|
|
44
|
+
|
|
45
|
+
### Instance (dedicated hosting)
|
|
46
|
+
|
|
47
|
+
One instance per project. The platform API spawns a dedicated `sl-pg-{slug}`,
|
|
48
|
+
`sl-api-{slug}`, and `sl-proc-{slug}` container set on the hosting side.
|
|
49
|
+
|
|
50
|
+
| Command | What it does |
|
|
51
|
+
|---|---|
|
|
52
|
+
| `sl instance create --plan <launch\|grow\|scale>` | Provision containers. Boxed reveal of `serviceKey` + `anonKey` (shown once). |
|
|
53
|
+
| `sl instance info` | Plan, status, resource usage, trial days left |
|
|
54
|
+
| `sl instance resize --plan <...>` | Recreate containers with new CPU/memory (~30s downtime) |
|
|
55
|
+
| `sl instance suspend` / `resume` | Stop/start containers, volume preserved |
|
|
56
|
+
| `sl instance keys rotate [--service\|--anon\|--both]` | Bump JWT gen, recreate API container, mint replacement key(s) |
|
|
57
|
+
| `sl instance delete` | Typed-slug confirm, hard teardown |
|
|
58
|
+
| `sl instance db` | Print `ssh -L` command + `DATABASE_URL` for tunneled Postgres access |
|
|
59
|
+
| `sl instance db add-key <path>` | Upload an SSH pubkey to the bastion |
|
|
60
|
+
| `sl instance db revoke-key` | Revoke your bastion access |
|
|
61
|
+
|
|
62
|
+
### Subgraphs (tenant-scoped)
|
|
63
|
+
|
|
64
|
+
All tenant-scoped commands auto-mint a 5-minute ephemeral service JWT per
|
|
65
|
+
invocation. No long-lived key on disk.
|
|
66
|
+
|
|
67
|
+
| Command | What it does |
|
|
68
|
+
|---|---|
|
|
69
|
+
| `sl subgraphs new <name>` | Scaffold a subgraph definition file |
|
|
70
|
+
| `sl subgraphs deploy <file>` | Deploy to the active instance |
|
|
71
|
+
| `sl subgraphs dev <file>` | Watch + hot-redeploy |
|
|
72
|
+
| `sl subgraphs list` | List deployed subgraphs |
|
|
73
|
+
| `sl subgraphs status <name>` | Indexing progress, row counts, gaps |
|
|
74
|
+
| `sl subgraphs query <name> <table>` | Query a subgraph table with filters, sort, pagination |
|
|
75
|
+
| `sl subgraphs reindex <name>` | Drop + re-process from the tip backwards |
|
|
76
|
+
| `sl subgraphs backfill <name>` | Fill a specific block range |
|
|
77
|
+
| `sl subgraphs stop <name>` | Pause processing |
|
|
78
|
+
| `sl subgraphs gaps <name>` | List missing block ranges |
|
|
79
|
+
| `sl subgraphs delete <name>` | Drop the subgraph + its schema |
|
|
80
|
+
| `sl subgraphs scaffold <SP...::contract>` | Generate a starter subgraph from a deployed contract |
|
|
81
|
+
| `sl subgraphs generate <name>` | Regenerate TS types for an existing subgraph |
|
|
82
|
+
|
|
83
|
+
### Local dev + OSS
|
|
84
|
+
|
|
85
|
+
| Command | What it does |
|
|
86
|
+
|---|---|
|
|
87
|
+
| `sl local start/stop/restart/status/logs` | Manage the local Docker stack |
|
|
88
|
+
| `sl local node setup/start/stop/...` | Manage the local Stacks node |
|
|
89
|
+
| `sl stack start/stop/restart` | Alias for `sl local` |
|
|
90
|
+
| `sl db blocks/txs/events/gaps/reset/resync` | Inspect the local source DB |
|
|
91
|
+
|
|
92
|
+
### Other
|
|
93
|
+
|
|
94
|
+
| Command | What it does |
|
|
95
|
+
|---|---|
|
|
96
|
+
| `sl generate [files...]` (aliases: `gen`, `codegen`) | Generate TS interfaces from Clarity contracts |
|
|
97
|
+
| `sl init` | Scaffold `secondlayer.config.ts` |
|
|
98
|
+
| `sl doctor` | Session + project + instance reachability + trial status check |
|
|
99
|
+
| `sl status` | Platform/instance health |
|
|
100
|
+
| `sl account profile` | Update display name / bio / slug |
|
|
101
|
+
| `sl config show/set/reset/clear` | Inspect or reset local config |
|
|
102
|
+
|
|
103
|
+
## Environment variables
|
|
104
|
+
|
|
105
|
+
| Var | Purpose |
|
|
106
|
+
|---|---|
|
|
107
|
+
| `SL_API_URL` | Bypass platform resolution — point at an OSS or internal API directly |
|
|
108
|
+
| `SL_SERVICE_KEY` | Service key when using env-var bypass |
|
|
109
|
+
| `SL_PLATFORM_API_URL` | Override the platform API base (default `https://api.secondlayer.tools`) |
|
|
110
|
+
| `STACKS_NETWORK` | Override via `--network <local\|testnet\|mainnet>` |
|
|
111
|
+
| `HIRO_API_KEY` | Used by `sl generate` for remote contract fetches |
|
|
112
|
+
|
|
113
|
+
## Error codes
|
|
114
|
+
|
|
115
|
+
Every tenant-scoped failure surfaces a typed code and an action hint:
|
|
116
|
+
|
|
117
|
+
| Code | CLI hint |
|
|
118
|
+
|---|---|
|
|
119
|
+
| `SESSION_EXPIRED` | `Session expired. Run: sl login` |
|
|
120
|
+
| `TRIAL_EXPIRED` | `Your trial expired. Run: sl instance resize --plan <...> and add payment` |
|
|
121
|
+
| `TENANT_SUSPENDED` | `Instance is suspended. Run: sl instance resume` |
|
|
122
|
+
| `NO_ACTIVE_PROJECT` | `No project selected. Run: sl project use <slug>` |
|
|
123
|
+
| `NO_TENANT_FOR_PROJECT` | `Project has no instance. Run: sl instance create --plan launch` |
|
|
124
|
+
| `KEY_ROTATED` | Handled transparently — `http.ts` re-mints and retries once |
|
|
125
|
+
|
|
126
|
+
## License
|
|
127
|
+
|
|
128
|
+
MIT
|
package/dist/cli.js
CHANGED
|
@@ -32410,7 +32410,7 @@ var {
|
|
|
32410
32410
|
// package.json
|
|
32411
32411
|
var package_default = {
|
|
32412
32412
|
name: "@secondlayer/cli",
|
|
32413
|
-
version: "3.
|
|
32413
|
+
version: "3.1.1",
|
|
32414
32414
|
description: "CLI for subgraphs and blockchain indexing on Stacks",
|
|
32415
32415
|
type: "module",
|
|
32416
32416
|
bin: {
|
|
@@ -32431,7 +32431,8 @@ var package_default = {
|
|
|
32431
32431
|
},
|
|
32432
32432
|
files: [
|
|
32433
32433
|
"dist",
|
|
32434
|
-
"templates"
|
|
32434
|
+
"templates",
|
|
32435
|
+
"README.md"
|
|
32435
32436
|
],
|
|
32436
32437
|
scripts: {
|
|
32437
32438
|
build: "bunup",
|
|
@@ -32452,8 +32453,8 @@ var package_default = {
|
|
|
32452
32453
|
dependencies: {
|
|
32453
32454
|
"@inquirer/prompts": "^8.2.0",
|
|
32454
32455
|
"@secondlayer/bundler": "^0.3.0",
|
|
32455
|
-
"@secondlayer/sdk": "^
|
|
32456
|
-
"@secondlayer/shared": "^2.
|
|
32456
|
+
"@secondlayer/sdk": "^2.0.0",
|
|
32457
|
+
"@secondlayer/shared": "^2.1.0",
|
|
32457
32458
|
"@secondlayer/stacks": "^0.3.0",
|
|
32458
32459
|
"@secondlayer/subgraphs": "^0.11.8",
|
|
32459
32460
|
"@secondlayer/workflows": "^1.1.0",
|
|
@@ -32521,20 +32522,6 @@ function withErrorHandling(fn, options) {
|
|
|
32521
32522
|
}
|
|
32522
32523
|
};
|
|
32523
32524
|
}
|
|
32524
|
-
async function assertOk(res) {
|
|
32525
|
-
if (res.ok)
|
|
32526
|
-
return;
|
|
32527
|
-
const body = await res.text();
|
|
32528
|
-
try {
|
|
32529
|
-
const parsed = JSON.parse(body);
|
|
32530
|
-
if (typeof parsed.error === "string" && parsed.error)
|
|
32531
|
-
throw new Error(parsed.error);
|
|
32532
|
-
} catch (e) {
|
|
32533
|
-
if (e instanceof Error && e.message !== body)
|
|
32534
|
-
throw e;
|
|
32535
|
-
}
|
|
32536
|
-
throw new Error(`HTTP ${res.status}`);
|
|
32537
|
-
}
|
|
32538
32525
|
async function getTenantClient() {
|
|
32539
32526
|
const { apiUrl, ephemeralKey } = await resolveActiveTenant();
|
|
32540
32527
|
return new SecondLayer({ baseUrl: apiUrl, apiKey: ephemeralKey });
|
|
@@ -32569,51 +32556,12 @@ async function querySubgraphTableCount(name, table, params = {}) {
|
|
|
32569
32556
|
async function getSubgraphGaps(name, opts) {
|
|
32570
32557
|
return (await getTenantClient()).subgraphs.gaps(name, opts);
|
|
32571
32558
|
}
|
|
32572
|
-
async function publishSubgraphApi(name, opts) {
|
|
32573
|
-
const { apiUrl, ephemeralKey } = await resolveActiveTenant();
|
|
32574
|
-
const res = await fetch(`${apiUrl}/api/subgraphs/${name}/publish`, {
|
|
32575
|
-
method: "POST",
|
|
32576
|
-
headers: {
|
|
32577
|
-
"content-type": "application/json",
|
|
32578
|
-
authorization: `Bearer ${ephemeralKey}`
|
|
32579
|
-
},
|
|
32580
|
-
body: JSON.stringify(opts ?? {})
|
|
32581
|
-
});
|
|
32582
|
-
await assertOk(res);
|
|
32583
|
-
return res.json();
|
|
32584
|
-
}
|
|
32585
|
-
async function unpublishSubgraphApi(name) {
|
|
32586
|
-
const { apiUrl, ephemeralKey } = await resolveActiveTenant();
|
|
32587
|
-
const res = await fetch(`${apiUrl}/api/subgraphs/${name}/unpublish`, {
|
|
32588
|
-
method: "POST",
|
|
32589
|
-
headers: {
|
|
32590
|
-
"content-type": "application/json",
|
|
32591
|
-
authorization: `Bearer ${ephemeralKey}`
|
|
32592
|
-
},
|
|
32593
|
-
body: JSON.stringify({})
|
|
32594
|
-
});
|
|
32595
|
-
await assertOk(res);
|
|
32596
|
-
return res.json();
|
|
32597
|
-
}
|
|
32598
32559
|
async function getAccountProfile() {
|
|
32599
32560
|
return httpPlatform("/api/accounts/me");
|
|
32600
32561
|
}
|
|
32601
32562
|
async function updateAccountProfile(data) {
|
|
32602
32563
|
return httpPlatform("/api/accounts/me", { method: "PATCH", body: data });
|
|
32603
32564
|
}
|
|
32604
|
-
async function getPlatformClient() {
|
|
32605
|
-
const base = process.env.SL_PLATFORM_API_URL ?? "https://api.secondlayer.tools";
|
|
32606
|
-
return new SecondLayer({ baseUrl: base });
|
|
32607
|
-
}
|
|
32608
|
-
async function browseMarketplace(opts = {}) {
|
|
32609
|
-
return (await getPlatformClient()).marketplace.browse(opts);
|
|
32610
|
-
}
|
|
32611
|
-
async function getMarketplaceSubgraph(name) {
|
|
32612
|
-
return (await getPlatformClient()).marketplace.get(name);
|
|
32613
|
-
}
|
|
32614
|
-
async function forkMarketplaceSubgraph(name, newName) {
|
|
32615
|
-
return (await getPlatformClient()).marketplace.fork(name, newName);
|
|
32616
|
-
}
|
|
32617
32565
|
|
|
32618
32566
|
// src/commands/account.ts
|
|
32619
32567
|
init_output();
|
|
@@ -34103,37 +34051,6 @@ ${rows.length} row(s)`));
|
|
|
34103
34051
|
handleApiError(err, "delete subgraph");
|
|
34104
34052
|
}
|
|
34105
34053
|
});
|
|
34106
|
-
subgraphs.command("publish <name>").description("Publish a subgraph to the marketplace").option("--tags <tags>", "Tags (comma-separated, max 5)").option("--description <desc>", "Public description (max 500 chars)").option("--json", "Output as JSON").action(async (name, options2) => {
|
|
34107
|
-
try {
|
|
34108
|
-
const opts = {};
|
|
34109
|
-
if (options2.tags) {
|
|
34110
|
-
opts.tags = options2.tags.split(",").map((t) => t.trim());
|
|
34111
|
-
}
|
|
34112
|
-
if (options2.description) {
|
|
34113
|
-
opts.description = options2.description;
|
|
34114
|
-
}
|
|
34115
|
-
const result = await publishSubgraphApi(name, opts);
|
|
34116
|
-
if (options2.json) {
|
|
34117
|
-
console.log(JSON.stringify(result, null, 2));
|
|
34118
|
-
return;
|
|
34119
|
-
}
|
|
34120
|
-
success(result.message);
|
|
34121
|
-
} catch (err) {
|
|
34122
|
-
handleApiError(err, "publish subgraph");
|
|
34123
|
-
}
|
|
34124
|
-
});
|
|
34125
|
-
subgraphs.command("unpublish <name>").description("Remove a subgraph from the marketplace").option("--json", "Output as JSON").action(async (name, options2) => {
|
|
34126
|
-
try {
|
|
34127
|
-
const result = await unpublishSubgraphApi(name);
|
|
34128
|
-
if (options2.json) {
|
|
34129
|
-
console.log(JSON.stringify(result, null, 2));
|
|
34130
|
-
return;
|
|
34131
|
-
}
|
|
34132
|
-
success(result.message);
|
|
34133
|
-
} catch (err) {
|
|
34134
|
-
handleApiError(err, "unpublish subgraph");
|
|
34135
|
-
}
|
|
34136
|
-
});
|
|
34137
34054
|
subgraphs.command("scaffold <contractAddress>").description("Scaffold a defineSubgraph() file from a contract ABI").option("-o, --output <path>", "Output file path (required)").option("--api-key <key>", "Hiro API key").action(async (contractAddress, options2) => {
|
|
34138
34055
|
try {
|
|
34139
34056
|
if (!options2.output) {
|
|
@@ -34981,89 +34898,6 @@ function formatLogLine3(service, line) {
|
|
|
34981
34898
|
}
|
|
34982
34899
|
return `${prefix} ${line}`;
|
|
34983
34900
|
}
|
|
34984
|
-
// src/commands/marketplace.ts
|
|
34985
|
-
init_output();
|
|
34986
|
-
function registerMarketplaceCommand(program2) {
|
|
34987
|
-
const marketplace = program2.command("marketplace").alias("mp").description("Browse the public subgraph marketplace");
|
|
34988
|
-
marketplace.command("browse").description("List public subgraphs").option("--tags <tags>", "Filter by tags (comma-separated)").option("--search <query>", "Search by name or description").option("--sort <field>", "Sort by: recent, popular, name", "recent").option("--limit <n>", "Max results", "20").option("--json", "Output as JSON").action(async (options2) => {
|
|
34989
|
-
try {
|
|
34990
|
-
const result = await browseMarketplace({
|
|
34991
|
-
tags: options2.tags ? options2.tags.split(",").map((t) => t.trim()) : undefined,
|
|
34992
|
-
search: options2.search,
|
|
34993
|
-
sort: options2.sort,
|
|
34994
|
-
limit: Number.parseInt(options2.limit ?? "20", 10)
|
|
34995
|
-
});
|
|
34996
|
-
if (options2.json) {
|
|
34997
|
-
console.log(JSON.stringify(result, null, 2));
|
|
34998
|
-
return;
|
|
34999
|
-
}
|
|
35000
|
-
if (result.data.length === 0) {
|
|
35001
|
-
console.log(dim("No public subgraphs found"));
|
|
35002
|
-
return;
|
|
35003
|
-
}
|
|
35004
|
-
const rows = result.data.map((s) => [
|
|
35005
|
-
s.name,
|
|
35006
|
-
s.creator?.displayName ?? s.creator?.slug ?? dim("—"),
|
|
35007
|
-
(s.tags ?? []).join(", ") || dim("—"),
|
|
35008
|
-
s.status
|
|
35009
|
-
]);
|
|
35010
|
-
console.log(formatTable(["Name", "Creator", "Tags", "Status"], rows));
|
|
35011
|
-
console.log(dim(`
|
|
35012
|
-
${result.meta.total} subgraph(s) total`));
|
|
35013
|
-
} catch (err) {
|
|
35014
|
-
handleApiError(err, "browse marketplace");
|
|
35015
|
-
}
|
|
35016
|
-
});
|
|
35017
|
-
marketplace.command("view <name>").description("View a public subgraph's details").option("--json", "Output as JSON").action(async (name, options2) => {
|
|
35018
|
-
try {
|
|
35019
|
-
const detail = await getMarketplaceSubgraph(name);
|
|
35020
|
-
if (options2.json) {
|
|
35021
|
-
console.log(JSON.stringify(detail, null, 2));
|
|
35022
|
-
return;
|
|
35023
|
-
}
|
|
35024
|
-
console.log(formatKeyValue([
|
|
35025
|
-
["Name", detail.name],
|
|
35026
|
-
["Description", detail.description ?? dim("—")],
|
|
35027
|
-
[
|
|
35028
|
-
"Creator",
|
|
35029
|
-
detail.creator?.displayName ?? detail.creator?.slug ?? dim("—")
|
|
35030
|
-
],
|
|
35031
|
-
["Status", detail.status],
|
|
35032
|
-
["Version", detail.version],
|
|
35033
|
-
["Tags", (detail.tags ?? []).join(", ") || dim("—")],
|
|
35034
|
-
["Tables", detail.tables?.join(", ") ?? dim("—")],
|
|
35035
|
-
["Start Block", String(detail.startBlock)],
|
|
35036
|
-
["Last Processed", String(detail.lastProcessedBlock)],
|
|
35037
|
-
["Queries (7d)", String(detail.usage?.totalQueries7d ?? 0)],
|
|
35038
|
-
["Queries (30d)", String(detail.usage?.totalQueries30d ?? 0)],
|
|
35039
|
-
["Created", detail.createdAt]
|
|
35040
|
-
]));
|
|
35041
|
-
if (detail.tableSchemas) {
|
|
35042
|
-
console.log(dim(`
|
|
35043
|
-
Endpoints:`));
|
|
35044
|
-
for (const [table, schema] of Object.entries(detail.tableSchemas)) {
|
|
35045
|
-
console.log(` ${cyan(table)} — ${schema.rowCount} rows — ${schema.endpoint}`);
|
|
35046
|
-
}
|
|
35047
|
-
}
|
|
35048
|
-
} catch (err) {
|
|
35049
|
-
handleApiError(err, "view subgraph");
|
|
35050
|
-
}
|
|
35051
|
-
});
|
|
35052
|
-
marketplace.command("fork <name>").description("Fork a public subgraph into your account").option("--name <newName>", "Name for the forked subgraph").option("--json", "Output as JSON").action(async (name, options2) => {
|
|
35053
|
-
try {
|
|
35054
|
-
const result = await forkMarketplaceSubgraph(name, options2.name);
|
|
35055
|
-
if (options2.json) {
|
|
35056
|
-
console.log(JSON.stringify(result, null, 2));
|
|
35057
|
-
return;
|
|
35058
|
-
}
|
|
35059
|
-
success(`Forked ${result.forkedFrom} as ${result.name}`);
|
|
35060
|
-
console.log(dim(`Subgraph ID: ${result.subgraphId}`));
|
|
35061
|
-
console.log(dim("Indexing will start automatically from the source's start block."));
|
|
35062
|
-
} catch (err) {
|
|
35063
|
-
handleApiError(err, "fork subgraph");
|
|
35064
|
-
}
|
|
35065
|
-
});
|
|
35066
|
-
}
|
|
35067
34901
|
// src/commands/whoami.ts
|
|
35068
34902
|
init_config();
|
|
35069
34903
|
init_http();
|
|
@@ -35355,6 +35189,62 @@ function registerInstanceCommand(program2) {
|
|
|
35355
35189
|
handleInstanceError(err, "rotate keys");
|
|
35356
35190
|
}
|
|
35357
35191
|
});
|
|
35192
|
+
const db = instance.command("db").description("Get a DATABASE_URL for direct Postgres access (via SSH tunnel)");
|
|
35193
|
+
db.command("info", { isDefault: true }).description("Print SSH tunnel command + DATABASE_URL for the instance").action(async () => {
|
|
35194
|
+
guardOssMode();
|
|
35195
|
+
try {
|
|
35196
|
+
const res = await httpPlatform("/api/tenants/me/db-access");
|
|
35197
|
+
console.log("");
|
|
35198
|
+
console.log(dim("1. Upload your public key (one time):"));
|
|
35199
|
+
console.log(dim(" sl instance db add-key ~/.ssh/id_ed25519.pub"));
|
|
35200
|
+
console.log("");
|
|
35201
|
+
console.log(dim("2. Open the SSH tunnel in a separate terminal:"));
|
|
35202
|
+
console.log(green(` ${res.sshCommand}`));
|
|
35203
|
+
console.log("");
|
|
35204
|
+
console.log(dim("3. Use this DATABASE_URL while the tunnel is open:"));
|
|
35205
|
+
console.log(green(` ${res.databaseUrl}`));
|
|
35206
|
+
console.log("");
|
|
35207
|
+
} catch (err) {
|
|
35208
|
+
handleInstanceError(err, "fetch db access info");
|
|
35209
|
+
}
|
|
35210
|
+
});
|
|
35211
|
+
db.command("add-key <path>").description("Upload an SSH public key to the bastion for this instance").action(async (path) => {
|
|
35212
|
+
guardOssMode();
|
|
35213
|
+
let publicKey;
|
|
35214
|
+
try {
|
|
35215
|
+
publicKey = (await Bun.file(path).text()).trim();
|
|
35216
|
+
} catch (err) {
|
|
35217
|
+
error(`Could not read ${path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
35218
|
+
process.exit(1);
|
|
35219
|
+
}
|
|
35220
|
+
if (!publicKey) {
|
|
35221
|
+
error(`${path} is empty`);
|
|
35222
|
+
process.exit(1);
|
|
35223
|
+
}
|
|
35224
|
+
try {
|
|
35225
|
+
await httpPlatform("/api/tenants/me/db-access/key", { method: "POST", body: { publicKey } });
|
|
35226
|
+
success("Bastion key installed. You can now open the SSH tunnel.");
|
|
35227
|
+
} catch (err) {
|
|
35228
|
+
handleInstanceError(err, "upload bastion key");
|
|
35229
|
+
}
|
|
35230
|
+
});
|
|
35231
|
+
db.command("revoke-key").description("Revoke bastion access for this instance").option("-y, --yes", "Skip confirmation").action(async (opts) => {
|
|
35232
|
+
guardOssMode();
|
|
35233
|
+
if (!opts.yes) {
|
|
35234
|
+
const ok = await confirm4({
|
|
35235
|
+
message: "Revoke bastion access for this instance?",
|
|
35236
|
+
default: false
|
|
35237
|
+
});
|
|
35238
|
+
if (!ok)
|
|
35239
|
+
return;
|
|
35240
|
+
}
|
|
35241
|
+
try {
|
|
35242
|
+
await httpPlatform("/api/tenants/me/db-access/key", { method: "DELETE" });
|
|
35243
|
+
success("Bastion access revoked.");
|
|
35244
|
+
} catch (err) {
|
|
35245
|
+
handleInstanceError(err, "revoke bastion key");
|
|
35246
|
+
}
|
|
35247
|
+
});
|
|
35358
35248
|
}
|
|
35359
35249
|
function guardOssMode() {
|
|
35360
35250
|
if (isOssMode()) {
|
|
@@ -35550,7 +35440,6 @@ registerWhoamiCommand(program);
|
|
|
35550
35440
|
registerProjectCommand(program);
|
|
35551
35441
|
registerInstanceCommand(program);
|
|
35552
35442
|
registerSubgraphsCommand(program);
|
|
35553
|
-
registerMarketplaceCommand(program);
|
|
35554
35443
|
registerStatusCommand(program);
|
|
35555
35444
|
registerStackCommand(program);
|
|
35556
35445
|
registerDbCommand(program);
|
|
@@ -35560,5 +35449,5 @@ registerLocalCommand(program);
|
|
|
35560
35449
|
registerAccountCommand(program);
|
|
35561
35450
|
program.parse();
|
|
35562
35451
|
|
|
35563
|
-
//# debugId=
|
|
35452
|
+
//# debugId=93095DDF734A3A2E64756E2164756E21
|
|
35564
35453
|
//# sourceMappingURL=cli.js.map
|