context101-cli 0.1.1 → 0.1.2
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/bin/context101.js +0 -0
- package/package.json +1 -4
- package/src/clone.js +29 -6
- package/src/config.js +2 -1
- package/src/defaults.js +1 -0
- package/src/deploy-env-load.js +5 -6
- package/src/main.js +4 -2
- package/src/parse-args.js +116 -65
- package/src/stacks.js +20 -13
package/bin/context101.js
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "context101-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Context101 self-host CLI. Use `npx context101-cli` or the `context101` bin. The public npm package named context101 is Context7's MCP, not this tool.",
|
|
6
6
|
"type": "module",
|
|
@@ -19,8 +19,5 @@
|
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@inquirer/prompts": "^7.8.4"
|
|
22
|
-
},
|
|
23
|
-
"publishConfig": {
|
|
24
|
-
"access": "public"
|
|
25
22
|
}
|
|
26
23
|
}
|
package/src/clone.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, mkdirSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
2
3
|
import path from "node:path";
|
|
3
|
-
import { DEFAULT_AMPLIFY_REPO } from "./defaults.js";
|
|
4
|
+
import { DEFAULT_AMPLIFY_REPO, HOME_SRC_REL } from "./defaults.js";
|
|
4
5
|
import { findRepoRoot } from "./repo.js";
|
|
5
6
|
|
|
6
7
|
export const CLONE_URL = DEFAULT_AMPLIFY_REPO;
|
|
@@ -10,6 +11,10 @@ export function resolveCloneDir(cwd, dir) {
|
|
|
10
11
|
return path.resolve(cwd, dir || DEFAULT_CLONE_DIR);
|
|
11
12
|
}
|
|
12
13
|
|
|
14
|
+
export function homeSrcDir(homeDir = homedir()) {
|
|
15
|
+
return path.join(homeDir, HOME_SRC_REL);
|
|
16
|
+
}
|
|
17
|
+
|
|
13
18
|
export function ensureRepoRoot({
|
|
14
19
|
cwd,
|
|
15
20
|
dir,
|
|
@@ -17,16 +22,27 @@ export function ensureRepoRoot({
|
|
|
17
22
|
io,
|
|
18
23
|
dryRun = false,
|
|
19
24
|
exists = existsSync,
|
|
25
|
+
homeDir,
|
|
26
|
+
preferHomeClone = false,
|
|
20
27
|
} = {}) {
|
|
21
28
|
const existing = findRepoRoot(cwd, exists);
|
|
22
29
|
if (existing) return { repoRoot: existing, cloned: false };
|
|
23
30
|
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
if (
|
|
31
|
+
const localTarget = resolveCloneDir(cwd, dir);
|
|
32
|
+
const alreadyLocal = findRepoRoot(localTarget, exists);
|
|
33
|
+
if (alreadyLocal) return { repoRoot: alreadyLocal, cloned: false };
|
|
34
|
+
|
|
35
|
+
const resolvedHome = homeDir ?? homedir();
|
|
36
|
+
const homeTarget = homeSrcDir(resolvedHome);
|
|
37
|
+
if (preferHomeClone) {
|
|
38
|
+
const alreadyHome = findRepoRoot(homeTarget, exists);
|
|
39
|
+
if (alreadyHome) return { repoRoot: alreadyHome, cloned: false };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const target = preferHomeClone && !dir ? homeTarget : localTarget;
|
|
27
43
|
|
|
28
44
|
if (dryRun) {
|
|
29
|
-
io?.write?.(`Would clone ${CLONE_URL} into ${
|
|
45
|
+
io?.write?.(`Would clone ${CLONE_URL} into ${displayCloneTarget(cwd, target, resolvedHome)}`);
|
|
30
46
|
return { repoRoot: target, cloned: false, wouldClone: true };
|
|
31
47
|
}
|
|
32
48
|
|
|
@@ -58,6 +74,13 @@ export function ensureRepoRoot({
|
|
|
58
74
|
error: `cloned ${target} but it is not a Context101 checkout (needs cdk/ and web/)`,
|
|
59
75
|
};
|
|
60
76
|
}
|
|
61
|
-
io?.ok?.(`cloned into ${
|
|
77
|
+
io?.ok?.(`cloned into ${displayCloneTarget(cwd, cloned, resolvedHome)}`);
|
|
62
78
|
return { repoRoot: cloned, cloned: true };
|
|
63
79
|
}
|
|
80
|
+
|
|
81
|
+
function displayCloneTarget(cwd, target, homeDir) {
|
|
82
|
+
if (homeDir && (target === homeDir || target.startsWith(homeDir + path.sep))) {
|
|
83
|
+
return `~${target.slice(homeDir.length)}`;
|
|
84
|
+
}
|
|
85
|
+
return path.relative(cwd, target) || target;
|
|
86
|
+
}
|
package/src/config.js
CHANGED
|
@@ -52,6 +52,7 @@ export async function runConfig(opts, ctx) {
|
|
|
52
52
|
envFile: opts.envFile,
|
|
53
53
|
home: opts.home,
|
|
54
54
|
cwd: ctx.cwd,
|
|
55
|
+
homeDir: ctx.homeDir,
|
|
55
56
|
});
|
|
56
57
|
|
|
57
58
|
if (opts.configAction === "set") {
|
|
@@ -85,7 +86,7 @@ async function writeConfig(opts, { io, filePath }) {
|
|
|
85
86
|
return 1;
|
|
86
87
|
}
|
|
87
88
|
if (!filePath) {
|
|
88
|
-
io.err("no deploy-env path.
|
|
89
|
+
io.err("no deploy-env path. Pass --home or --deploy-env.");
|
|
89
90
|
return 1;
|
|
90
91
|
}
|
|
91
92
|
|
package/src/defaults.js
CHANGED
|
@@ -10,6 +10,7 @@ export const BILLING_ENABLED = "false";
|
|
|
10
10
|
export const EXAMPLE_ENV_REL = "cdk/.deploy-env.example";
|
|
11
11
|
export const REPO_ENV_REL = "cdk/.deploy-env";
|
|
12
12
|
export const HOME_ENV_REL = ".context101/deploy-env";
|
|
13
|
+
export const HOME_SRC_REL = ".context101/src";
|
|
13
14
|
export const DEPLOY_CLI = "context101 deploy";
|
|
14
15
|
export const LIST_CLI = "context101 list";
|
|
15
16
|
export const DESTROY_CLI = "context101 destroy";
|
package/src/deploy-env-load.js
CHANGED
|
@@ -39,23 +39,22 @@ export function findDeployEnvPath({
|
|
|
39
39
|
home = false,
|
|
40
40
|
cwd,
|
|
41
41
|
exists = existsSync,
|
|
42
|
+
homeDir,
|
|
42
43
|
} = {}) {
|
|
44
|
+
const resolvedHome = homeDir ?? homedir();
|
|
43
45
|
if (envFile) {
|
|
44
46
|
return path.isAbsolute(envFile)
|
|
45
47
|
? envFile
|
|
46
48
|
: path.resolve(cwd ?? repoRoot ?? process.cwd(), envFile);
|
|
47
49
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
return exists(homePath) ? homePath : homePath;
|
|
51
|
-
}
|
|
50
|
+
const homePath = path.join(resolvedHome, HOME_ENV_REL);
|
|
51
|
+
if (home) return homePath;
|
|
52
52
|
if (repoRoot) {
|
|
53
53
|
const repoPath = path.join(repoRoot, ...REPO_ENV_REL.split("/"));
|
|
54
54
|
if (exists(repoPath)) return repoPath;
|
|
55
55
|
}
|
|
56
|
-
const homePath = path.join(homedir(), HOME_ENV_REL);
|
|
57
56
|
if (exists(homePath)) return homePath;
|
|
58
|
-
return repoRoot ? path.join(repoRoot, ...REPO_ENV_REL.split("/")) :
|
|
57
|
+
return repoRoot ? path.join(repoRoot, ...REPO_ENV_REL.split("/")) : homePath;
|
|
59
58
|
}
|
|
60
59
|
|
|
61
60
|
export function readDeployEnvFile(filePath, { readFile = readFileSync, exists = existsSync } = {}) {
|
package/src/main.js
CHANGED
|
@@ -19,8 +19,10 @@ export async function main(argv, ctx) {
|
|
|
19
19
|
throw error;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
if (opts.help) {
|
|
23
|
-
|
|
22
|
+
if (opts.help || opts.command === "help") {
|
|
23
|
+
const topic =
|
|
24
|
+
opts.helpTopic ?? (opts.command !== "help" ? opts.command : null);
|
|
25
|
+
io.write(helpText(topic));
|
|
24
26
|
return 0;
|
|
25
27
|
}
|
|
26
28
|
|
package/src/parse-args.js
CHANGED
|
@@ -1,85 +1,90 @@
|
|
|
1
1
|
import { DRIVER_NEON, DRIVER_POSTGRES } from "./defaults.js";
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
context101 deploy
|
|
20
|
-
npx context101-cli init
|
|
21
|
-
npx context101-cli deploy
|
|
22
|
-
|
|
23
|
-
--dry-run print the plan; write nothing, deploy nothing
|
|
3
|
+
const COMMAND_LINES = [
|
|
4
|
+
["init", "write deploy-env (default); TTY asks to deploy"],
|
|
5
|
+
["deploy", "deploy the AWS stack"],
|
|
6
|
+
["diff", "cdk diff with the same context"],
|
|
7
|
+
["synth", "cdk synth with the same context"],
|
|
8
|
+
["list", "list Context101 CloudFormation stacks"],
|
|
9
|
+
["destroy", "tear down a listed stack (name required)"],
|
|
10
|
+
["config", "show deploy-env keys (values redacted)"],
|
|
11
|
+
["config set", "write one key (chmod 600; value is not printed)"],
|
|
12
|
+
["help", "list commands"],
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
const TOPIC_HELP = {
|
|
16
|
+
init: `init — write deploy-env (default); TTY asks to deploy
|
|
17
|
+
|
|
18
|
+
--dry-run
|
|
24
19
|
--yes, -y accept defaults (creates RDS if no --database-url);
|
|
25
|
-
|
|
20
|
+
required --aws-profile when several exist
|
|
26
21
|
--force overwrite an existing env file
|
|
27
|
-
--dir <path> clone
|
|
22
|
+
--dir <path> clone here when not in a checkout
|
|
28
23
|
--deploy-env <path> default: <repo>/cdk/.deploy-env
|
|
29
|
-
--home
|
|
30
|
-
--database-url <url>
|
|
31
|
-
--create-rds
|
|
24
|
+
--home ~/.context101/deploy-env
|
|
25
|
+
--database-url <url>
|
|
26
|
+
--create-rds default when no URL
|
|
32
27
|
--database-driver ${DRIVER_NEON} | ${DRIVER_POSTGRES}
|
|
33
28
|
--database-prepare true | false
|
|
34
|
-
--aws-profile <name>
|
|
35
|
-
|
|
36
|
-
--aws-access-key-id used when no profile exists (also AWS_ACCESS_KEY_ID)
|
|
29
|
+
--aws-profile <name>
|
|
30
|
+
--aws-access-key-id
|
|
37
31
|
--aws-secret-access-key
|
|
38
|
-
|
|
39
|
-
--
|
|
40
|
-
|
|
41
|
-
--
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
deploy / diff / synth:
|
|
48
|
-
--seed upload knowledge/ once (first deploy only)
|
|
32
|
+
--repo <url> Amplify watch (skipped unless gh login is jginorio)
|
|
33
|
+
--embed-model <id>
|
|
34
|
+
--skip-bedrock-access
|
|
35
|
+
--seed
|
|
36
|
+
--deploy deploy after writing without asking`,
|
|
37
|
+
|
|
38
|
+
deploy: `deploy — deploy the AWS stack
|
|
39
|
+
|
|
40
|
+
--seed
|
|
49
41
|
--deploy-env <path>
|
|
50
42
|
--home
|
|
51
|
-
--dry-run
|
|
43
|
+
--dry-run`,
|
|
44
|
+
|
|
45
|
+
diff: `diff — cdk diff with the same context as deploy
|
|
46
|
+
|
|
47
|
+
--seed
|
|
48
|
+
--deploy-env <path>
|
|
49
|
+
--home
|
|
50
|
+
--dry-run`,
|
|
51
|
+
|
|
52
|
+
synth: `synth — cdk synth with the same context as deploy
|
|
53
|
+
|
|
54
|
+
--seed
|
|
55
|
+
--deploy-env <path>
|
|
56
|
+
--home
|
|
57
|
+
--dry-run`,
|
|
58
|
+
|
|
59
|
+
list: `list — list Context101 CloudFormation stacks (no checkout)
|
|
52
60
|
|
|
53
|
-
list:
|
|
54
61
|
--aws-profile <name>
|
|
55
62
|
--aws-access-key-id
|
|
56
|
-
--aws-secret-access-key
|
|
63
|
+
--aws-secret-access-key`,
|
|
57
64
|
|
|
58
|
-
destroy <
|
|
59
|
-
|
|
65
|
+
destroy: `destroy <name> — tear down a listed stack (clones if needed)
|
|
66
|
+
|
|
67
|
+
--yes, -y
|
|
60
68
|
--aws-profile <name>
|
|
61
69
|
--aws-access-key-id
|
|
62
70
|
--aws-secret-access-key
|
|
71
|
+
--dir <path>
|
|
63
72
|
--deploy-env <path>
|
|
64
73
|
--home
|
|
65
|
-
--dry-run
|
|
74
|
+
--dry-run`,
|
|
75
|
+
|
|
76
|
+
config: `config — show deploy-env keys (values redacted)
|
|
66
77
|
|
|
67
|
-
config:
|
|
68
78
|
--deploy-env <path>
|
|
69
|
-
--home
|
|
79
|
+
--home`,
|
|
70
80
|
|
|
71
|
-
|
|
72
|
-
npm run context101 -- init
|
|
73
|
-
npm run context101 -- deploy
|
|
74
|
-
npx context101-cli init
|
|
75
|
-
npx context101-cli deploy
|
|
76
|
-
context101 list
|
|
77
|
-
context101 destroy Context101Stack
|
|
78
|
-
context101 config
|
|
81
|
+
"config set": `config set KEY=value — write one key (chmod 600; value is not printed)
|
|
79
82
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
+
--deploy-env <path>
|
|
84
|
+
--home`,
|
|
85
|
+
|
|
86
|
+
help: `help [command] — list commands, or flags for one command`,
|
|
87
|
+
};
|
|
83
88
|
|
|
84
89
|
const INIT_ONLY = new Set([
|
|
85
90
|
"--yes",
|
|
@@ -102,6 +107,7 @@ const INIT_ONLY = new Set([
|
|
|
102
107
|
const DESTROY_FROM_INIT = new Set([
|
|
103
108
|
"--yes",
|
|
104
109
|
"-y",
|
|
110
|
+
"--dir",
|
|
105
111
|
"--aws-profile",
|
|
106
112
|
"--aws-access-key-id",
|
|
107
113
|
"--aws-secret-access-key",
|
|
@@ -126,10 +132,22 @@ const COMMANDS = {
|
|
|
126
132
|
remove: "destroy",
|
|
127
133
|
rm: "destroy",
|
|
128
134
|
config: "config",
|
|
135
|
+
help: "help",
|
|
129
136
|
};
|
|
130
137
|
|
|
131
|
-
export function helpText() {
|
|
132
|
-
|
|
138
|
+
export function helpText(topic) {
|
|
139
|
+
if (topic) {
|
|
140
|
+
const key = COMMANDS[topic] ?? topic;
|
|
141
|
+
return TOPIC_HELP[key] ?? helpText();
|
|
142
|
+
}
|
|
143
|
+
const nameW = Math.max(...COMMAND_LINES.map(([name]) => name.length));
|
|
144
|
+
return [
|
|
145
|
+
"Usage: context101 <command>",
|
|
146
|
+
"",
|
|
147
|
+
...COMMAND_LINES.map(([name, desc]) => ` ${name.padEnd(nameW)} ${desc}`),
|
|
148
|
+
"",
|
|
149
|
+
"npx context101 (unscoped) is Context7's MCP — use context101-cli.",
|
|
150
|
+
].join("\n");
|
|
133
151
|
}
|
|
134
152
|
|
|
135
153
|
export function parseArgs(argv) {
|
|
@@ -158,24 +176,30 @@ export function parseArgs(argv) {
|
|
|
158
176
|
configAction: "show",
|
|
159
177
|
configKey: null,
|
|
160
178
|
configValue: null,
|
|
179
|
+
helpTopic: null,
|
|
161
180
|
};
|
|
162
181
|
|
|
163
182
|
const args = [...argv];
|
|
164
183
|
if (args.length === 0) return opts;
|
|
165
184
|
|
|
166
185
|
const first = args[0];
|
|
167
|
-
if (
|
|
186
|
+
if (first === "--help" || first === "-h") {
|
|
187
|
+
opts.command = "help";
|
|
188
|
+
opts.help = true;
|
|
189
|
+
args.shift();
|
|
190
|
+
} else if (COMMANDS[first]) {
|
|
168
191
|
opts.command = COMMANDS[first];
|
|
169
192
|
args.shift();
|
|
170
|
-
} else if (first === "help" || first === "--help" || first === "-h") {
|
|
171
|
-
opts.help = true;
|
|
172
|
-
return opts;
|
|
173
193
|
} else if (!first.startsWith("-")) {
|
|
174
194
|
const err = new Error(`unknown command: ${first}`);
|
|
175
195
|
err.code = "USAGE";
|
|
176
196
|
throw err;
|
|
177
197
|
}
|
|
178
198
|
|
|
199
|
+
if (opts.command === "help") {
|
|
200
|
+
return parseHelpArgs(opts, args);
|
|
201
|
+
}
|
|
202
|
+
|
|
179
203
|
if (opts.command === "config" && args[0] === "set") {
|
|
180
204
|
args.shift();
|
|
181
205
|
const pair = args.shift();
|
|
@@ -284,6 +308,33 @@ export function parseArgs(argv) {
|
|
|
284
308
|
return opts;
|
|
285
309
|
}
|
|
286
310
|
|
|
311
|
+
function parseHelpArgs(opts, args) {
|
|
312
|
+
opts.help = true;
|
|
313
|
+
if (args[0] && !args[0].startsWith("-")) {
|
|
314
|
+
const topic = args.shift();
|
|
315
|
+
if (topic === "config" && args[0] === "set") {
|
|
316
|
+
args.shift();
|
|
317
|
+
opts.helpTopic = "config set";
|
|
318
|
+
} else if (COMMANDS[topic] && topic !== "help") {
|
|
319
|
+
opts.helpTopic = COMMANDS[topic];
|
|
320
|
+
} else if (topic === "help") {
|
|
321
|
+
opts.helpTopic = "help";
|
|
322
|
+
} else {
|
|
323
|
+
const err = new Error(`unknown command: ${topic}`);
|
|
324
|
+
err.code = "USAGE";
|
|
325
|
+
throw err;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
while (args.length) {
|
|
329
|
+
const arg = args.shift();
|
|
330
|
+
if (arg === "--help" || arg === "-h") continue;
|
|
331
|
+
const err = new Error(`unknown flag: ${arg}`);
|
|
332
|
+
err.code = "USAGE";
|
|
333
|
+
throw err;
|
|
334
|
+
}
|
|
335
|
+
return opts;
|
|
336
|
+
}
|
|
337
|
+
|
|
287
338
|
function flagAllowed(command, arg) {
|
|
288
339
|
if (!INIT_ONLY.has(arg)) return true;
|
|
289
340
|
if (command === "init") return true;
|
package/src/stacks.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DEPLOY_CLI, DESTROY_CLI, LIST_CLI, SMOOTH_REGION } from "./defaults.js";
|
|
2
2
|
import { runCdk } from "./cdk-invoke.js";
|
|
3
|
+
import { ensureRepoRoot } from "./clone.js";
|
|
3
4
|
import { createExec } from "./exec.js";
|
|
4
|
-
import { findRepoRoot } from "./repo.js";
|
|
5
5
|
import { banner, writers } from "./style.js";
|
|
6
6
|
|
|
7
7
|
export function parseStackSummaries(payload) {
|
|
@@ -70,12 +70,6 @@ export async function runList(opts, ctx) {
|
|
|
70
70
|
|
|
71
71
|
banner(ctx);
|
|
72
72
|
|
|
73
|
-
const repoRoot = findRepoRoot(ctx.cwd);
|
|
74
|
-
if (!repoRoot) {
|
|
75
|
-
io.err("run this from a Context101 checkout (needs cdk/ and web/).");
|
|
76
|
-
return 1;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
73
|
const env = withAwsAuth(ctx.env ?? {}, {
|
|
80
74
|
profile: opts.awsProfile,
|
|
81
75
|
accessKeyId: opts.awsAccessKeyId,
|
|
@@ -101,12 +95,6 @@ export async function runDestroy(opts, ctx) {
|
|
|
101
95
|
io.write("");
|
|
102
96
|
}
|
|
103
97
|
|
|
104
|
-
const repoRoot = findRepoRoot(ctx.cwd);
|
|
105
|
-
if (!repoRoot) {
|
|
106
|
-
io.err("run this from a Context101 checkout (needs cdk/ and web/).");
|
|
107
|
-
return 1;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
98
|
const stackName = String(opts.stackName || "").trim();
|
|
111
99
|
if (!stackName) {
|
|
112
100
|
io.err(`destroy needs a stack name from \`${LIST_CLI}\`.`);
|
|
@@ -157,6 +145,25 @@ export async function runDestroy(opts, ctx) {
|
|
|
157
145
|
io.warn(
|
|
158
146
|
"Non-default brains are not in CloudFormation — delete them from /brains first."
|
|
159
147
|
);
|
|
148
|
+
|
|
149
|
+
const checkout = ensureRepoRoot({
|
|
150
|
+
cwd: ctx.cwd,
|
|
151
|
+
dir: opts.dir,
|
|
152
|
+
exec,
|
|
153
|
+
io,
|
|
154
|
+
preferHomeClone: true,
|
|
155
|
+
homeDir: ctx.homeDir,
|
|
156
|
+
});
|
|
157
|
+
if (checkout.error) {
|
|
158
|
+
io.err(checkout.error);
|
|
159
|
+
return 1;
|
|
160
|
+
}
|
|
161
|
+
const repoRoot = checkout.repoRoot;
|
|
162
|
+
if (!repoRoot) {
|
|
163
|
+
io.err("could not find or clone a Context101 checkout (needs cdk/ and web/).");
|
|
164
|
+
return 1;
|
|
165
|
+
}
|
|
166
|
+
|
|
160
167
|
io.write(`Destroying ${stackName}…`);
|
|
161
168
|
return (ctx.runDeploy ?? runCdk)({
|
|
162
169
|
repoRoot,
|