@codevector/cli 0.3.4 → 0.4.0
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 +37 -21
- package/dist/index.js +2409 -1235
- package/dist/index.js.map +1 -1
- package/package.json +7 -5
- package/scripts/postinstall.mjs +140 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codevector/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "CodeVector CLI — installs and configures first-party coding-tool integrations.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"bin": {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"files": [
|
|
10
10
|
"bin",
|
|
11
11
|
"dist",
|
|
12
|
+
"scripts",
|
|
12
13
|
"src/hooks"
|
|
13
14
|
],
|
|
14
15
|
"type": "module",
|
|
@@ -16,7 +17,7 @@
|
|
|
16
17
|
"access": "public"
|
|
17
18
|
},
|
|
18
19
|
"dependencies": {
|
|
19
|
-
"@clack/prompts": "1.
|
|
20
|
+
"@clack/prompts": "1.4.0",
|
|
20
21
|
"citty": "^0.2.2",
|
|
21
22
|
"hono": "4.12.16",
|
|
22
23
|
"smol-toml": "^1.6.1"
|
|
@@ -28,8 +29,8 @@
|
|
|
28
29
|
"typescript": "6.0.3",
|
|
29
30
|
"vitest": "4.1.5",
|
|
30
31
|
"zod": "4.4.2",
|
|
31
|
-
"@codevector/api": "1.
|
|
32
|
-
"@codevector/common": "1.
|
|
32
|
+
"@codevector/api": "1.10.0",
|
|
33
|
+
"@codevector/common": "1.10.0"
|
|
33
34
|
},
|
|
34
35
|
"engines": {
|
|
35
36
|
"node": ">=20"
|
|
@@ -39,6 +40,7 @@
|
|
|
39
40
|
"build": "tsup",
|
|
40
41
|
"type-check": "tsc --noEmit",
|
|
41
42
|
"test": "vitest run",
|
|
42
|
-
"test:watch": "vitest"
|
|
43
|
+
"test:watch": "vitest",
|
|
44
|
+
"postinstall": "node scripts/postinstall.mjs"
|
|
43
45
|
}
|
|
44
46
|
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Post-install tasks. Two independent things, each wrapped so neither can
|
|
4
|
+
* fail the install:
|
|
5
|
+
*
|
|
6
|
+
* 1. Migrate legacy single-profile credentials.json to the multi-profile
|
|
7
|
+
* format introduced in v0.4.0.
|
|
8
|
+
* 2. Detect which package manager invoked the install (npm / pnpm / yarn),
|
|
9
|
+
* parsed out of `process.env.npm_config_user_agent`, and persist it to
|
|
10
|
+
* `~/.config/codevector/install.json` so `codevector update` can use
|
|
11
|
+
* the same manager without prompting.
|
|
12
|
+
*
|
|
13
|
+
* Runs automatically after `npm install` / `pnpm install` / `yarn add`.
|
|
14
|
+
* Zero external dependencies — only Node built-ins. Any error is silently
|
|
15
|
+
* ignored so the install never fails.
|
|
16
|
+
*/
|
|
17
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
18
|
+
import { dirname, join } from "node:path";
|
|
19
|
+
import { homedir } from "node:os";
|
|
20
|
+
|
|
21
|
+
const CONFIG_DIR = process.env.CODEVECTOR_CONFIG_DIR ?? join(homedir(), ".config", "codevector");
|
|
22
|
+
const CREDENTIALS_FILE = join(CONFIG_DIR, "credentials.json");
|
|
23
|
+
const INSTALL_FILE = join(CONFIG_DIR, "install.json");
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
migrateCredentialsIfNeeded();
|
|
27
|
+
} catch {
|
|
28
|
+
// Never fail the install.
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
capturePackageManagerIfPossible();
|
|
33
|
+
} catch {
|
|
34
|
+
// Never fail the install.
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function migrateCredentialsIfNeeded() {
|
|
38
|
+
let raw;
|
|
39
|
+
try {
|
|
40
|
+
raw = readFileSync(CREDENTIALS_FILE, "utf8");
|
|
41
|
+
} catch (err) {
|
|
42
|
+
if (err.code === "ENOENT") return; // Nothing to migrate.
|
|
43
|
+
throw err;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let parsed;
|
|
47
|
+
try {
|
|
48
|
+
parsed = JSON.parse(raw);
|
|
49
|
+
} catch {
|
|
50
|
+
return; // Corrupted file — leave it for the CLI to surface.
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Already migrated (or created by a newer CLI).
|
|
54
|
+
if (parsed && typeof parsed === "object" && "profiles" in parsed) return;
|
|
55
|
+
|
|
56
|
+
// Legacy format guard.
|
|
57
|
+
if (
|
|
58
|
+
!parsed ||
|
|
59
|
+
typeof parsed !== "object" ||
|
|
60
|
+
typeof parsed.apiKey !== "string" ||
|
|
61
|
+
typeof parsed.gatewayUrl !== "string"
|
|
62
|
+
) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const migrated = {
|
|
67
|
+
activeProfile: "default",
|
|
68
|
+
profiles: { default: parsed },
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
mkdirSync(dirname(CREDENTIALS_FILE), { recursive: true, mode: 0o700 });
|
|
72
|
+
|
|
73
|
+
// Always enforce 0600 — secrets file.
|
|
74
|
+
const mode = 0o600;
|
|
75
|
+
const tmp = `${CREDENTIALS_FILE}.${process.pid}.tmp`;
|
|
76
|
+
writeFileSync(tmp, JSON.stringify(migrated, null, 2), { mode });
|
|
77
|
+
try {
|
|
78
|
+
chmodSync(tmp, mode);
|
|
79
|
+
} catch {
|
|
80
|
+
// Windows or unsupported FS.
|
|
81
|
+
}
|
|
82
|
+
renameSync(tmp, CREDENTIALS_FILE);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function capturePackageManagerIfPossible() {
|
|
86
|
+
const pm = detectPackageManager();
|
|
87
|
+
if (!pm) return;
|
|
88
|
+
|
|
89
|
+
// Preserve an existing user-pinned choice — only auto-detection writes
|
|
90
|
+
// are overwritten by later installs.
|
|
91
|
+
if (existsSync(INSTALL_FILE)) {
|
|
92
|
+
try {
|
|
93
|
+
const existing = JSON.parse(readFileSync(INSTALL_FILE, "utf8"));
|
|
94
|
+
if (existing && existing.source === "user") return;
|
|
95
|
+
// If the user is reinstalling with the same PM, no-op so we don't
|
|
96
|
+
// churn mtimes on every `npm i -g` rerun.
|
|
97
|
+
if (existing && existing.packageManager === pm && existing.source === "auto") return;
|
|
98
|
+
} catch {
|
|
99
|
+
// Fall through and overwrite a corrupt install.json.
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
|
|
104
|
+
const payload = {
|
|
105
|
+
packageManager: pm,
|
|
106
|
+
source: "auto",
|
|
107
|
+
detectedAt: new Date().toISOString(),
|
|
108
|
+
};
|
|
109
|
+
const tmp = `${INSTALL_FILE}.${process.pid}.tmp`;
|
|
110
|
+
writeFileSync(tmp, JSON.stringify(payload, null, 2));
|
|
111
|
+
renameSync(tmp, INSTALL_FILE);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Parse npm_config_user_agent. All three managers set it during script
|
|
116
|
+
* execution. Example values:
|
|
117
|
+
*
|
|
118
|
+
* npm/10.2.4 node/v20.11.0 darwin arm64 workspaces/false
|
|
119
|
+
* pnpm/8.15.0 npm/? node/v20.11.0 darwin arm64
|
|
120
|
+
* yarn/1.22.19 npm/? node/v20.11.0 darwin arm64
|
|
121
|
+
*
|
|
122
|
+
* We look at the LEADING token: that's the manager actually running this
|
|
123
|
+
* script. pnpm/yarn both append a ghost "npm/?" segment, which is why we
|
|
124
|
+
* never use a contains-check.
|
|
125
|
+
*/
|
|
126
|
+
function detectPackageManager() {
|
|
127
|
+
const ua = process.env.npm_config_user_agent;
|
|
128
|
+
if (typeof ua !== "string" || ua.length === 0) return null;
|
|
129
|
+
const head = ua.split(" ")[0] ?? "";
|
|
130
|
+
const slash = head.indexOf("/");
|
|
131
|
+
const name = slash > 0 ? head.slice(0, slash) : head;
|
|
132
|
+
switch (name) {
|
|
133
|
+
case "npm":
|
|
134
|
+
case "pnpm":
|
|
135
|
+
case "yarn":
|
|
136
|
+
return name;
|
|
137
|
+
default:
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
}
|