@codevector/cli 0.4.0 → 0.5.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/dist/index.js +1429 -516
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/scripts/postinstall.mjs +14 -4
package/dist/index.js
CHANGED
|
@@ -1995,15 +1995,12 @@ var ApiClientError = class extends Error {
|
|
|
1995
1995
|
};
|
|
1996
1996
|
function gatewayClient(gatewayUrl, apiKey, timeoutMs = 15e3) {
|
|
1997
1997
|
const client = hc(trimRightSlash(gatewayUrl), {
|
|
1998
|
-
init: {
|
|
1999
|
-
headers: {
|
|
2000
|
-
"x-api-key": apiKey
|
|
2001
|
-
}
|
|
2002
|
-
},
|
|
2003
1998
|
fetch: (input, init) => {
|
|
2004
1999
|
const controller = new AbortController();
|
|
2005
2000
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
2006
|
-
|
|
2001
|
+
const headers = new Headers(init?.headers);
|
|
2002
|
+
headers.set("x-api-key", apiKey);
|
|
2003
|
+
return fetch(input, { ...init, headers, signal: controller.signal }).finally(
|
|
2007
2004
|
() => clearTimeout(timer)
|
|
2008
2005
|
);
|
|
2009
2006
|
}
|
|
@@ -17532,124 +17529,25 @@ function assertHttpsUrl(urlString) {
|
|
|
17532
17529
|
} catch {
|
|
17533
17530
|
throw new Error(`Invalid gateway URL: "${urlString}"`);
|
|
17534
17531
|
}
|
|
17535
|
-
if (
|
|
17532
|
+
if (process.env.CODEVECTOR_SANDBOX === "1") return;
|
|
17533
|
+
if (parsed.protocol !== "https:" && parsed.hostname !== "localhost" && parsed.hostname !== "127.0.0.1" && parsed.hostname !== "host.docker.internal") {
|
|
17536
17534
|
throw new Error(
|
|
17537
|
-
`Gateway URL must be https:// (got ${parsed.protocol}). Only localhost
|
|
17535
|
+
`Gateway URL must be https:// (got ${parsed.protocol}). Only localhost / 127.0.0.1 / host.docker.internal are allowed over http.`
|
|
17538
17536
|
);
|
|
17539
17537
|
}
|
|
17540
17538
|
}
|
|
17541
17539
|
|
|
17542
|
-
// src/commands/configure.ts
|
|
17543
|
-
import { homedir as homedir5 } from "os";
|
|
17544
|
-
|
|
17545
|
-
// src/lib/hooks.ts
|
|
17546
|
-
import { chmodSync as chmodSync3, copyFileSync, existsSync as existsSync4, mkdirSync as mkdirSync4 } from "fs";
|
|
17547
|
-
import { dirname as dirname4, join as join5 } from "path";
|
|
17548
|
-
import { fileURLToPath } from "url";
|
|
17549
|
-
function bundledHookSource() {
|
|
17550
|
-
const here = dirname4(fileURLToPath(import.meta.url));
|
|
17551
|
-
const candidates = [
|
|
17552
|
-
join5(here, "hooks", "acceptance.sh"),
|
|
17553
|
-
// production — dist/hooks/…
|
|
17554
|
-
join5(here, "..", "hooks", "acceptance.sh")
|
|
17555
|
-
// dev — src/hooks/…
|
|
17556
|
-
];
|
|
17557
|
-
for (const candidate of candidates) {
|
|
17558
|
-
if (existsSync4(candidate)) return candidate;
|
|
17559
|
-
}
|
|
17560
|
-
throw new Error(
|
|
17561
|
-
"Could not locate the bundled acceptance hook script (acceptance.sh). Reinstall @codevector/cli to repair the installation."
|
|
17562
|
-
);
|
|
17563
|
-
}
|
|
17564
|
-
function installAcceptanceHook() {
|
|
17565
|
-
mkdirSync4(HOOKS_DIR, { recursive: true, mode: 448 });
|
|
17566
|
-
copyFileSync(bundledHookSource(), ACCEPTANCE_HOOK_FILE);
|
|
17567
|
-
try {
|
|
17568
|
-
chmodSync3(ACCEPTANCE_HOOK_FILE, 493);
|
|
17569
|
-
} catch {
|
|
17570
|
-
}
|
|
17571
|
-
return ACCEPTANCE_HOOK_FILE;
|
|
17572
|
-
}
|
|
17573
|
-
|
|
17574
|
-
// src/lib/project-context.ts
|
|
17575
|
-
import { execFileSync } from "child_process";
|
|
17576
|
-
import { existsSync as existsSync5, readFileSync as readFileSync6 } from "fs";
|
|
17577
|
-
import { join as join6 } from "path";
|
|
17578
|
-
var DEFAULT_TICKET_PATTERN = /[A-Z]+-\d+/;
|
|
17579
|
-
function safeGit(args, cwd) {
|
|
17580
|
-
try {
|
|
17581
|
-
const out = execFileSync("git", args, {
|
|
17582
|
-
encoding: "utf8",
|
|
17583
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
17584
|
-
cwd,
|
|
17585
|
-
timeout: 2e3
|
|
17586
|
-
});
|
|
17587
|
-
return out.trim() || null;
|
|
17588
|
-
} catch {
|
|
17589
|
-
return null;
|
|
17590
|
-
}
|
|
17591
|
-
}
|
|
17592
|
-
function repoSlugFromRemote(remote) {
|
|
17593
|
-
const trimmed = remote.trim().replace(/\.git$/i, "");
|
|
17594
|
-
const lastSegment = trimmed.split(/[:/]/).pop();
|
|
17595
|
-
if (!lastSegment) return null;
|
|
17596
|
-
return lastSegment.length > 0 ? lastSegment : null;
|
|
17597
|
-
}
|
|
17598
|
-
function resolveProjectContext(cwd = process.cwd(), ticketPattern = DEFAULT_TICKET_PATTERN) {
|
|
17599
|
-
let project = null;
|
|
17600
|
-
let ticket = null;
|
|
17601
|
-
const override = readLocalConfig(cwd);
|
|
17602
|
-
if (override?.projectName) project = override.projectName;
|
|
17603
|
-
const effectivePattern = override?.ticketPattern ? safeCompileTicketPattern(override.ticketPattern) ?? ticketPattern : ticketPattern;
|
|
17604
|
-
if (!project) {
|
|
17605
|
-
const remote = safeGit(["remote", "get-url", "origin"], cwd);
|
|
17606
|
-
if (remote) project = repoSlugFromRemote(remote);
|
|
17607
|
-
}
|
|
17608
|
-
const branch = safeGit(["branch", "--show-current"], cwd);
|
|
17609
|
-
if (branch) {
|
|
17610
|
-
const match = effectivePattern.exec(branch);
|
|
17611
|
-
if (match) ticket = match[0];
|
|
17612
|
-
}
|
|
17613
|
-
return { project, ticket };
|
|
17614
|
-
}
|
|
17615
|
-
function readLocalConfig(cwd) {
|
|
17616
|
-
const path = join6(cwd, ".codevector.json");
|
|
17617
|
-
if (!existsSync5(path)) return null;
|
|
17618
|
-
try {
|
|
17619
|
-
const parsed = JSON.parse(readFileSync6(path, "utf8"));
|
|
17620
|
-
if (typeof parsed !== "object" || parsed === null) return null;
|
|
17621
|
-
const p2 = parsed;
|
|
17622
|
-
const result = {};
|
|
17623
|
-
if (typeof p2.projectName === "string" && p2.projectName.length > 0) {
|
|
17624
|
-
result.projectName = p2.projectName;
|
|
17625
|
-
}
|
|
17626
|
-
if (typeof p2.ticketPattern === "string" && p2.ticketPattern.length > 0) {
|
|
17627
|
-
result.ticketPattern = p2.ticketPattern;
|
|
17628
|
-
}
|
|
17629
|
-
return result;
|
|
17630
|
-
} catch {
|
|
17631
|
-
return null;
|
|
17632
|
-
}
|
|
17633
|
-
}
|
|
17634
|
-
function safeCompileTicketPattern(pattern) {
|
|
17635
|
-
try {
|
|
17636
|
-
return new RegExp(pattern);
|
|
17637
|
-
} catch {
|
|
17638
|
-
return null;
|
|
17639
|
-
}
|
|
17640
|
-
}
|
|
17641
|
-
|
|
17642
17540
|
// src/config-writers/codex.ts
|
|
17643
17541
|
import {
|
|
17644
|
-
chmodSync as
|
|
17645
|
-
existsSync as
|
|
17646
|
-
mkdirSync as
|
|
17647
|
-
readFileSync as
|
|
17542
|
+
chmodSync as chmodSync3,
|
|
17543
|
+
existsSync as existsSync4,
|
|
17544
|
+
mkdirSync as mkdirSync4,
|
|
17545
|
+
readFileSync as readFileSync6,
|
|
17648
17546
|
renameSync as renameSync3,
|
|
17649
17547
|
statSync as statSync4,
|
|
17650
17548
|
writeFileSync as writeFileSync5
|
|
17651
17549
|
} from "fs";
|
|
17652
|
-
import { dirname as
|
|
17550
|
+
import { dirname as dirname4, join as join5 } from "path";
|
|
17653
17551
|
import { homedir as homedir4 } from "os";
|
|
17654
17552
|
|
|
17655
17553
|
// ../../node_modules/.pnpm/smol-toml@1.6.1/node_modules/smol-toml/dist/error.js
|
|
@@ -17732,7 +17630,7 @@ function skipVoid(str, ptr, banNewLines, banComments) {
|
|
|
17732
17630
|
}
|
|
17733
17631
|
return ptr;
|
|
17734
17632
|
}
|
|
17735
|
-
function skipUntil(str, ptr,
|
|
17633
|
+
function skipUntil(str, ptr, sep2, end, banNewLines = false) {
|
|
17736
17634
|
if (!end) {
|
|
17737
17635
|
ptr = indexOfNewline(str, ptr);
|
|
17738
17636
|
return ptr < 0 ? str.length : ptr;
|
|
@@ -17741,7 +17639,7 @@ function skipUntil(str, ptr, sep, end, banNewLines = false) {
|
|
|
17741
17639
|
let c = str[i];
|
|
17742
17640
|
if (c === "#") {
|
|
17743
17641
|
i = indexOfNewline(str, i);
|
|
17744
|
-
} else if (c ===
|
|
17642
|
+
} else if (c === sep2) {
|
|
17745
17643
|
return i + 1;
|
|
17746
17644
|
} else if (c === end || banNewLines && (c === "\n" || c === "\r" && str[i + 1] === "\n")) {
|
|
17747
17645
|
return i;
|
|
@@ -18489,6 +18387,7 @@ var PROVIDER_ID = "codevector";
|
|
|
18489
18387
|
var writeCodexConfig = ({
|
|
18490
18388
|
gatewayUrl,
|
|
18491
18389
|
scope,
|
|
18390
|
+
project,
|
|
18492
18391
|
model,
|
|
18493
18392
|
availableModels = []
|
|
18494
18393
|
}) => {
|
|
@@ -18502,7 +18401,8 @@ var writeCodexConfig = ({
|
|
|
18502
18401
|
name: "CodeVector Gateway",
|
|
18503
18402
|
base_url: `${gateway}/gateway/openai/v1`,
|
|
18504
18403
|
env_key: ENV_VAR_NAME2,
|
|
18505
|
-
wire_api: "responses"
|
|
18404
|
+
wire_api: "responses",
|
|
18405
|
+
http_headers: buildHttpHeaders(scope, project)
|
|
18506
18406
|
};
|
|
18507
18407
|
merged.model_providers = providers;
|
|
18508
18408
|
if (model) {
|
|
@@ -18540,15 +18440,15 @@ var writeCodexConfig = ({
|
|
|
18540
18440
|
function codexConfigPath(scope = "user") {
|
|
18541
18441
|
switch (scope) {
|
|
18542
18442
|
case "user":
|
|
18543
|
-
return
|
|
18443
|
+
return join5(homedir4(), ".codex", "config.toml");
|
|
18544
18444
|
case "project":
|
|
18545
18445
|
case "local":
|
|
18546
|
-
return
|
|
18446
|
+
return join5(userCwd(), ".codex", "config.toml");
|
|
18547
18447
|
}
|
|
18548
18448
|
}
|
|
18549
18449
|
function readTomlOrEmpty(path) {
|
|
18550
|
-
if (!
|
|
18551
|
-
const raw =
|
|
18450
|
+
if (!existsSync4(path)) return {};
|
|
18451
|
+
const raw = readFileSync6(path, "utf8");
|
|
18552
18452
|
if (raw.trim().length === 0) return {};
|
|
18553
18453
|
const parsed = parse3(raw);
|
|
18554
18454
|
if (!isObject4(parsed)) {
|
|
@@ -18557,10 +18457,10 @@ function readTomlOrEmpty(path) {
|
|
|
18557
18457
|
return parsed;
|
|
18558
18458
|
}
|
|
18559
18459
|
function writeTomlAtomic(path, value) {
|
|
18560
|
-
const dir =
|
|
18561
|
-
|
|
18460
|
+
const dir = dirname4(path);
|
|
18461
|
+
mkdirSync4(dir, { recursive: true, mode: 448 });
|
|
18562
18462
|
let mode;
|
|
18563
|
-
if (
|
|
18463
|
+
if (existsSync4(path)) {
|
|
18564
18464
|
mode = statSync4(path).mode & 511;
|
|
18565
18465
|
}
|
|
18566
18466
|
const tmp = `${path}.${process.pid}.tmp`;
|
|
@@ -18568,7 +18468,7 @@ function writeTomlAtomic(path, value) {
|
|
|
18568
18468
|
`);
|
|
18569
18469
|
if (mode !== void 0) {
|
|
18570
18470
|
try {
|
|
18571
|
-
|
|
18471
|
+
chmodSync3(tmp, mode);
|
|
18572
18472
|
} catch {
|
|
18573
18473
|
}
|
|
18574
18474
|
}
|
|
@@ -18580,127 +18480,172 @@ function isObject4(v2) {
|
|
|
18580
18480
|
function trimRightSlash4(url2) {
|
|
18581
18481
|
return url2.endsWith("/") ? url2.slice(0, -1) : url2;
|
|
18582
18482
|
}
|
|
18483
|
+
function buildHttpHeaders(scope, project) {
|
|
18484
|
+
const safe = (v2) => v2.replace(/[\r\n]/g, "").trim();
|
|
18485
|
+
const headers = {
|
|
18486
|
+
"x-client-app": "codex"
|
|
18487
|
+
};
|
|
18488
|
+
if (scope !== "user" && project) {
|
|
18489
|
+
const slug = safe(project);
|
|
18490
|
+
if (slug) {
|
|
18491
|
+
headers["x-project"] = slug;
|
|
18492
|
+
}
|
|
18493
|
+
}
|
|
18494
|
+
return headers;
|
|
18495
|
+
}
|
|
18583
18496
|
|
|
18584
|
-
// src/
|
|
18497
|
+
// src/lib/hooks.ts
|
|
18498
|
+
import { chmodSync as chmodSync4, copyFileSync, existsSync as existsSync5, mkdirSync as mkdirSync5 } from "fs";
|
|
18499
|
+
import { dirname as dirname5, join as join6 } from "path";
|
|
18500
|
+
import { fileURLToPath } from "url";
|
|
18501
|
+
function bundledHookSource() {
|
|
18502
|
+
const here = dirname5(fileURLToPath(import.meta.url));
|
|
18503
|
+
const candidates = [
|
|
18504
|
+
join6(here, "hooks", "acceptance.sh"),
|
|
18505
|
+
// production — dist/hooks/…
|
|
18506
|
+
join6(here, "..", "hooks", "acceptance.sh")
|
|
18507
|
+
// dev — src/hooks/…
|
|
18508
|
+
];
|
|
18509
|
+
for (const candidate of candidates) {
|
|
18510
|
+
if (existsSync5(candidate)) return candidate;
|
|
18511
|
+
}
|
|
18512
|
+
throw new Error(
|
|
18513
|
+
"Could not locate the bundled acceptance hook script (acceptance.sh). Reinstall @codevector/cli to repair the installation."
|
|
18514
|
+
);
|
|
18515
|
+
}
|
|
18516
|
+
function installAcceptanceHook() {
|
|
18517
|
+
mkdirSync5(HOOKS_DIR, { recursive: true, mode: 448 });
|
|
18518
|
+
copyFileSync(bundledHookSource(), ACCEPTANCE_HOOK_FILE);
|
|
18519
|
+
try {
|
|
18520
|
+
chmodSync4(ACCEPTANCE_HOOK_FILE, 493);
|
|
18521
|
+
} catch {
|
|
18522
|
+
}
|
|
18523
|
+
return ACCEPTANCE_HOOK_FILE;
|
|
18524
|
+
}
|
|
18525
|
+
|
|
18526
|
+
// src/lib/project-config.ts
|
|
18527
|
+
import { existsSync as existsSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
|
|
18528
|
+
import { dirname as dirname6, join as join7, parse as parsePath, resolve } from "path";
|
|
18529
|
+
var PROJECT_CONFIG_FILENAME = ".codevector.json";
|
|
18530
|
+
var ProjectConfigSchema = external_exports.object({
|
|
18531
|
+
projectName: external_exports.string().min(1).optional(),
|
|
18532
|
+
ticketPattern: external_exports.string().min(1).optional(),
|
|
18533
|
+
gateway: external_exports.url().optional(),
|
|
18534
|
+
tools: external_exports.array(ToolConfigSchema).optional()
|
|
18535
|
+
});
|
|
18536
|
+
function findProjectConfigPath(startDir) {
|
|
18537
|
+
let dir = resolve(startDir);
|
|
18538
|
+
const root = parsePath(dir).root;
|
|
18539
|
+
while (true) {
|
|
18540
|
+
const candidate = join7(dir, PROJECT_CONFIG_FILENAME);
|
|
18541
|
+
if (existsSync6(candidate)) return candidate;
|
|
18542
|
+
if (dir === root) return null;
|
|
18543
|
+
const parent = dirname6(dir);
|
|
18544
|
+
if (parent === dir) return null;
|
|
18545
|
+
dir = parent;
|
|
18546
|
+
}
|
|
18547
|
+
}
|
|
18548
|
+
function readProjectConfigAt(path) {
|
|
18549
|
+
let raw;
|
|
18550
|
+
try {
|
|
18551
|
+
raw = readFileSync7(path, "utf8");
|
|
18552
|
+
} catch {
|
|
18553
|
+
return null;
|
|
18554
|
+
}
|
|
18555
|
+
let parsed;
|
|
18556
|
+
try {
|
|
18557
|
+
parsed = JSON.parse(raw);
|
|
18558
|
+
} catch {
|
|
18559
|
+
return null;
|
|
18560
|
+
}
|
|
18561
|
+
const result = ProjectConfigSchema.safeParse(parsed);
|
|
18562
|
+
return result.success ? result.data : null;
|
|
18563
|
+
}
|
|
18564
|
+
function readProjectConfig(startDir) {
|
|
18565
|
+
const path = findProjectConfigPath(startDir);
|
|
18566
|
+
if (!path) return null;
|
|
18567
|
+
const config2 = readProjectConfigAt(path);
|
|
18568
|
+
if (!config2) return null;
|
|
18569
|
+
return { config: config2, path };
|
|
18570
|
+
}
|
|
18571
|
+
function writeProjectConfig(path, config2) {
|
|
18572
|
+
const parsed = ProjectConfigSchema.parse(config2);
|
|
18573
|
+
writeFileSync6(path, `${JSON.stringify(parsed, null, 2)}
|
|
18574
|
+
`, { mode: 420 });
|
|
18575
|
+
}
|
|
18576
|
+
|
|
18577
|
+
// src/commands/config/sync.ts
|
|
18585
18578
|
var WRITERS = {
|
|
18586
18579
|
"claude-code": writeClaudeCodeConfig,
|
|
18587
18580
|
opencode: writeOpencodeConfig,
|
|
18588
18581
|
codex: writeCodexConfig
|
|
18589
18582
|
};
|
|
18590
|
-
var
|
|
18591
|
-
var SCOPES = ["local", "project"];
|
|
18592
|
-
var configureCommand = defineCommand({
|
|
18583
|
+
var configSyncCommand = defineCommand({
|
|
18593
18584
|
meta: {
|
|
18594
|
-
name: "
|
|
18595
|
-
description: "
|
|
18585
|
+
name: "sync",
|
|
18586
|
+
description: "Re-apply the tool configuration recorded in .codevector.json. Use this when on-disk IDE config files have drifted from the manifest."
|
|
18596
18587
|
},
|
|
18597
|
-
|
|
18598
|
-
|
|
18599
|
-
|
|
18600
|
-
|
|
18601
|
-
|
|
18602
|
-
|
|
18603
|
-
|
|
18604
|
-
type: "string",
|
|
18605
|
-
description: "Settings scope: local (default, per-repo, gitignored) or project (committed). For user-scope (global) setup, use `codevector system configure`.",
|
|
18606
|
-
valueHint: "local|project"
|
|
18607
|
-
},
|
|
18608
|
-
all: {
|
|
18609
|
-
type: "boolean",
|
|
18610
|
-
description: "Configure every supported tool."
|
|
18588
|
+
async run() {
|
|
18589
|
+
ge("codevector sync");
|
|
18590
|
+
const found = readProjectConfig(userCwd());
|
|
18591
|
+
if (!found) {
|
|
18592
|
+
throw new Error(
|
|
18593
|
+
"No .codevector.json on the path to root. Run `codevector init` first."
|
|
18594
|
+
);
|
|
18611
18595
|
}
|
|
18612
|
-
|
|
18613
|
-
|
|
18614
|
-
const
|
|
18615
|
-
if (
|
|
18596
|
+
const { config: config2, path: manifestPath } = found;
|
|
18597
|
+
R2.info(`Manifest: ${manifestPath}`);
|
|
18598
|
+
const tools = config2.tools ?? [];
|
|
18599
|
+
if (tools.length === 0) {
|
|
18600
|
+
R2.warn(
|
|
18601
|
+
"Manifest has no `tools` to sync. Run `codevector configure` to set up tools for this repo."
|
|
18602
|
+
);
|
|
18603
|
+
ye("Nothing to do.");
|
|
18604
|
+
return;
|
|
18605
|
+
}
|
|
18606
|
+
const profiles = await readProfiles();
|
|
18607
|
+
if (!profiles) {
|
|
18616
18608
|
throw new Error("Not signed in. Run `codevector auth login` first.");
|
|
18617
18609
|
}
|
|
18618
|
-
const
|
|
18619
|
-
const creds = initialProfiles.profiles[activeProfileName];
|
|
18610
|
+
const creds = profiles.profiles[profiles.activeProfile];
|
|
18620
18611
|
if (!creds) {
|
|
18621
18612
|
throw new Error(
|
|
18622
|
-
`Active profile "${
|
|
18613
|
+
`Active profile "${profiles.activeProfile}" is missing from credentials. Run \`codevector auth login\`.`
|
|
18623
18614
|
);
|
|
18624
18615
|
}
|
|
18625
|
-
|
|
18626
|
-
|
|
18627
|
-
|
|
18628
|
-
const project = scope === "user" ? void 0 : resolveProjectContext(userCwd()).project ?? void 0;
|
|
18629
|
-
if (scope !== "user") {
|
|
18630
|
-
R2.info(
|
|
18631
|
-
project ? `Project: ${project} (used for x-project attribution header)` : "No project detected (no git remote and no .codevector.json)."
|
|
18616
|
+
if (config2.gateway && trimRightSlash5(creds.gatewayUrl) !== trimRightSlash5(config2.gateway)) {
|
|
18617
|
+
R2.warn(
|
|
18618
|
+
`Active profile points at ${creds.gatewayUrl} but manifest pins ${config2.gateway}. Sync will use the active profile.`
|
|
18632
18619
|
);
|
|
18633
18620
|
}
|
|
18634
18621
|
const reachable = await fetchReachableChatModels(creds);
|
|
18635
|
-
const model = await pickPinnedModel(reachable);
|
|
18636
18622
|
const hookPath = installAcceptanceHook();
|
|
18623
|
+
const project = config2.projectName;
|
|
18637
18624
|
const results = [];
|
|
18638
18625
|
for (const tool of tools) {
|
|
18639
|
-
const writer = WRITERS[tool];
|
|
18626
|
+
const writer = WRITERS[tool.tool];
|
|
18640
18627
|
if (!writer) {
|
|
18641
18628
|
results.push({
|
|
18642
|
-
tool,
|
|
18629
|
+
tool: tool.tool,
|
|
18643
18630
|
status: "skipped",
|
|
18644
18631
|
path: "",
|
|
18645
|
-
scope,
|
|
18646
|
-
notes: `Unsupported tool "${tool}". Known: ${Object.keys(WRITERS).join(", ")}.`
|
|
18632
|
+
scope: tool.scope,
|
|
18633
|
+
notes: `Unsupported tool "${tool.tool}". Known: ${Object.keys(WRITERS).join(", ")}.`
|
|
18647
18634
|
});
|
|
18648
18635
|
continue;
|
|
18649
18636
|
}
|
|
18650
18637
|
try {
|
|
18651
|
-
results.push(
|
|
18652
|
-
writer({
|
|
18653
|
-
gatewayUrl: creds.gatewayUrl,
|
|
18654
|
-
apiKey: creds.apiKey,
|
|
18655
|
-
hookScriptPath: hookPath,
|
|
18656
|
-
scope,
|
|
18657
|
-
...project ? { project } : {},
|
|
18658
|
-
...model ? { model } : {},
|
|
18659
|
-
availableModels: reachable.map((m2) => ({
|
|
18660
|
-
slug: m2.slug,
|
|
18661
|
-
displayName: m2.displayName,
|
|
18662
|
-
contextWindow: m2.contextWindow,
|
|
18663
|
-
maxOutputTokens: m2.maxOutputTokens,
|
|
18664
|
-
maxInputTokens: m2.maxInputTokens,
|
|
18665
|
-
inputPricePerMtok: m2.inputPricePerMtok,
|
|
18666
|
-
cachedInputPricePerMtok: m2.cachedInputPricePerMtok,
|
|
18667
|
-
cacheWritePricePerMtok: m2.cacheWritePricePerMtok,
|
|
18668
|
-
reasoningPricePerMtok: m2.reasoningPricePerMtok,
|
|
18669
|
-
outputPricePerMtok: m2.outputPricePerMtok,
|
|
18670
|
-
supportsReasoning: m2.supportsReasoning,
|
|
18671
|
-
supportsAttachment: m2.supportsAttachment,
|
|
18672
|
-
supportsToolCall: m2.supportsToolCall,
|
|
18673
|
-
supportsTemperature: m2.supportsTemperature,
|
|
18674
|
-
inputModalities: m2.inputModalities,
|
|
18675
|
-
outputModalities: m2.outputModalities,
|
|
18676
|
-
releaseDate: m2.releaseDate,
|
|
18677
|
-
family: m2.family
|
|
18678
|
-
}))
|
|
18679
|
-
})
|
|
18680
|
-
);
|
|
18638
|
+
results.push(writer(buildWriterInput(tool, creds, hookPath, project, reachable)));
|
|
18681
18639
|
} catch (err) {
|
|
18682
18640
|
results.push({
|
|
18683
|
-
tool,
|
|
18641
|
+
tool: tool.tool,
|
|
18684
18642
|
status: "skipped",
|
|
18685
18643
|
path: "",
|
|
18686
|
-
scope,
|
|
18644
|
+
scope: tool.scope,
|
|
18687
18645
|
notes: err instanceof Error ? err.message : String(err)
|
|
18688
18646
|
});
|
|
18689
18647
|
}
|
|
18690
18648
|
}
|
|
18691
|
-
const persisted = [];
|
|
18692
|
-
for (const r of results) {
|
|
18693
|
-
if (r.status === "configured") {
|
|
18694
|
-
persisted.push({
|
|
18695
|
-
tool: r.tool,
|
|
18696
|
-
scope: r.scope,
|
|
18697
|
-
...model ? { modelSlug: model.slug } : {}
|
|
18698
|
-
});
|
|
18699
|
-
}
|
|
18700
|
-
}
|
|
18701
|
-
if (persisted.length > 0) {
|
|
18702
|
-
updateProfileToolConfigs(activeProfileName, persisted);
|
|
18703
|
-
}
|
|
18704
18649
|
for (const r of results) {
|
|
18705
18650
|
if (r.status === "configured") {
|
|
18706
18651
|
R2.success(`${r.tool} \u2192 ${r.path} [${r.scope}]`);
|
|
@@ -18709,37 +18654,320 @@ var configureCommand = defineCommand({
|
|
|
18709
18654
|
R2.warn(`${r.tool}: skipped \u2014 ${r.notes ?? "unknown reason"}`);
|
|
18710
18655
|
}
|
|
18711
18656
|
}
|
|
18712
|
-
|
|
18713
|
-
Se(
|
|
18714
|
-
"Claude Code auto-ignores .claude/settings.local.json in git; your API key stays on this machine.",
|
|
18715
|
-
"Local scope"
|
|
18716
|
-
);
|
|
18717
|
-
}
|
|
18718
|
-
ye("Done.");
|
|
18657
|
+
ye("Sync complete.");
|
|
18719
18658
|
}
|
|
18720
18659
|
});
|
|
18721
|
-
|
|
18722
|
-
|
|
18723
|
-
|
|
18724
|
-
|
|
18725
|
-
|
|
18726
|
-
|
|
18727
|
-
|
|
18728
|
-
|
|
18729
|
-
|
|
18730
|
-
|
|
18731
|
-
|
|
18732
|
-
|
|
18733
|
-
|
|
18734
|
-
|
|
18735
|
-
|
|
18736
|
-
|
|
18737
|
-
|
|
18738
|
-
|
|
18739
|
-
|
|
18740
|
-
|
|
18741
|
-
|
|
18742
|
-
|
|
18660
|
+
function buildWriterInput(tool, creds, hookPath, project, reachable) {
|
|
18661
|
+
const model = pickModel(tool.modelSlug, reachable);
|
|
18662
|
+
return {
|
|
18663
|
+
gatewayUrl: creds.gatewayUrl,
|
|
18664
|
+
apiKey: creds.apiKey,
|
|
18665
|
+
hookScriptPath: hookPath,
|
|
18666
|
+
scope: tool.scope,
|
|
18667
|
+
...project ? { project } : {},
|
|
18668
|
+
...model ? { model } : {},
|
|
18669
|
+
availableModels: reachable.map((m2) => ({
|
|
18670
|
+
slug: m2.slug,
|
|
18671
|
+
displayName: m2.displayName,
|
|
18672
|
+
contextWindow: m2.contextWindow,
|
|
18673
|
+
maxOutputTokens: m2.maxOutputTokens,
|
|
18674
|
+
maxInputTokens: m2.maxInputTokens,
|
|
18675
|
+
inputPricePerMtok: m2.inputPricePerMtok,
|
|
18676
|
+
cachedInputPricePerMtok: m2.cachedInputPricePerMtok,
|
|
18677
|
+
cacheWritePricePerMtok: m2.cacheWritePricePerMtok,
|
|
18678
|
+
reasoningPricePerMtok: m2.reasoningPricePerMtok,
|
|
18679
|
+
outputPricePerMtok: m2.outputPricePerMtok,
|
|
18680
|
+
supportsReasoning: m2.supportsReasoning,
|
|
18681
|
+
supportsAttachment: m2.supportsAttachment,
|
|
18682
|
+
supportsToolCall: m2.supportsToolCall,
|
|
18683
|
+
supportsTemperature: m2.supportsTemperature,
|
|
18684
|
+
inputModalities: m2.inputModalities,
|
|
18685
|
+
outputModalities: m2.outputModalities,
|
|
18686
|
+
releaseDate: m2.releaseDate,
|
|
18687
|
+
family: m2.family
|
|
18688
|
+
}))
|
|
18689
|
+
};
|
|
18690
|
+
}
|
|
18691
|
+
function pickModel(modelSlug, reachable) {
|
|
18692
|
+
if (!modelSlug) return void 0;
|
|
18693
|
+
const found = reachable.find((m2) => m2.slug === modelSlug);
|
|
18694
|
+
if (!found) return void 0;
|
|
18695
|
+
return { slug: found.slug, providerKind: found.providerKind };
|
|
18696
|
+
}
|
|
18697
|
+
async function fetchReachableChatModels(creds) {
|
|
18698
|
+
const client = gatewayClient(creds.gatewayUrl, creds.apiKey, 1e4);
|
|
18699
|
+
const s = ft();
|
|
18700
|
+
s.start("Loading reachable models\u2026");
|
|
18701
|
+
try {
|
|
18702
|
+
const res = await call(parseResponse(client.models.$get()));
|
|
18703
|
+
const models = res.data.filter((m2) => m2.kind === "chat").map((m2) => ({
|
|
18704
|
+
slug: m2.slug,
|
|
18705
|
+
providerKind: m2.providerKind,
|
|
18706
|
+
displayName: m2.displayName,
|
|
18707
|
+
contextWindow: m2.contextWindow,
|
|
18708
|
+
maxOutputTokens: m2.maxOutputTokens,
|
|
18709
|
+
maxInputTokens: m2.maxInputTokens,
|
|
18710
|
+
inputPricePerMtok: m2.inputPricePerMtok,
|
|
18711
|
+
cachedInputPricePerMtok: m2.cachedInputPricePerMtok,
|
|
18712
|
+
cacheWritePricePerMtok: m2.cacheWritePricePerMtok,
|
|
18713
|
+
reasoningPricePerMtok: m2.reasoningPricePerMtok,
|
|
18714
|
+
outputPricePerMtok: m2.outputPricePerMtok,
|
|
18715
|
+
supportsReasoning: m2.supportsReasoning,
|
|
18716
|
+
supportsAttachment: m2.supportsAttachment,
|
|
18717
|
+
supportsToolCall: m2.supportsToolCall,
|
|
18718
|
+
supportsTemperature: m2.supportsTemperature,
|
|
18719
|
+
inputModalities: m2.inputModalities,
|
|
18720
|
+
outputModalities: m2.outputModalities,
|
|
18721
|
+
releaseDate: m2.releaseDate,
|
|
18722
|
+
family: m2.family
|
|
18723
|
+
}));
|
|
18724
|
+
s.stop(`${models.length} model${models.length === 1 ? "" : "s"} reachable`);
|
|
18725
|
+
return models;
|
|
18726
|
+
} catch (err) {
|
|
18727
|
+
s.stop("Could not load models");
|
|
18728
|
+
R2.warn(
|
|
18729
|
+
`Continuing without model list \u2014 ${err instanceof ApiClientError ? err.message : String(err)}.`
|
|
18730
|
+
);
|
|
18731
|
+
return [];
|
|
18732
|
+
}
|
|
18733
|
+
}
|
|
18734
|
+
function trimRightSlash5(url2) {
|
|
18735
|
+
return url2.endsWith("/") ? url2.slice(0, -1) : url2;
|
|
18736
|
+
}
|
|
18737
|
+
|
|
18738
|
+
// src/commands/config/index.ts
|
|
18739
|
+
var configCommand = defineCommand({
|
|
18740
|
+
meta: {
|
|
18741
|
+
name: "config",
|
|
18742
|
+
description: "Inspect and re-apply this repo's .codevector.json configuration."
|
|
18743
|
+
},
|
|
18744
|
+
subCommands: {
|
|
18745
|
+
sync: configSyncCommand
|
|
18746
|
+
}
|
|
18747
|
+
});
|
|
18748
|
+
|
|
18749
|
+
// src/commands/configure.ts
|
|
18750
|
+
import { homedir as homedir5 } from "os";
|
|
18751
|
+
|
|
18752
|
+
// src/lib/project-context.ts
|
|
18753
|
+
import { execFileSync } from "child_process";
|
|
18754
|
+
import { join as join8 } from "path";
|
|
18755
|
+
var DEFAULT_TICKET_PATTERN = /[A-Z]+-\d+/;
|
|
18756
|
+
function safeGit(args, cwd) {
|
|
18757
|
+
try {
|
|
18758
|
+
const out = execFileSync("git", args, {
|
|
18759
|
+
encoding: "utf8",
|
|
18760
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
18761
|
+
cwd,
|
|
18762
|
+
timeout: 2e3
|
|
18763
|
+
});
|
|
18764
|
+
return out.trim() || null;
|
|
18765
|
+
} catch {
|
|
18766
|
+
return null;
|
|
18767
|
+
}
|
|
18768
|
+
}
|
|
18769
|
+
function repoSlugFromRemote(remote) {
|
|
18770
|
+
const trimmed = remote.trim().replace(/\.git$/i, "");
|
|
18771
|
+
const lastSegment = trimmed.split(/[:/]/).pop();
|
|
18772
|
+
if (!lastSegment) return null;
|
|
18773
|
+
return lastSegment.length > 0 ? lastSegment : null;
|
|
18774
|
+
}
|
|
18775
|
+
function resolveProjectContext(cwd = process.cwd(), ticketPattern = DEFAULT_TICKET_PATTERN) {
|
|
18776
|
+
let project = null;
|
|
18777
|
+
let ticket = null;
|
|
18778
|
+
const override = readLocalConfig(cwd);
|
|
18779
|
+
if (override?.projectName) project = override.projectName;
|
|
18780
|
+
const effectivePattern = override?.ticketPattern ? safeCompileTicketPattern(override.ticketPattern) ?? ticketPattern : ticketPattern;
|
|
18781
|
+
if (!project) {
|
|
18782
|
+
const remote = safeGit(["remote", "get-url", "origin"], cwd);
|
|
18783
|
+
if (remote) project = repoSlugFromRemote(remote);
|
|
18784
|
+
}
|
|
18785
|
+
const branch = safeGit(["branch", "--show-current"], cwd);
|
|
18786
|
+
if (branch) {
|
|
18787
|
+
const match = effectivePattern.exec(branch);
|
|
18788
|
+
if (match) ticket = match[0];
|
|
18789
|
+
}
|
|
18790
|
+
return { project, ticket };
|
|
18791
|
+
}
|
|
18792
|
+
function readLocalConfig(cwd) {
|
|
18793
|
+
const config2 = readProjectConfigAt(join8(cwd, PROJECT_CONFIG_FILENAME));
|
|
18794
|
+
if (!config2) return null;
|
|
18795
|
+
const result = {};
|
|
18796
|
+
if (config2.projectName) result.projectName = config2.projectName;
|
|
18797
|
+
if (config2.ticketPattern) result.ticketPattern = config2.ticketPattern;
|
|
18798
|
+
return result;
|
|
18799
|
+
}
|
|
18800
|
+
function safeCompileTicketPattern(pattern) {
|
|
18801
|
+
try {
|
|
18802
|
+
return new RegExp(pattern);
|
|
18803
|
+
} catch {
|
|
18804
|
+
return null;
|
|
18805
|
+
}
|
|
18806
|
+
}
|
|
18807
|
+
|
|
18808
|
+
// src/commands/configure.ts
|
|
18809
|
+
import { join as join9 } from "path";
|
|
18810
|
+
var WRITERS2 = {
|
|
18811
|
+
"claude-code": writeClaudeCodeConfig,
|
|
18812
|
+
opencode: writeOpencodeConfig,
|
|
18813
|
+
codex: writeCodexConfig
|
|
18814
|
+
};
|
|
18815
|
+
var ALL_TOOLS = ["claude-code", "opencode", "codex"];
|
|
18816
|
+
var SCOPES = ["local", "project"];
|
|
18817
|
+
var configureCommand = defineCommand({
|
|
18818
|
+
meta: {
|
|
18819
|
+
name: "configure",
|
|
18820
|
+
description: "Configure coding tools to route through the gateway."
|
|
18821
|
+
},
|
|
18822
|
+
args: {
|
|
18823
|
+
tool: {
|
|
18824
|
+
type: "positional",
|
|
18825
|
+
required: false,
|
|
18826
|
+
description: "Tool to configure. Prompted if omitted. One of: claude-code, opencode, codex, all."
|
|
18827
|
+
},
|
|
18828
|
+
scope: {
|
|
18829
|
+
type: "string",
|
|
18830
|
+
description: "Settings scope: local (default, per-repo, gitignored) or project (committed). For user-scope (global) setup, use `codevector system configure`.",
|
|
18831
|
+
valueHint: "local|project"
|
|
18832
|
+
},
|
|
18833
|
+
all: {
|
|
18834
|
+
type: "boolean",
|
|
18835
|
+
description: "Configure every supported tool."
|
|
18836
|
+
}
|
|
18837
|
+
},
|
|
18838
|
+
async run({ args }) {
|
|
18839
|
+
const initialProfiles = await readProfiles();
|
|
18840
|
+
if (!initialProfiles) {
|
|
18841
|
+
throw new Error("Not signed in. Run `codevector auth login` first.");
|
|
18842
|
+
}
|
|
18843
|
+
const activeProfileName = initialProfiles.activeProfile;
|
|
18844
|
+
const creds = initialProfiles.profiles[activeProfileName];
|
|
18845
|
+
if (!creds) {
|
|
18846
|
+
throw new Error(
|
|
18847
|
+
`Active profile "${activeProfileName}" is missing from credentials. Run \`codevector auth login\` to recover.`
|
|
18848
|
+
);
|
|
18849
|
+
}
|
|
18850
|
+
ge("codevector configure");
|
|
18851
|
+
const tools = await resolveTools(args);
|
|
18852
|
+
const scope = await resolveScope(args.scope, tools);
|
|
18853
|
+
const project = scope === "user" ? void 0 : resolveProjectContext(userCwd()).project ?? void 0;
|
|
18854
|
+
if (scope !== "user") {
|
|
18855
|
+
R2.info(
|
|
18856
|
+
project ? `Project: ${project} (used for x-project attribution header)` : "No project detected (no git remote and no .codevector.json)."
|
|
18857
|
+
);
|
|
18858
|
+
}
|
|
18859
|
+
const reachable = await fetchReachableChatModels2(creds);
|
|
18860
|
+
const model = await pickPinnedModel(reachable);
|
|
18861
|
+
const hookPath = installAcceptanceHook();
|
|
18862
|
+
const results = [];
|
|
18863
|
+
for (const tool of tools) {
|
|
18864
|
+
const writer = WRITERS2[tool];
|
|
18865
|
+
if (!writer) {
|
|
18866
|
+
results.push({
|
|
18867
|
+
tool,
|
|
18868
|
+
status: "skipped",
|
|
18869
|
+
path: "",
|
|
18870
|
+
scope,
|
|
18871
|
+
notes: `Unsupported tool "${tool}". Known: ${Object.keys(WRITERS2).join(", ")}.`
|
|
18872
|
+
});
|
|
18873
|
+
continue;
|
|
18874
|
+
}
|
|
18875
|
+
try {
|
|
18876
|
+
results.push(
|
|
18877
|
+
writer({
|
|
18878
|
+
gatewayUrl: creds.gatewayUrl,
|
|
18879
|
+
apiKey: creds.apiKey,
|
|
18880
|
+
hookScriptPath: hookPath,
|
|
18881
|
+
scope,
|
|
18882
|
+
...project ? { project } : {},
|
|
18883
|
+
...model ? { model } : {},
|
|
18884
|
+
availableModels: reachable.map((m2) => ({
|
|
18885
|
+
slug: m2.slug,
|
|
18886
|
+
displayName: m2.displayName,
|
|
18887
|
+
contextWindow: m2.contextWindow,
|
|
18888
|
+
maxOutputTokens: m2.maxOutputTokens,
|
|
18889
|
+
maxInputTokens: m2.maxInputTokens,
|
|
18890
|
+
inputPricePerMtok: m2.inputPricePerMtok,
|
|
18891
|
+
cachedInputPricePerMtok: m2.cachedInputPricePerMtok,
|
|
18892
|
+
cacheWritePricePerMtok: m2.cacheWritePricePerMtok,
|
|
18893
|
+
reasoningPricePerMtok: m2.reasoningPricePerMtok,
|
|
18894
|
+
outputPricePerMtok: m2.outputPricePerMtok,
|
|
18895
|
+
supportsReasoning: m2.supportsReasoning,
|
|
18896
|
+
supportsAttachment: m2.supportsAttachment,
|
|
18897
|
+
supportsToolCall: m2.supportsToolCall,
|
|
18898
|
+
supportsTemperature: m2.supportsTemperature,
|
|
18899
|
+
inputModalities: m2.inputModalities,
|
|
18900
|
+
outputModalities: m2.outputModalities,
|
|
18901
|
+
releaseDate: m2.releaseDate,
|
|
18902
|
+
family: m2.family
|
|
18903
|
+
}))
|
|
18904
|
+
})
|
|
18905
|
+
);
|
|
18906
|
+
} catch (err) {
|
|
18907
|
+
results.push({
|
|
18908
|
+
tool,
|
|
18909
|
+
status: "skipped",
|
|
18910
|
+
path: "",
|
|
18911
|
+
scope,
|
|
18912
|
+
notes: err instanceof Error ? err.message : String(err)
|
|
18913
|
+
});
|
|
18914
|
+
}
|
|
18915
|
+
}
|
|
18916
|
+
const persisted = [];
|
|
18917
|
+
for (const r of results) {
|
|
18918
|
+
if (r.status === "configured") {
|
|
18919
|
+
persisted.push({
|
|
18920
|
+
tool: r.tool,
|
|
18921
|
+
scope: r.scope,
|
|
18922
|
+
...model ? { modelSlug: model.slug } : {}
|
|
18923
|
+
});
|
|
18924
|
+
}
|
|
18925
|
+
}
|
|
18926
|
+
if (persisted.length > 0) {
|
|
18927
|
+
updateProfileToolConfigs(activeProfileName, persisted);
|
|
18928
|
+
if (scope !== "user") {
|
|
18929
|
+
mergeToolsIntoProjectConfig(userCwd(), persisted, creds.gatewayUrl);
|
|
18930
|
+
}
|
|
18931
|
+
}
|
|
18932
|
+
for (const r of results) {
|
|
18933
|
+
if (r.status === "configured") {
|
|
18934
|
+
R2.success(`${r.tool} \u2192 ${r.path} [${r.scope}]`);
|
|
18935
|
+
if (r.notes) R2.info(r.notes);
|
|
18936
|
+
} else {
|
|
18937
|
+
R2.warn(`${r.tool}: skipped \u2014 ${r.notes ?? "unknown reason"}`);
|
|
18938
|
+
}
|
|
18939
|
+
}
|
|
18940
|
+
if (scope === "local" && tools.includes("claude-code")) {
|
|
18941
|
+
Se(
|
|
18942
|
+
"Claude Code auto-ignores .claude/settings.local.json in git; your API key stays on this machine.",
|
|
18943
|
+
"Local scope"
|
|
18944
|
+
);
|
|
18945
|
+
}
|
|
18946
|
+
ye("Done.");
|
|
18947
|
+
}
|
|
18948
|
+
});
|
|
18949
|
+
async function resolveTools(args) {
|
|
18950
|
+
if (args.all) return [...ALL_TOOLS];
|
|
18951
|
+
if (args.tool) {
|
|
18952
|
+
if (args.tool === "all") return [...ALL_TOOLS];
|
|
18953
|
+
if (isTool(args.tool)) return [args.tool];
|
|
18954
|
+
throw new Error(
|
|
18955
|
+
`Unsupported tool "${args.tool}". Known: ${ALL_TOOLS.join(", ")}, or pass --all.`
|
|
18956
|
+
);
|
|
18957
|
+
}
|
|
18958
|
+
Se(
|
|
18959
|
+
"Use arrow keys to move, space to toggle, a to toggle all, enter to confirm.",
|
|
18960
|
+
"Controls"
|
|
18961
|
+
);
|
|
18962
|
+
const picked = unwrap(
|
|
18963
|
+
await ve({
|
|
18964
|
+
message: "Which tools do you want to configure? (space to select/deselect, enter to confirm)",
|
|
18965
|
+
options: ALL_TOOLS.map((t) => ({ value: t, label: t })),
|
|
18966
|
+
initialValues: ["claude-code"],
|
|
18967
|
+
required: true
|
|
18968
|
+
})
|
|
18969
|
+
);
|
|
18970
|
+
return picked;
|
|
18743
18971
|
}
|
|
18744
18972
|
async function resolveScope(raw, tools) {
|
|
18745
18973
|
if (raw === "user") {
|
|
@@ -18798,10 +19026,22 @@ function relativizeHomeAndCwd(absolutePath) {
|
|
|
18798
19026
|
function isTool(value) {
|
|
18799
19027
|
return ALL_TOOLS.includes(value);
|
|
18800
19028
|
}
|
|
19029
|
+
function mergeToolsIntoProjectConfig(cwd, tools, gatewayUrl) {
|
|
19030
|
+
const path = join9(cwd, PROJECT_CONFIG_FILENAME);
|
|
19031
|
+
const existing = readProjectConfigAt(path) ?? {};
|
|
19032
|
+
const byTool = new Map((existing.tools ?? []).map((c) => [c.tool, c]));
|
|
19033
|
+
for (const cfg of tools) byTool.set(cfg.tool, cfg);
|
|
19034
|
+
const next = {
|
|
19035
|
+
...existing,
|
|
19036
|
+
...existing.gateway ? {} : { gateway: gatewayUrl },
|
|
19037
|
+
tools: [...byTool.values()]
|
|
19038
|
+
};
|
|
19039
|
+
writeProjectConfig(path, next);
|
|
19040
|
+
}
|
|
18801
19041
|
function isScope(value) {
|
|
18802
19042
|
return SCOPES.includes(value);
|
|
18803
19043
|
}
|
|
18804
|
-
async function
|
|
19044
|
+
async function fetchReachableChatModels2(creds) {
|
|
18805
19045
|
const client = gatewayClient(creds.gatewayUrl, creds.apiKey, 1e4);
|
|
18806
19046
|
const s = ft();
|
|
18807
19047
|
s.start("Loading reachable models\u2026");
|
|
@@ -18869,7 +19109,74 @@ async function pickPinnedModel(models) {
|
|
|
18869
19109
|
}
|
|
18870
19110
|
|
|
18871
19111
|
// src/commands/doctor.ts
|
|
18872
|
-
import { existsSync as
|
|
19112
|
+
import { existsSync as existsSync8, readFileSync as readFileSync9, statSync as statSync5 } from "fs";
|
|
19113
|
+
|
|
19114
|
+
// src/lib/install-pref.ts
|
|
19115
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync6, readFileSync as readFileSync8, realpathSync, renameSync as renameSync4, writeFileSync as writeFileSync7 } from "fs";
|
|
19116
|
+
import { join as join10, sep } from "path";
|
|
19117
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
19118
|
+
var INSTALL_PREF_FILE = join10(CODEVECTOR_CONFIG_DIR, "install.json");
|
|
19119
|
+
var PACKAGE_MANAGERS = ["npm", "pnpm", "yarn"];
|
|
19120
|
+
function isPackageManager(v2) {
|
|
19121
|
+
return typeof v2 === "string" && PACKAGE_MANAGERS.includes(v2);
|
|
19122
|
+
}
|
|
19123
|
+
function readInstallPref() {
|
|
19124
|
+
if (!existsSync7(INSTALL_PREF_FILE)) return void 0;
|
|
19125
|
+
try {
|
|
19126
|
+
const raw = readFileSync8(INSTALL_PREF_FILE, "utf8");
|
|
19127
|
+
const parsed = JSON.parse(raw);
|
|
19128
|
+
if (!isPackageManager(parsed.packageManager)) return void 0;
|
|
19129
|
+
const source = parsed.source === "user" ? "user" : "auto";
|
|
19130
|
+
return {
|
|
19131
|
+
packageManager: parsed.packageManager,
|
|
19132
|
+
source,
|
|
19133
|
+
detectedAt: typeof parsed.detectedAt === "string" ? parsed.detectedAt : (/* @__PURE__ */ new Date()).toISOString()
|
|
19134
|
+
};
|
|
19135
|
+
} catch {
|
|
19136
|
+
return void 0;
|
|
19137
|
+
}
|
|
19138
|
+
}
|
|
19139
|
+
function writeInstallPref(pref) {
|
|
19140
|
+
mkdirSync6(CODEVECTOR_CONFIG_DIR, { recursive: true, mode: 448 });
|
|
19141
|
+
const tmp = `${INSTALL_PREF_FILE}.${process.pid}.tmp`;
|
|
19142
|
+
writeFileSync7(tmp, JSON.stringify(pref, null, 2));
|
|
19143
|
+
renameSync4(tmp, INSTALL_PREF_FILE);
|
|
19144
|
+
}
|
|
19145
|
+
function packageManagerFromPath(installPath) {
|
|
19146
|
+
if (installPath.includes(`${sep}.pnpm${sep}`)) return "pnpm";
|
|
19147
|
+
if (installPath.includes(`${sep}.config${sep}yarn${sep}global${sep}`) || installPath.includes(`${sep}.yarn${sep}`)) {
|
|
19148
|
+
return "yarn";
|
|
19149
|
+
}
|
|
19150
|
+
if (installPath.includes(`${sep}lib${sep}node_modules${sep}`)) return "npm";
|
|
19151
|
+
return void 0;
|
|
19152
|
+
}
|
|
19153
|
+
function detectPackageManagerFromInstallPath() {
|
|
19154
|
+
try {
|
|
19155
|
+
const real = realpathSync(fileURLToPath2(import.meta.url));
|
|
19156
|
+
return packageManagerFromPath(real);
|
|
19157
|
+
} catch {
|
|
19158
|
+
return void 0;
|
|
19159
|
+
}
|
|
19160
|
+
}
|
|
19161
|
+
function resolveInstallManager() {
|
|
19162
|
+
const pref = readInstallPref();
|
|
19163
|
+
if (pref?.source === "user") return { packageManager: pref.packageManager, source: "user" };
|
|
19164
|
+
const fromPath = detectPackageManagerFromInstallPath();
|
|
19165
|
+
if (fromPath) {
|
|
19166
|
+
if (pref?.packageManager !== fromPath) {
|
|
19167
|
+
writeInstallPref({
|
|
19168
|
+
packageManager: fromPath,
|
|
19169
|
+
source: "auto",
|
|
19170
|
+
detectedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
19171
|
+
});
|
|
19172
|
+
}
|
|
19173
|
+
return { packageManager: fromPath, source: "path" };
|
|
19174
|
+
}
|
|
19175
|
+
if (pref) return { packageManager: pref.packageManager, source: "auto" };
|
|
19176
|
+
return { packageManager: void 0, source: "unknown" };
|
|
19177
|
+
}
|
|
19178
|
+
|
|
19179
|
+
// src/commands/doctor.ts
|
|
18873
19180
|
var CLAUDE_SCOPE_ORDER = ["local", "project", "user"];
|
|
18874
19181
|
var doctorCommand = defineCommand({
|
|
18875
19182
|
meta: {
|
|
@@ -18881,44 +19188,43 @@ var doctorCommand = defineCommand({
|
|
|
18881
19188
|
const creds = await readCredentials();
|
|
18882
19189
|
if (!creds) {
|
|
18883
19190
|
checks.push({
|
|
18884
|
-
level: "
|
|
19191
|
+
level: "warn",
|
|
18885
19192
|
label: "credentials",
|
|
18886
|
-
detail: `no credentials at ${CREDENTIALS_FILE}
|
|
19193
|
+
detail: `not logged in \u2014 no credentials at ${CREDENTIALS_FILE}. Run \`codevector auth login\`. Local checks still run.`
|
|
18887
19194
|
});
|
|
18888
|
-
|
|
18889
|
-
|
|
18890
|
-
|
|
18891
|
-
|
|
18892
|
-
|
|
18893
|
-
|
|
18894
|
-
|
|
18895
|
-
|
|
18896
|
-
});
|
|
18897
|
-
checks.push(
|
|
18898
|
-
credentialsFileModeOk() ? { level: "ok", label: "credentials permissions", detail: "chmod 600" } : {
|
|
18899
|
-
level: "warn",
|
|
18900
|
-
label: "credentials permissions",
|
|
18901
|
-
detail: `expected 0600 on ${CREDENTIALS_FILE}`
|
|
18902
|
-
}
|
|
18903
|
-
);
|
|
18904
|
-
const client = gatewayClient(creds.gatewayUrl, creds.apiKey, 1e4);
|
|
18905
|
-
try {
|
|
18906
|
-
const me2 = await call(parseResponse(client.me.$get()));
|
|
18907
|
-
if (me2.user.id !== creds.userId) {
|
|
18908
|
-
checks.push({
|
|
19195
|
+
} else {
|
|
19196
|
+
checks.push({
|
|
19197
|
+
level: "ok",
|
|
19198
|
+
label: "credentials",
|
|
19199
|
+
detail: `${creds.email} @ ${creds.gatewayUrl} (${maskApiKey(creds.apiKey)})`
|
|
19200
|
+
});
|
|
19201
|
+
checks.push(
|
|
19202
|
+
credentialsFileModeOk() ? { level: "ok", label: "credentials permissions", detail: "chmod 600" } : {
|
|
18909
19203
|
level: "warn",
|
|
18910
|
-
label: "
|
|
18911
|
-
detail:
|
|
18912
|
-
}
|
|
18913
|
-
|
|
18914
|
-
|
|
19204
|
+
label: "credentials permissions",
|
|
19205
|
+
detail: `expected 0600 on ${CREDENTIALS_FILE}`
|
|
19206
|
+
}
|
|
19207
|
+
);
|
|
19208
|
+
const client = gatewayClient(creds.gatewayUrl, creds.apiKey, 1e4);
|
|
19209
|
+
try {
|
|
19210
|
+
const me2 = await call(parseResponse(client.me.$get()));
|
|
19211
|
+
if (me2.user.id !== creds.userId) {
|
|
19212
|
+
checks.push({
|
|
19213
|
+
level: "warn",
|
|
19214
|
+
label: "gateway /me",
|
|
19215
|
+
detail: "user id mismatch \u2014 credentials may be stale. Re-run `codevector auth login`."
|
|
19216
|
+
});
|
|
19217
|
+
} else {
|
|
19218
|
+
checks.push({ level: "ok", label: "gateway /me", detail: "200 OK" });
|
|
19219
|
+
}
|
|
19220
|
+
} catch (err) {
|
|
19221
|
+
const message = err instanceof ApiClientError ? `${err.code}: ${err.message}` : String(err);
|
|
19222
|
+
checks.push({ level: "fail", label: "gateway /me", detail: message });
|
|
18915
19223
|
}
|
|
18916
|
-
} catch (err) {
|
|
18917
|
-
const message = err instanceof ApiClientError ? `${err.code}: ${err.message}` : String(err);
|
|
18918
|
-
checks.push({ level: "fail", label: "gateway /me", detail: message });
|
|
18919
19224
|
}
|
|
18920
19225
|
checks.push(inspectClaudeSettings());
|
|
18921
|
-
|
|
19226
|
+
checks.push(...inspectManifestDrift());
|
|
19227
|
+
if (!existsSync8(ACCEPTANCE_HOOK_FILE)) {
|
|
18922
19228
|
checks.push({
|
|
18923
19229
|
level: "warn",
|
|
18924
19230
|
label: "acceptance hook",
|
|
@@ -18934,18 +19240,35 @@ var doctorCommand = defineCommand({
|
|
|
18934
19240
|
}
|
|
18935
19241
|
);
|
|
18936
19242
|
}
|
|
19243
|
+
checks.push(inspectUpdateManager());
|
|
18937
19244
|
emit(checks);
|
|
18938
19245
|
if (checks.some((c) => c.level === "fail")) {
|
|
18939
19246
|
process.exitCode = 1;
|
|
18940
19247
|
}
|
|
18941
19248
|
}
|
|
18942
19249
|
});
|
|
19250
|
+
function inspectUpdateManager() {
|
|
19251
|
+
const resolved = resolveInstallManager();
|
|
19252
|
+
if (!resolved.packageManager) {
|
|
19253
|
+
return {
|
|
19254
|
+
level: "warn",
|
|
19255
|
+
label: "update manager",
|
|
19256
|
+
detail: "couldn't determine which package manager installed the CLI \u2014 `codevector update` will have to guess. Pin it with `codevector update --with <npm|pnpm|yarn>`."
|
|
19257
|
+
};
|
|
19258
|
+
}
|
|
19259
|
+
const how = resolved.source === "user" ? "pinned via --with" : resolved.source === "path" ? "detected from install path" : "auto-detected at install";
|
|
19260
|
+
return {
|
|
19261
|
+
level: "ok",
|
|
19262
|
+
label: "update manager",
|
|
19263
|
+
detail: `${resolved.packageManager} (${how})`
|
|
19264
|
+
};
|
|
19265
|
+
}
|
|
18943
19266
|
function inspectClaudeSettings() {
|
|
18944
19267
|
for (const scope of CLAUDE_SCOPE_ORDER) {
|
|
18945
19268
|
const path = claudeSettingsPath(scope);
|
|
18946
|
-
if (!
|
|
19269
|
+
if (!existsSync8(path)) continue;
|
|
18947
19270
|
try {
|
|
18948
|
-
const raw = JSON.parse(
|
|
19271
|
+
const raw = JSON.parse(readFileSync9(path, "utf8"));
|
|
18949
19272
|
if (typeof raw.env?.ANTHROPIC_BASE_URL === "string") {
|
|
18950
19273
|
return {
|
|
18951
19274
|
level: "ok",
|
|
@@ -18961,36 +19284,339 @@ function inspectClaudeSettings() {
|
|
|
18961
19284
|
};
|
|
18962
19285
|
}
|
|
18963
19286
|
}
|
|
18964
|
-
return {
|
|
18965
|
-
level: "warn",
|
|
18966
|
-
label: "claude-code settings",
|
|
18967
|
-
detail: "ANTHROPIC_BASE_URL not set at any scope \u2014 run `codevector configure claude-code`"
|
|
18968
|
-
};
|
|
19287
|
+
return {
|
|
19288
|
+
level: "warn",
|
|
19289
|
+
label: "claude-code settings",
|
|
19290
|
+
detail: "ANTHROPIC_BASE_URL not set at any scope \u2014 run `codevector configure claude-code`"
|
|
19291
|
+
};
|
|
19292
|
+
}
|
|
19293
|
+
function inspectManifestDrift() {
|
|
19294
|
+
const found = readProjectConfig(userCwd());
|
|
19295
|
+
if (!found) return [];
|
|
19296
|
+
const { config: config2 } = found;
|
|
19297
|
+
const tools = config2.tools ?? [];
|
|
19298
|
+
if (tools.length === 0) return [];
|
|
19299
|
+
const gateway = config2.gateway?.replace(/\/$/, "") ?? null;
|
|
19300
|
+
if (!gateway) {
|
|
19301
|
+
return [
|
|
19302
|
+
{
|
|
19303
|
+
level: "warn",
|
|
19304
|
+
label: "manifest drift",
|
|
19305
|
+
detail: ".codevector.json lists tools but has no `gateway` \u2014 run `codevector init`"
|
|
19306
|
+
}
|
|
19307
|
+
];
|
|
19308
|
+
}
|
|
19309
|
+
const checks = [];
|
|
19310
|
+
for (const tool of tools) {
|
|
19311
|
+
const path = manifestToolPath(tool.tool, tool.scope);
|
|
19312
|
+
if (!path) {
|
|
19313
|
+
checks.push({
|
|
19314
|
+
level: "warn",
|
|
19315
|
+
label: `manifest drift: ${tool.tool}`,
|
|
19316
|
+
detail: `unknown tool "${tool.tool}"`
|
|
19317
|
+
});
|
|
19318
|
+
continue;
|
|
19319
|
+
}
|
|
19320
|
+
if (!existsSync8(path)) {
|
|
19321
|
+
checks.push({
|
|
19322
|
+
level: "fail",
|
|
19323
|
+
label: `manifest drift: ${tool.tool}`,
|
|
19324
|
+
detail: `${path} missing \u2014 run \`codevector config sync\``
|
|
19325
|
+
});
|
|
19326
|
+
continue;
|
|
19327
|
+
}
|
|
19328
|
+
let raw;
|
|
19329
|
+
try {
|
|
19330
|
+
raw = readFileSync9(path, "utf8");
|
|
19331
|
+
} catch (err) {
|
|
19332
|
+
checks.push({
|
|
19333
|
+
level: "fail",
|
|
19334
|
+
label: `manifest drift: ${tool.tool}`,
|
|
19335
|
+
detail: `cannot read ${path}: ${err instanceof Error ? err.message : String(err)}`
|
|
19336
|
+
});
|
|
19337
|
+
continue;
|
|
19338
|
+
}
|
|
19339
|
+
if (!raw.includes(gateway)) {
|
|
19340
|
+
checks.push({
|
|
19341
|
+
level: "fail",
|
|
19342
|
+
label: `manifest drift: ${tool.tool}`,
|
|
19343
|
+
detail: `${path} does not reference ${gateway} \u2014 run \`codevector config sync\``
|
|
19344
|
+
});
|
|
19345
|
+
continue;
|
|
19346
|
+
}
|
|
19347
|
+
checks.push({
|
|
19348
|
+
level: "ok",
|
|
19349
|
+
label: `manifest: ${tool.tool}`,
|
|
19350
|
+
detail: `[${tool.scope}] ${path}`
|
|
19351
|
+
});
|
|
19352
|
+
}
|
|
19353
|
+
return checks;
|
|
19354
|
+
}
|
|
19355
|
+
function manifestToolPath(tool, scope) {
|
|
19356
|
+
switch (tool) {
|
|
19357
|
+
case "claude-code":
|
|
19358
|
+
return claudeSettingsPath(scope);
|
|
19359
|
+
case "opencode":
|
|
19360
|
+
return opencodeSettingsPath(scope);
|
|
19361
|
+
case "codex":
|
|
19362
|
+
return codexConfigPath(scope);
|
|
19363
|
+
default:
|
|
19364
|
+
return null;
|
|
19365
|
+
}
|
|
19366
|
+
}
|
|
19367
|
+
function emit(checks) {
|
|
19368
|
+
Se(
|
|
19369
|
+
checks.map((c) => {
|
|
19370
|
+
const icon = c.level === "ok" ? "\u2713" : c.level === "warn" ? "!" : "\u2717";
|
|
19371
|
+
const detail = c.detail ? ` \u2014 ${c.detail}` : "";
|
|
19372
|
+
return `${icon} ${c.label}${detail}`;
|
|
19373
|
+
}).join("\n"),
|
|
19374
|
+
"Doctor"
|
|
19375
|
+
);
|
|
19376
|
+
for (const c of checks) {
|
|
19377
|
+
if (c.level === "fail") R2.error(c.label);
|
|
19378
|
+
else if (c.level === "warn") R2.warn(c.label);
|
|
19379
|
+
}
|
|
19380
|
+
}
|
|
19381
|
+
|
|
19382
|
+
// src/commands/env.ts
|
|
19383
|
+
var SHELLS = ["bash", "zsh", "fish"];
|
|
19384
|
+
var envCommand = defineCommand({
|
|
19385
|
+
meta: {
|
|
19386
|
+
name: "env",
|
|
19387
|
+
description: "Print shell exports to activate / deactivate codevector credentials for the current directory. Intended to be eval'd by the shell hook (see `codevector hook`)."
|
|
19388
|
+
},
|
|
19389
|
+
args: {
|
|
19390
|
+
shell: {
|
|
19391
|
+
type: "string",
|
|
19392
|
+
description: "Output dialect: bash, zsh, or fish.",
|
|
19393
|
+
valueHint: "bash|zsh|fish"
|
|
19394
|
+
}
|
|
19395
|
+
},
|
|
19396
|
+
async run({ args }) {
|
|
19397
|
+
const shell = resolveShell(args.shell);
|
|
19398
|
+
const cwd = userCwd();
|
|
19399
|
+
const previousDir = process.env.CODEVECTOR_ACTIVE_DIR ?? null;
|
|
19400
|
+
const found = readProjectConfig(cwd);
|
|
19401
|
+
const repoDir = found?.path ? dirOf(found.path) : null;
|
|
19402
|
+
const gateway = found?.config.gateway ?? null;
|
|
19403
|
+
const projectName = found?.config.projectName ?? null;
|
|
19404
|
+
const lines = [];
|
|
19405
|
+
if (!gateway || !repoDir) {
|
|
19406
|
+
if (previousDir) emitDeactivate(lines, shell, previousDir);
|
|
19407
|
+
process.stdout.write(lines.join("\n") + (lines.length > 0 ? "\n" : ""));
|
|
19408
|
+
return;
|
|
19409
|
+
}
|
|
19410
|
+
if (previousDir === repoDir) {
|
|
19411
|
+
return;
|
|
19412
|
+
}
|
|
19413
|
+
const profiles = await readProfiles();
|
|
19414
|
+
const match = pickProfileForGateway(
|
|
19415
|
+
profiles?.profiles ?? {},
|
|
19416
|
+
profiles?.activeProfile ?? null,
|
|
19417
|
+
gateway
|
|
19418
|
+
);
|
|
19419
|
+
if (!match) {
|
|
19420
|
+
if (previousDir) emitDeactivate(lines, shell, previousDir);
|
|
19421
|
+
emitEcho(
|
|
19422
|
+
lines,
|
|
19423
|
+
shell,
|
|
19424
|
+
`codevector: no matching profile for gateway ${gateway}. Run \`codevector auth login --gateway ${gateway}\`.`
|
|
19425
|
+
);
|
|
19426
|
+
process.stdout.write(lines.join("\n") + "\n");
|
|
19427
|
+
return;
|
|
19428
|
+
}
|
|
19429
|
+
const { name: profileName, profile } = match;
|
|
19430
|
+
const headers = buildAnthropicCustomHeaders(projectName);
|
|
19431
|
+
emitExport(lines, shell, "ANTHROPIC_BASE_URL", `${trimRightSlash6(profile.gatewayUrl)}/gateway/anthropic`);
|
|
19432
|
+
emitExport(lines, shell, "ANTHROPIC_API_KEY", profile.apiKey);
|
|
19433
|
+
if (headers) emitExport(lines, shell, "ANTHROPIC_CUSTOM_HEADERS", headers);
|
|
19434
|
+
emitExport(lines, shell, "OPENAI_BASE_URL", `${trimRightSlash6(profile.gatewayUrl)}/gateway/openai/v1`);
|
|
19435
|
+
emitExport(lines, shell, "OPENAI_API_KEY", profile.apiKey);
|
|
19436
|
+
emitExport(lines, shell, "CODEVECTOR_ACTIVE_DIR", repoDir);
|
|
19437
|
+
emitExport(lines, shell, "CODEVECTOR_ACTIVE_PROFILE", profileName);
|
|
19438
|
+
const projectSuffix = projectName ? ` (project: ${projectName})` : "";
|
|
19439
|
+
emitEcho(
|
|
19440
|
+
lines,
|
|
19441
|
+
shell,
|
|
19442
|
+
`codevector: ${profileName} -> ${trimRightSlash6(profile.gatewayUrl)}${projectSuffix}`
|
|
19443
|
+
);
|
|
19444
|
+
process.stdout.write(lines.join("\n") + "\n");
|
|
19445
|
+
}
|
|
19446
|
+
});
|
|
19447
|
+
function resolveShell(raw) {
|
|
19448
|
+
if (!raw) return detectShell();
|
|
19449
|
+
if (!SHELLS.includes(raw)) {
|
|
19450
|
+
throw new Error(`Invalid --shell "${raw}". Use one of: ${SHELLS.join(", ")}.`);
|
|
19451
|
+
}
|
|
19452
|
+
return raw;
|
|
19453
|
+
}
|
|
19454
|
+
function detectShell() {
|
|
19455
|
+
const shellPath = process.env.SHELL ?? "";
|
|
19456
|
+
if (shellPath.endsWith("/fish")) return "fish";
|
|
19457
|
+
if (shellPath.endsWith("/zsh")) return "zsh";
|
|
19458
|
+
return "bash";
|
|
19459
|
+
}
|
|
19460
|
+
function emitExport(lines, shell, name, value) {
|
|
19461
|
+
const quoted = shellQuote(value);
|
|
19462
|
+
if (shell === "fish") {
|
|
19463
|
+
lines.push(`set -gx ${name} ${quoted}`);
|
|
19464
|
+
} else {
|
|
19465
|
+
lines.push(`export ${name}=${quoted}`);
|
|
19466
|
+
}
|
|
18969
19467
|
}
|
|
18970
|
-
function
|
|
18971
|
-
|
|
18972
|
-
|
|
18973
|
-
|
|
18974
|
-
|
|
18975
|
-
|
|
18976
|
-
|
|
18977
|
-
|
|
19468
|
+
function emitUnset(lines, shell, name) {
|
|
19469
|
+
if (shell === "fish") {
|
|
19470
|
+
lines.push(`set -e ${name}`);
|
|
19471
|
+
} else {
|
|
19472
|
+
lines.push(`unset ${name}`);
|
|
19473
|
+
}
|
|
19474
|
+
}
|
|
19475
|
+
function emitEcho(lines, _shell, message) {
|
|
19476
|
+
lines.push(`echo ${shellQuote(message)} 1>&2`);
|
|
19477
|
+
}
|
|
19478
|
+
function emitDeactivate(lines, shell, previousDir) {
|
|
19479
|
+
for (const name of [
|
|
19480
|
+
"ANTHROPIC_BASE_URL",
|
|
19481
|
+
"ANTHROPIC_API_KEY",
|
|
19482
|
+
"ANTHROPIC_CUSTOM_HEADERS",
|
|
19483
|
+
"OPENAI_BASE_URL",
|
|
19484
|
+
"OPENAI_API_KEY",
|
|
19485
|
+
"CODEVECTOR_ACTIVE_DIR",
|
|
19486
|
+
"CODEVECTOR_ACTIVE_PROFILE"
|
|
19487
|
+
]) {
|
|
19488
|
+
emitUnset(lines, shell, name);
|
|
19489
|
+
}
|
|
19490
|
+
emitEcho(lines, shell, `codevector: left ${previousDir}, credentials cleared.`);
|
|
19491
|
+
}
|
|
19492
|
+
function shellQuote(value) {
|
|
19493
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
19494
|
+
}
|
|
19495
|
+
function trimRightSlash6(url2) {
|
|
19496
|
+
return url2.endsWith("/") ? url2.slice(0, -1) : url2;
|
|
19497
|
+
}
|
|
19498
|
+
function dirOf(filePath) {
|
|
19499
|
+
const idx = filePath.lastIndexOf("/");
|
|
19500
|
+
return idx === -1 ? filePath : filePath.slice(0, idx);
|
|
19501
|
+
}
|
|
19502
|
+
function buildAnthropicCustomHeaders(projectName) {
|
|
19503
|
+
if (!projectName) return null;
|
|
19504
|
+
const safe = projectName.replace(/[\r\n]/g, "").trim();
|
|
19505
|
+
if (!safe) return null;
|
|
19506
|
+
return `x-project: ${safe}`;
|
|
19507
|
+
}
|
|
19508
|
+
function pickProfileForGateway(profiles, activeProfile, gateway) {
|
|
19509
|
+
const target = trimRightSlash6(gateway);
|
|
19510
|
+
const matches = Object.entries(profiles).filter(
|
|
19511
|
+
([, p2]) => trimRightSlash6(p2.gatewayUrl) === target
|
|
18978
19512
|
);
|
|
18979
|
-
|
|
18980
|
-
|
|
18981
|
-
|
|
19513
|
+
if (matches.length === 0) return null;
|
|
19514
|
+
if (activeProfile) {
|
|
19515
|
+
const active = matches.find(([name]) => name === activeProfile);
|
|
19516
|
+
if (active) return { name: active[0], profile: active[1] };
|
|
18982
19517
|
}
|
|
19518
|
+
const sorted = [...matches].sort((a, b2) => a[0].localeCompare(b2[0]));
|
|
19519
|
+
const picked = sorted[0];
|
|
19520
|
+
return { name: picked[0], profile: picked[1] };
|
|
18983
19521
|
}
|
|
18984
19522
|
|
|
19523
|
+
// src/commands/hook.ts
|
|
19524
|
+
var SHELLS2 = ["bash", "zsh", "fish"];
|
|
19525
|
+
var hookCommand = defineCommand({
|
|
19526
|
+
meta: {
|
|
19527
|
+
name: "hook",
|
|
19528
|
+
description: 'Print the shell snippet that auto-activates codevector credentials on cd. Add `eval "$(codevector hook bash)"` (or zsh / fish) to your shell\'s rc file.'
|
|
19529
|
+
},
|
|
19530
|
+
args: {
|
|
19531
|
+
shell: {
|
|
19532
|
+
type: "positional",
|
|
19533
|
+
required: false,
|
|
19534
|
+
description: "Target shell: bash, zsh, or fish.",
|
|
19535
|
+
valueHint: "bash|zsh|fish"
|
|
19536
|
+
}
|
|
19537
|
+
},
|
|
19538
|
+
run({ args }) {
|
|
19539
|
+
const shell = resolveShell2(args.shell);
|
|
19540
|
+
process.stdout.write(snippetFor(shell));
|
|
19541
|
+
}
|
|
19542
|
+
});
|
|
19543
|
+
function resolveShell2(raw) {
|
|
19544
|
+
if (!raw) {
|
|
19545
|
+
const detected = detectShell2();
|
|
19546
|
+
if (!detected) {
|
|
19547
|
+
throw new Error(
|
|
19548
|
+
`Could not detect shell from $SHELL. Pass one explicitly: ${SHELLS2.join(", ")}.`
|
|
19549
|
+
);
|
|
19550
|
+
}
|
|
19551
|
+
return detected;
|
|
19552
|
+
}
|
|
19553
|
+
if (!SHELLS2.includes(raw)) {
|
|
19554
|
+
throw new Error(`Invalid shell "${raw}". Use one of: ${SHELLS2.join(", ")}.`);
|
|
19555
|
+
}
|
|
19556
|
+
return raw;
|
|
19557
|
+
}
|
|
19558
|
+
function detectShell2() {
|
|
19559
|
+
const shellPath = process.env.SHELL ?? "";
|
|
19560
|
+
if (shellPath.endsWith("/fish")) return "fish";
|
|
19561
|
+
if (shellPath.endsWith("/zsh")) return "zsh";
|
|
19562
|
+
if (shellPath.endsWith("/bash")) return "bash";
|
|
19563
|
+
return null;
|
|
19564
|
+
}
|
|
19565
|
+
function snippetFor(shell) {
|
|
19566
|
+
switch (shell) {
|
|
19567
|
+
case "bash":
|
|
19568
|
+
return BASH_SNIPPET;
|
|
19569
|
+
case "zsh":
|
|
19570
|
+
return ZSH_SNIPPET;
|
|
19571
|
+
case "fish":
|
|
19572
|
+
return FISH_SNIPPET;
|
|
19573
|
+
}
|
|
19574
|
+
}
|
|
19575
|
+
var BASH_SNIPPET = `# codevector shell hook (bash) \u2014 auto-activates credentials on cd
|
|
19576
|
+
_codevector_on_prompt() {
|
|
19577
|
+
if [ "$PWD" = "\${_CODEVECTOR_LAST_PWD:-}" ]; then return; fi
|
|
19578
|
+
_CODEVECTOR_LAST_PWD="$PWD"
|
|
19579
|
+
local _codevector_out
|
|
19580
|
+
_codevector_out="$(command codevector env --shell bash 2>/dev/null)"
|
|
19581
|
+
if [ -n "$_codevector_out" ]; then eval "$_codevector_out"; fi
|
|
19582
|
+
}
|
|
19583
|
+
case "\${PROMPT_COMMAND:-}" in
|
|
19584
|
+
*_codevector_on_prompt*) ;;
|
|
19585
|
+
"") PROMPT_COMMAND="_codevector_on_prompt" ;;
|
|
19586
|
+
*) PROMPT_COMMAND="_codevector_on_prompt;$PROMPT_COMMAND" ;;
|
|
19587
|
+
esac
|
|
19588
|
+
`;
|
|
19589
|
+
var ZSH_SNIPPET = `# codevector shell hook (zsh) \u2014 auto-activates credentials on cd
|
|
19590
|
+
_codevector_on_chpwd() {
|
|
19591
|
+
local _codevector_out
|
|
19592
|
+
_codevector_out="$(command codevector env --shell zsh 2>/dev/null)"
|
|
19593
|
+
if [ -n "$_codevector_out" ]; then eval "$_codevector_out"; fi
|
|
19594
|
+
}
|
|
19595
|
+
autoload -Uz add-zsh-hook
|
|
19596
|
+
add-zsh-hook chpwd _codevector_on_chpwd
|
|
19597
|
+
# Run once for the current directory at shell startup.
|
|
19598
|
+
_codevector_on_chpwd
|
|
19599
|
+
`;
|
|
19600
|
+
var FISH_SNIPPET = `# codevector shell hook (fish) \u2014 auto-activates credentials on cd
|
|
19601
|
+
function _codevector_on_pwd --on-variable PWD
|
|
19602
|
+
set -l out (command codevector env --shell fish 2>/dev/null)
|
|
19603
|
+
if test -n "$out"
|
|
19604
|
+
eval $out
|
|
19605
|
+
end
|
|
19606
|
+
end
|
|
19607
|
+
# Run once for the current directory at shell startup.
|
|
19608
|
+
_codevector_on_pwd
|
|
19609
|
+
`;
|
|
19610
|
+
|
|
18985
19611
|
// src/commands/init.ts
|
|
18986
|
-
import { existsSync as existsSync8, writeFileSync as writeFileSync6 } from "fs";
|
|
18987
19612
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
18988
|
-
import {
|
|
19613
|
+
import { existsSync as existsSync9 } from "fs";
|
|
19614
|
+
import { join as join11 } from "path";
|
|
18989
19615
|
var DEFAULT_TICKET_PATTERN2 = "[A-Z]+-\\d+";
|
|
18990
19616
|
var initCommand = defineCommand({
|
|
18991
19617
|
meta: {
|
|
18992
19618
|
name: "init",
|
|
18993
|
-
description: "
|
|
19619
|
+
description: "Set this repo up for codevector: write .codevector.json (gateway + project) and optionally configure coding tools."
|
|
18994
19620
|
},
|
|
18995
19621
|
args: {
|
|
18996
19622
|
project: {
|
|
@@ -19001,33 +19627,147 @@ var initCommand = defineCommand({
|
|
|
19001
19627
|
type: "string",
|
|
19002
19628
|
description: "Override the ticket-extraction regex."
|
|
19003
19629
|
},
|
|
19630
|
+
gateway: {
|
|
19631
|
+
type: "string",
|
|
19632
|
+
description: "Gateway URL to pin for this repo (e.g. https://gateway.acme.com)."
|
|
19633
|
+
},
|
|
19004
19634
|
force: {
|
|
19005
19635
|
type: "boolean",
|
|
19006
|
-
description: "Overwrite an existing .codevector.json."
|
|
19636
|
+
description: "Overwrite an existing .codevector.json without prompting."
|
|
19637
|
+
},
|
|
19638
|
+
"skip-configure": {
|
|
19639
|
+
type: "boolean",
|
|
19640
|
+
description: "Don't run `codevector configure` after writing the file."
|
|
19007
19641
|
}
|
|
19008
19642
|
},
|
|
19009
|
-
run({ args }) {
|
|
19643
|
+
async run({ args }) {
|
|
19010
19644
|
const cwd = userCwd();
|
|
19011
|
-
const target =
|
|
19012
|
-
|
|
19013
|
-
|
|
19014
|
-
|
|
19015
|
-
const projectName = args.project ?? deriveProjectName(cwd);
|
|
19016
|
-
if (!projectName) {
|
|
19645
|
+
const target = join11(cwd, PROJECT_CONFIG_FILENAME);
|
|
19646
|
+
const existing = existsSync9(target) ? readProjectConfigAt(target) : null;
|
|
19647
|
+
const interactive = isInteractive(args);
|
|
19648
|
+
if (existing && !args.force && !interactive) {
|
|
19017
19649
|
throw new Error(
|
|
19018
|
-
|
|
19650
|
+
`${PROJECT_CONFIG_FILENAME} already exists in ${cwd}. Pass --force to overwrite, or run without flags for the interactive update flow.`
|
|
19019
19651
|
);
|
|
19020
19652
|
}
|
|
19021
|
-
|
|
19022
|
-
|
|
19023
|
-
|
|
19024
|
-
|
|
19025
|
-
|
|
19026
|
-
|
|
19027
|
-
|
|
19028
|
-
|
|
19653
|
+
if (interactive) ge("codevector init");
|
|
19654
|
+
if (existing && interactive && !args.force) {
|
|
19655
|
+
R2.info(`Found existing ${PROJECT_CONFIG_FILENAME} \u2014 updating in place.`);
|
|
19656
|
+
}
|
|
19657
|
+
const projectName = await resolveProjectName(args.project, existing, cwd, interactive);
|
|
19658
|
+
const gateway = await resolveGateway(args.gateway, existing, interactive);
|
|
19659
|
+
const ticketPattern = args["ticket-pattern"] ?? existing?.ticketPattern ?? DEFAULT_TICKET_PATTERN2;
|
|
19660
|
+
const next = {
|
|
19661
|
+
...projectName ? { projectName } : {},
|
|
19662
|
+
ticketPattern,
|
|
19663
|
+
...gateway ? { gateway } : {},
|
|
19664
|
+
...existing?.tools ? { tools: existing.tools } : {}
|
|
19665
|
+
};
|
|
19666
|
+
writeProjectConfig(target, next);
|
|
19667
|
+
if (interactive) {
|
|
19668
|
+
R2.success(`Wrote ${target}`);
|
|
19669
|
+
R2.info(`projectName: ${projectName ?? "(unset)"}`);
|
|
19670
|
+
R2.info(`gateway: ${gateway ?? "(unset)"}`);
|
|
19671
|
+
R2.info(`ticketPattern: ${ticketPattern}`);
|
|
19672
|
+
} else {
|
|
19673
|
+
R2.success(`Wrote ${target}`);
|
|
19674
|
+
}
|
|
19675
|
+
if (!args["skip-configure"] && interactive && gateway) {
|
|
19676
|
+
const run = unwrap(
|
|
19677
|
+
await ue({
|
|
19678
|
+
message: "Configure coding tools (Claude Code, Codex, OpenCode) for this repo now?",
|
|
19679
|
+
initialValue: true
|
|
19680
|
+
})
|
|
19681
|
+
);
|
|
19682
|
+
if (run) {
|
|
19683
|
+
await runCommand(configureCommand, { rawArgs: [] });
|
|
19684
|
+
} else {
|
|
19685
|
+
Se("Run `codevector configure` when you're ready.", "Skipped");
|
|
19686
|
+
}
|
|
19687
|
+
}
|
|
19688
|
+
if (interactive) {
|
|
19689
|
+
Se(
|
|
19690
|
+
'Add `eval "$(codevector hook bash)"` (or zsh / fish) to your shell rc so credentials auto-activate on cd.',
|
|
19691
|
+
"Shell hook"
|
|
19692
|
+
);
|
|
19693
|
+
ye("Done.");
|
|
19694
|
+
}
|
|
19029
19695
|
}
|
|
19030
19696
|
});
|
|
19697
|
+
function isInteractive(args) {
|
|
19698
|
+
if (!process.stdout.isTTY) return false;
|
|
19699
|
+
return !args.gateway;
|
|
19700
|
+
}
|
|
19701
|
+
async function resolveProjectName(fromArgs, existing, cwd, interactive) {
|
|
19702
|
+
if (fromArgs) return fromArgs;
|
|
19703
|
+
const derived = deriveProjectName(cwd) ?? existing?.projectName ?? null;
|
|
19704
|
+
if (!interactive) return derived;
|
|
19705
|
+
const initial = derived ?? "";
|
|
19706
|
+
const entered = unwrap(
|
|
19707
|
+
await Pe({
|
|
19708
|
+
message: "Project name (used for x-project attribution header):",
|
|
19709
|
+
placeholder: initial || "my-project",
|
|
19710
|
+
defaultValue: initial
|
|
19711
|
+
})
|
|
19712
|
+
);
|
|
19713
|
+
const trimmed = entered.trim();
|
|
19714
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
19715
|
+
}
|
|
19716
|
+
async function resolveGateway(fromArgs, existing, interactive) {
|
|
19717
|
+
if (fromArgs) return fromArgs;
|
|
19718
|
+
if (!interactive) return existing?.gateway ?? null;
|
|
19719
|
+
const profiles = await readProfiles();
|
|
19720
|
+
const knownUrls = uniqueGatewayUrls(profiles?.profiles ?? {});
|
|
19721
|
+
const initialUrl = existing?.gateway ?? profiles?.profiles[profiles.activeProfile]?.gatewayUrl;
|
|
19722
|
+
const CUSTOM = "__custom__";
|
|
19723
|
+
const options = [
|
|
19724
|
+
...knownUrls.map((url2) => ({ value: url2, label: url2 })),
|
|
19725
|
+
{ value: CUSTOM, label: "Enter a different URL\u2026" }
|
|
19726
|
+
];
|
|
19727
|
+
if (knownUrls.length === 0) {
|
|
19728
|
+
const entered2 = unwrap(
|
|
19729
|
+
await Pe({
|
|
19730
|
+
message: "Gateway URL (e.g. https://gateway.acme.com):",
|
|
19731
|
+
placeholder: "https://gateway.acme.com",
|
|
19732
|
+
validate: validateUrl
|
|
19733
|
+
})
|
|
19734
|
+
);
|
|
19735
|
+
return entered2.trim();
|
|
19736
|
+
}
|
|
19737
|
+
const picked = unwrap(
|
|
19738
|
+
await xe({
|
|
19739
|
+
message: "Pin a gateway for this repo (auto-activates on cd):",
|
|
19740
|
+
options,
|
|
19741
|
+
...initialUrl && knownUrls.includes(initialUrl) ? { initialValue: initialUrl } : {}
|
|
19742
|
+
})
|
|
19743
|
+
);
|
|
19744
|
+
if (picked !== CUSTOM) return picked;
|
|
19745
|
+
const entered = unwrap(
|
|
19746
|
+
await Pe({
|
|
19747
|
+
message: "Gateway URL:",
|
|
19748
|
+
placeholder: "https://gateway.acme.com",
|
|
19749
|
+
validate: validateUrl
|
|
19750
|
+
})
|
|
19751
|
+
);
|
|
19752
|
+
return entered.trim();
|
|
19753
|
+
}
|
|
19754
|
+
function uniqueGatewayUrls(profiles) {
|
|
19755
|
+
const seen = /* @__PURE__ */ new Set();
|
|
19756
|
+
for (const p2 of Object.values(profiles)) {
|
|
19757
|
+
seen.add(p2.gatewayUrl);
|
|
19758
|
+
}
|
|
19759
|
+
return [...seen].sort();
|
|
19760
|
+
}
|
|
19761
|
+
function validateUrl(value) {
|
|
19762
|
+
const trimmed = (value ?? "").trim();
|
|
19763
|
+
if (!trimmed) return "Required.";
|
|
19764
|
+
try {
|
|
19765
|
+
new URL(trimmed);
|
|
19766
|
+
} catch {
|
|
19767
|
+
return "Must be a valid URL (e.g. https://gateway.acme.com).";
|
|
19768
|
+
}
|
|
19769
|
+
return void 0;
|
|
19770
|
+
}
|
|
19031
19771
|
function deriveProjectName(cwd) {
|
|
19032
19772
|
try {
|
|
19033
19773
|
const out = execFileSync2("git", ["remote", "get-url", "origin"], {
|
|
@@ -19235,8 +19975,8 @@ var modelsCommand = defineCommand({
|
|
|
19235
19975
|
});
|
|
19236
19976
|
|
|
19237
19977
|
// src/commands/profile.ts
|
|
19238
|
-
import { existsSync as
|
|
19239
|
-
var
|
|
19978
|
+
import { existsSync as existsSync10, readFileSync as readFileSync10 } from "fs";
|
|
19979
|
+
var WRITERS3 = {
|
|
19240
19980
|
"claude-code": writeClaudeCodeConfig,
|
|
19241
19981
|
opencode: writeOpencodeConfig,
|
|
19242
19982
|
codex: writeCodexConfig
|
|
@@ -19333,7 +20073,7 @@ Key: ${maskApiKey(active.apiKey)}`,
|
|
|
19333
20073
|
const persisted = [];
|
|
19334
20074
|
for (const tc of toolConfigs) {
|
|
19335
20075
|
const tool = tc.tool;
|
|
19336
|
-
const writer =
|
|
20076
|
+
const writer = WRITERS3[tool];
|
|
19337
20077
|
if (!writer) {
|
|
19338
20078
|
results.push({
|
|
19339
20079
|
tool: tc.tool,
|
|
@@ -19424,82 +20164,254 @@ Or add it to your shell rc to persist across sessions.`,
|
|
|
19424
20164
|
}
|
|
19425
20165
|
}
|
|
19426
20166
|
});
|
|
19427
|
-
var profileCommand = defineCommand({
|
|
20167
|
+
var profileCommand = defineCommand({
|
|
20168
|
+
meta: {
|
|
20169
|
+
name: "profile",
|
|
20170
|
+
description: "Manage CLI profiles."
|
|
20171
|
+
},
|
|
20172
|
+
subCommands: {
|
|
20173
|
+
list: profileListCommand,
|
|
20174
|
+
switch: profileSwitchCommand
|
|
20175
|
+
}
|
|
20176
|
+
});
|
|
20177
|
+
async function fetchReachableModels(gatewayUrl, apiKey) {
|
|
20178
|
+
const client = gatewayClient(gatewayUrl, apiKey, 1e4);
|
|
20179
|
+
try {
|
|
20180
|
+
const res = await call(parseResponse(client.models.$get()));
|
|
20181
|
+
const models = res.data.filter((m2) => m2.kind === "chat").map((m2) => ({
|
|
20182
|
+
slug: m2.slug,
|
|
20183
|
+
providerKind: m2.providerKind,
|
|
20184
|
+
displayName: m2.displayName,
|
|
20185
|
+
contextWindow: m2.contextWindow,
|
|
20186
|
+
maxOutputTokens: m2.maxOutputTokens,
|
|
20187
|
+
maxInputTokens: m2.maxInputTokens,
|
|
20188
|
+
inputPricePerMtok: m2.inputPricePerMtok,
|
|
20189
|
+
cachedInputPricePerMtok: m2.cachedInputPricePerMtok,
|
|
20190
|
+
cacheWritePricePerMtok: m2.cacheWritePricePerMtok,
|
|
20191
|
+
reasoningPricePerMtok: m2.reasoningPricePerMtok,
|
|
20192
|
+
outputPricePerMtok: m2.outputPricePerMtok,
|
|
20193
|
+
supportsReasoning: m2.supportsReasoning,
|
|
20194
|
+
supportsAttachment: m2.supportsAttachment,
|
|
20195
|
+
supportsToolCall: m2.supportsToolCall,
|
|
20196
|
+
supportsTemperature: m2.supportsTemperature,
|
|
20197
|
+
inputModalities: m2.inputModalities,
|
|
20198
|
+
outputModalities: m2.outputModalities,
|
|
20199
|
+
releaseDate: m2.releaseDate,
|
|
20200
|
+
family: m2.family
|
|
20201
|
+
}));
|
|
20202
|
+
return { ok: true, models };
|
|
20203
|
+
} catch (err) {
|
|
20204
|
+
return { ok: false, reason: err instanceof ApiClientError ? err.message : String(err) };
|
|
20205
|
+
}
|
|
20206
|
+
}
|
|
20207
|
+
function isResponsesCompatibleProviderKind(providerKind) {
|
|
20208
|
+
return providerKind === "openai" || providerKind === "openai_compatible";
|
|
20209
|
+
}
|
|
20210
|
+
function pickCodexFallbackModel(reachable) {
|
|
20211
|
+
const candidates = reachable.filter((m2) => isResponsesCompatibleProviderKind(m2.providerKind));
|
|
20212
|
+
if (candidates.length === 0) {
|
|
20213
|
+
return void 0;
|
|
20214
|
+
}
|
|
20215
|
+
const ranked = readCodexNuxRankedModels();
|
|
20216
|
+
for (const slug of ranked) {
|
|
20217
|
+
const match = candidates.find((m2) => m2.slug === slug);
|
|
20218
|
+
if (match) return match;
|
|
20219
|
+
}
|
|
20220
|
+
return candidates[0];
|
|
20221
|
+
}
|
|
20222
|
+
function readCodexNuxRankedModels() {
|
|
20223
|
+
const path = codexConfigPath();
|
|
20224
|
+
if (!existsSync10(path)) return [];
|
|
20225
|
+
let parsed;
|
|
20226
|
+
try {
|
|
20227
|
+
parsed = parse3(readFileSync10(path, "utf8"));
|
|
20228
|
+
} catch {
|
|
20229
|
+
return [];
|
|
20230
|
+
}
|
|
20231
|
+
if (!isObject5(parsed)) return [];
|
|
20232
|
+
const tui = asObject(parsed.tui);
|
|
20233
|
+
const availability = asObject(tui?.model_availability_nux);
|
|
20234
|
+
if (!availability) return [];
|
|
20235
|
+
return Object.entries(availability).filter((entry) => typeof entry[1] === "number" && Number.isFinite(entry[1])).sort((a, b2) => b2[1] - a[1] || a[0].localeCompare(b2[0])).map(([slug]) => slug);
|
|
20236
|
+
}
|
|
20237
|
+
function isObject5(value) {
|
|
20238
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
20239
|
+
}
|
|
20240
|
+
function asObject(value) {
|
|
20241
|
+
return isObject5(value) ? value : void 0;
|
|
20242
|
+
}
|
|
20243
|
+
|
|
20244
|
+
// src/commands/skills.ts
|
|
20245
|
+
import { readFile, writeFile } from "fs/promises";
|
|
20246
|
+
import { resolve as resolve2 } from "path";
|
|
20247
|
+
import { spawn } from "child_process";
|
|
20248
|
+
async function getClient() {
|
|
20249
|
+
const creds = await readCredentials();
|
|
20250
|
+
if (!creds) {
|
|
20251
|
+
R2.warn("Not signed in. Run `codevector auth login` to get started.");
|
|
20252
|
+
process.exitCode = 1;
|
|
20253
|
+
return null;
|
|
20254
|
+
}
|
|
20255
|
+
return gatewayClient(creds.gatewayUrl, creds.apiKey, 1e4);
|
|
20256
|
+
}
|
|
20257
|
+
var skillsListCommand = defineCommand({
|
|
20258
|
+
meta: {
|
|
20259
|
+
name: "list",
|
|
20260
|
+
description: "List skill packs available on the gateway."
|
|
20261
|
+
},
|
|
20262
|
+
args: {
|
|
20263
|
+
json: {
|
|
20264
|
+
type: "boolean",
|
|
20265
|
+
description: "Emit raw JSON (for scripting)."
|
|
20266
|
+
}
|
|
20267
|
+
},
|
|
20268
|
+
async run({ args }) {
|
|
20269
|
+
const client = await getClient();
|
|
20270
|
+
if (!client) return;
|
|
20271
|
+
const s = args.json ? null : ft();
|
|
20272
|
+
s?.start("Loading skill packs\u2026");
|
|
20273
|
+
try {
|
|
20274
|
+
const res = await call(parseResponse(client["skill-packs"].$get()));
|
|
20275
|
+
s?.stop(`${res.data.length} skill pack${res.data.length === 1 ? "" : "s"}`);
|
|
20276
|
+
if (args.json) {
|
|
20277
|
+
process.stdout.write(`${JSON.stringify(res.data, null, 2)}
|
|
20278
|
+
`);
|
|
20279
|
+
return;
|
|
20280
|
+
}
|
|
20281
|
+
if (res.data.length === 0) {
|
|
20282
|
+
R2.info("No skill packs found. Ask an admin to upload one with `codevector skills upload <name>`.");
|
|
20283
|
+
return;
|
|
20284
|
+
}
|
|
20285
|
+
const headers = ["NAME", "UPDATED"];
|
|
20286
|
+
const cells = res.data.map((p2) => [
|
|
20287
|
+
p2.name,
|
|
20288
|
+
new Date(p2.updatedAt).toLocaleString()
|
|
20289
|
+
]);
|
|
20290
|
+
Se(renderTable(headers, cells), "Skill packs");
|
|
20291
|
+
} catch (err) {
|
|
20292
|
+
s?.stop("Could not load skill packs");
|
|
20293
|
+
R2.error(err instanceof ApiClientError ? err.message : String(err));
|
|
20294
|
+
process.exitCode = 1;
|
|
20295
|
+
}
|
|
20296
|
+
}
|
|
20297
|
+
});
|
|
20298
|
+
var skillsUploadCommand = defineCommand({
|
|
20299
|
+
meta: {
|
|
20300
|
+
name: "upload",
|
|
20301
|
+
description: "Upload the local skills-lock.json as a named skill pack."
|
|
20302
|
+
},
|
|
20303
|
+
args: {
|
|
20304
|
+
name: {
|
|
20305
|
+
type: "positional",
|
|
20306
|
+
description: "Name for the skill pack.",
|
|
20307
|
+
required: true
|
|
20308
|
+
}
|
|
20309
|
+
},
|
|
20310
|
+
async run({ args }) {
|
|
20311
|
+
const client = await getClient();
|
|
20312
|
+
if (!client) return;
|
|
20313
|
+
const lockfilePath = resolve2("skills-lock.json");
|
|
20314
|
+
let lockfile;
|
|
20315
|
+
try {
|
|
20316
|
+
const raw = await readFile(lockfilePath, "utf-8");
|
|
20317
|
+
lockfile = JSON.parse(raw);
|
|
20318
|
+
} catch {
|
|
20319
|
+
R2.error(`No lockfile found at ${lockfilePath}. Run \`npx skills install\` first.`);
|
|
20320
|
+
process.exitCode = 1;
|
|
20321
|
+
return;
|
|
20322
|
+
}
|
|
20323
|
+
const s = ft();
|
|
20324
|
+
s.start("Uploading skill pack\u2026");
|
|
20325
|
+
try {
|
|
20326
|
+
await call(
|
|
20327
|
+
parseResponse(
|
|
20328
|
+
client["skill-packs"].$post({
|
|
20329
|
+
json: { name: args.name, lockfile }
|
|
20330
|
+
})
|
|
20331
|
+
)
|
|
20332
|
+
);
|
|
20333
|
+
s.stop(`Skill pack "${args.name}" uploaded.`);
|
|
20334
|
+
} catch (err) {
|
|
20335
|
+
s.stop("Upload failed");
|
|
20336
|
+
R2.error(err instanceof ApiClientError ? err.message : String(err));
|
|
20337
|
+
process.exitCode = 1;
|
|
20338
|
+
}
|
|
20339
|
+
}
|
|
20340
|
+
});
|
|
20341
|
+
var skillsSyncCommand = defineCommand({
|
|
20342
|
+
meta: {
|
|
20343
|
+
name: "sync",
|
|
20344
|
+
description: "Fetch a skill pack's lockfile, write skills-lock.json, and run npx skills install."
|
|
20345
|
+
},
|
|
20346
|
+
args: {
|
|
20347
|
+
name: {
|
|
20348
|
+
type: "positional",
|
|
20349
|
+
description: "Name of the skill pack to sync.",
|
|
20350
|
+
required: true
|
|
20351
|
+
}
|
|
20352
|
+
},
|
|
20353
|
+
async run({ args }) {
|
|
20354
|
+
const client = await getClient();
|
|
20355
|
+
if (!client) return;
|
|
20356
|
+
const s = ft();
|
|
20357
|
+
s.start(`Fetching skill pack "${args.name}"\u2026`);
|
|
20358
|
+
let pack;
|
|
20359
|
+
try {
|
|
20360
|
+
pack = await call(
|
|
20361
|
+
parseResponse(
|
|
20362
|
+
client["skill-packs"][":name"].$get({ param: { name: args.name } })
|
|
20363
|
+
)
|
|
20364
|
+
);
|
|
20365
|
+
} catch (err) {
|
|
20366
|
+
s.stop("Fetch failed");
|
|
20367
|
+
R2.error(err instanceof ApiClientError ? err.message : String(err));
|
|
20368
|
+
process.exitCode = 1;
|
|
20369
|
+
return;
|
|
20370
|
+
}
|
|
20371
|
+
s.stop("Fetched");
|
|
20372
|
+
const lockfilePath = resolve2("skills-lock.json");
|
|
20373
|
+
s.start("Writing skills-lock.json\u2026");
|
|
20374
|
+
try {
|
|
20375
|
+
await writeFile(lockfilePath, `${JSON.stringify(pack.lockfile, null, 2)}
|
|
20376
|
+
`, "utf-8");
|
|
20377
|
+
} catch (err) {
|
|
20378
|
+
s.stop("Write failed");
|
|
20379
|
+
R2.error(err instanceof Error ? err.message : String(err));
|
|
20380
|
+
process.exitCode = 1;
|
|
20381
|
+
return;
|
|
20382
|
+
}
|
|
20383
|
+
s.stop("skills-lock.json written");
|
|
20384
|
+
s.start("Running npx skills install\u2026");
|
|
20385
|
+
try {
|
|
20386
|
+
await new Promise((resolve3, reject) => {
|
|
20387
|
+
const child = spawn("npx", ["skills", "experimental_install"], {
|
|
20388
|
+
stdio: "inherit"
|
|
20389
|
+
});
|
|
20390
|
+
child.on("close", (code) => {
|
|
20391
|
+
if (code === 0) resolve3();
|
|
20392
|
+
else reject(new Error(`npx skills experimental_install exited with code ${code}`));
|
|
20393
|
+
});
|
|
20394
|
+
child.on("error", reject);
|
|
20395
|
+
});
|
|
20396
|
+
s.stop("Skills installed");
|
|
20397
|
+
} catch (err) {
|
|
20398
|
+
s.stop("Install failed");
|
|
20399
|
+
R2.error(err instanceof Error ? err.message : String(err));
|
|
20400
|
+
process.exitCode = 1;
|
|
20401
|
+
}
|
|
20402
|
+
}
|
|
20403
|
+
});
|
|
20404
|
+
var skillsCommand = defineCommand({
|
|
19428
20405
|
meta: {
|
|
19429
|
-
name: "
|
|
19430
|
-
description: "
|
|
20406
|
+
name: "skills",
|
|
20407
|
+
description: "Upload and sync skill packs."
|
|
19431
20408
|
},
|
|
19432
20409
|
subCommands: {
|
|
19433
|
-
list:
|
|
19434
|
-
|
|
20410
|
+
list: skillsListCommand,
|
|
20411
|
+
upload: skillsUploadCommand,
|
|
20412
|
+
sync: skillsSyncCommand
|
|
19435
20413
|
}
|
|
19436
20414
|
});
|
|
19437
|
-
async function fetchReachableModels(gatewayUrl, apiKey) {
|
|
19438
|
-
const client = gatewayClient(gatewayUrl, apiKey, 1e4);
|
|
19439
|
-
try {
|
|
19440
|
-
const res = await call(parseResponse(client.models.$get()));
|
|
19441
|
-
const models = res.data.filter((m2) => m2.kind === "chat").map((m2) => ({
|
|
19442
|
-
slug: m2.slug,
|
|
19443
|
-
providerKind: m2.providerKind,
|
|
19444
|
-
displayName: m2.displayName,
|
|
19445
|
-
contextWindow: m2.contextWindow,
|
|
19446
|
-
maxOutputTokens: m2.maxOutputTokens,
|
|
19447
|
-
maxInputTokens: m2.maxInputTokens,
|
|
19448
|
-
inputPricePerMtok: m2.inputPricePerMtok,
|
|
19449
|
-
cachedInputPricePerMtok: m2.cachedInputPricePerMtok,
|
|
19450
|
-
cacheWritePricePerMtok: m2.cacheWritePricePerMtok,
|
|
19451
|
-
reasoningPricePerMtok: m2.reasoningPricePerMtok,
|
|
19452
|
-
outputPricePerMtok: m2.outputPricePerMtok,
|
|
19453
|
-
supportsReasoning: m2.supportsReasoning,
|
|
19454
|
-
supportsAttachment: m2.supportsAttachment,
|
|
19455
|
-
supportsToolCall: m2.supportsToolCall,
|
|
19456
|
-
supportsTemperature: m2.supportsTemperature,
|
|
19457
|
-
inputModalities: m2.inputModalities,
|
|
19458
|
-
outputModalities: m2.outputModalities,
|
|
19459
|
-
releaseDate: m2.releaseDate,
|
|
19460
|
-
family: m2.family
|
|
19461
|
-
}));
|
|
19462
|
-
return { ok: true, models };
|
|
19463
|
-
} catch (err) {
|
|
19464
|
-
return { ok: false, reason: err instanceof ApiClientError ? err.message : String(err) };
|
|
19465
|
-
}
|
|
19466
|
-
}
|
|
19467
|
-
function isResponsesCompatibleProviderKind(providerKind) {
|
|
19468
|
-
return providerKind === "openai" || providerKind === "openai_compatible";
|
|
19469
|
-
}
|
|
19470
|
-
function pickCodexFallbackModel(reachable) {
|
|
19471
|
-
const candidates = reachable.filter((m2) => isResponsesCompatibleProviderKind(m2.providerKind));
|
|
19472
|
-
if (candidates.length === 0) {
|
|
19473
|
-
return void 0;
|
|
19474
|
-
}
|
|
19475
|
-
const ranked = readCodexNuxRankedModels();
|
|
19476
|
-
for (const slug of ranked) {
|
|
19477
|
-
const match = candidates.find((m2) => m2.slug === slug);
|
|
19478
|
-
if (match) return match;
|
|
19479
|
-
}
|
|
19480
|
-
return candidates[0];
|
|
19481
|
-
}
|
|
19482
|
-
function readCodexNuxRankedModels() {
|
|
19483
|
-
const path = codexConfigPath();
|
|
19484
|
-
if (!existsSync9(path)) return [];
|
|
19485
|
-
let parsed;
|
|
19486
|
-
try {
|
|
19487
|
-
parsed = parse3(readFileSync9(path, "utf8"));
|
|
19488
|
-
} catch {
|
|
19489
|
-
return [];
|
|
19490
|
-
}
|
|
19491
|
-
if (!isObject5(parsed)) return [];
|
|
19492
|
-
const tui = asObject(parsed.tui);
|
|
19493
|
-
const availability = asObject(tui?.model_availability_nux);
|
|
19494
|
-
if (!availability) return [];
|
|
19495
|
-
return Object.entries(availability).filter((entry) => typeof entry[1] === "number" && Number.isFinite(entry[1])).sort((a, b2) => b2[1] - a[1] || a[0].localeCompare(b2[0])).map(([slug]) => slug);
|
|
19496
|
-
}
|
|
19497
|
-
function isObject5(value) {
|
|
19498
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
19499
|
-
}
|
|
19500
|
-
function asObject(value) {
|
|
19501
|
-
return isObject5(value) ? value : void 0;
|
|
19502
|
-
}
|
|
19503
20415
|
|
|
19504
20416
|
// src/commands/status.ts
|
|
19505
20417
|
var statusCommand = defineCommand({
|
|
@@ -19538,47 +20450,47 @@ Last saved: ${creds.savedAt}`,
|
|
|
19538
20450
|
});
|
|
19539
20451
|
|
|
19540
20452
|
// src/commands/system.ts
|
|
19541
|
-
import { existsSync as
|
|
20453
|
+
import { existsSync as existsSync12 } from "fs";
|
|
19542
20454
|
|
|
19543
20455
|
// src/lib/backup.ts
|
|
19544
20456
|
import {
|
|
19545
20457
|
copyFileSync as copyFileSync2,
|
|
19546
|
-
existsSync as
|
|
19547
|
-
mkdirSync as
|
|
20458
|
+
existsSync as existsSync11,
|
|
20459
|
+
mkdirSync as mkdirSync7,
|
|
19548
20460
|
readdirSync,
|
|
19549
20461
|
statSync as statSync6
|
|
19550
20462
|
} from "fs";
|
|
19551
|
-
import { basename, dirname as
|
|
20463
|
+
import { basename, dirname as dirname7, join as join12 } from "path";
|
|
19552
20464
|
import { homedir as homedir6 } from "os";
|
|
19553
|
-
var BACKUP_ROOT = process.env.CODEVECTOR_BACKUP_ROOT ??
|
|
20465
|
+
var BACKUP_ROOT = process.env.CODEVECTOR_BACKUP_ROOT ?? join12(homedir6(), ".codevector", "backups");
|
|
19554
20466
|
function backupTimestamp(d = /* @__PURE__ */ new Date()) {
|
|
19555
20467
|
return d.toISOString().replace(/:/g, "-");
|
|
19556
20468
|
}
|
|
19557
20469
|
function backupFile(sourcePath, tool, timestamp) {
|
|
19558
|
-
if (!
|
|
19559
|
-
const destDir =
|
|
19560
|
-
|
|
19561
|
-
const dest =
|
|
20470
|
+
if (!existsSync11(sourcePath)) return void 0;
|
|
20471
|
+
const destDir = join12(BACKUP_ROOT, timestamp, tool);
|
|
20472
|
+
mkdirSync7(destDir, { recursive: true, mode: 448 });
|
|
20473
|
+
const dest = join12(destDir, basename(sourcePath));
|
|
19562
20474
|
copyFileSync2(sourcePath, dest);
|
|
19563
20475
|
return dest;
|
|
19564
20476
|
}
|
|
19565
20477
|
function listBackupRuns() {
|
|
19566
|
-
if (!
|
|
20478
|
+
if (!existsSync11(BACKUP_ROOT)) return [];
|
|
19567
20479
|
const entries = readdirSync(BACKUP_ROOT, { withFileTypes: true }).filter((e2) => e2.isDirectory()).map((e2) => e2.name).sort().reverse();
|
|
19568
20480
|
const runs = [];
|
|
19569
20481
|
for (const ts of entries) {
|
|
19570
|
-
const dir =
|
|
20482
|
+
const dir = join12(BACKUP_ROOT, ts);
|
|
19571
20483
|
const tools = readdirSync(dir, { withFileTypes: true }).filter((e2) => e2.isDirectory());
|
|
19572
20484
|
const collected = [];
|
|
19573
20485
|
for (const toolDir of tools) {
|
|
19574
|
-
const toolPath =
|
|
20486
|
+
const toolPath = join12(dir, toolDir.name);
|
|
19575
20487
|
for (const file2 of readdirSync(toolPath, { withFileTypes: true })) {
|
|
19576
20488
|
if (!file2.isFile()) continue;
|
|
19577
20489
|
collected.push({
|
|
19578
20490
|
tool: toolDir.name,
|
|
19579
20491
|
original: "",
|
|
19580
20492
|
// resolved by caller per tool — see restoreBackup()
|
|
19581
|
-
backup:
|
|
20493
|
+
backup: join12(toolPath, file2.name)
|
|
19582
20494
|
});
|
|
19583
20495
|
}
|
|
19584
20496
|
}
|
|
@@ -19588,10 +20500,10 @@ function listBackupRuns() {
|
|
|
19588
20500
|
return runs;
|
|
19589
20501
|
}
|
|
19590
20502
|
function restoreBackup(backupPath, originalPath) {
|
|
19591
|
-
if (!
|
|
20503
|
+
if (!existsSync11(backupPath)) {
|
|
19592
20504
|
throw new Error(`Backup file missing: ${backupPath}`);
|
|
19593
20505
|
}
|
|
19594
|
-
|
|
20506
|
+
mkdirSync7(dirname7(originalPath), { recursive: true });
|
|
19595
20507
|
copyFileSync2(backupPath, originalPath);
|
|
19596
20508
|
}
|
|
19597
20509
|
function backupRunMtime(dir) {
|
|
@@ -19600,7 +20512,7 @@ function backupRunMtime(dir) {
|
|
|
19600
20512
|
|
|
19601
20513
|
// src/commands/system.ts
|
|
19602
20514
|
var TOOLS = ["claude-code", "opencode", "codex"];
|
|
19603
|
-
var
|
|
20515
|
+
var WRITERS4 = {
|
|
19604
20516
|
"claude-code": writeClaudeCodeConfig,
|
|
19605
20517
|
opencode: writeOpencodeConfig,
|
|
19606
20518
|
codex: writeCodexConfig
|
|
@@ -19647,11 +20559,11 @@ var systemConfigureCommand = defineCommand({
|
|
|
19647
20559
|
} else {
|
|
19648
20560
|
R2.info("No pre-existing user-scope config files to back up.");
|
|
19649
20561
|
}
|
|
19650
|
-
const reachable = await
|
|
20562
|
+
const reachable = await fetchReachableChatModels3(creds);
|
|
19651
20563
|
const hookPath = installAcceptanceHook();
|
|
19652
20564
|
const results = [];
|
|
19653
20565
|
for (const tool of TOOLS) {
|
|
19654
|
-
const writer =
|
|
20566
|
+
const writer = WRITERS4[tool];
|
|
19655
20567
|
try {
|
|
19656
20568
|
results.push(
|
|
19657
20569
|
writer({
|
|
@@ -19750,7 +20662,7 @@ var systemRestoreCommand = defineCommand({
|
|
|
19750
20662
|
}
|
|
19751
20663
|
const target = userPathFor(e2.tool);
|
|
19752
20664
|
plan.push({ tool: e2.tool, backup: e2.backup, target });
|
|
19753
|
-
R2.info(` ${e2.tool} \u2192 ${target}${
|
|
20665
|
+
R2.info(` ${e2.tool} \u2192 ${target}${existsSync12(target) ? " (will overwrite)" : " (new)"}`);
|
|
19754
20666
|
}
|
|
19755
20667
|
if (plan.length === 0) {
|
|
19756
20668
|
ye("Nothing to restore.");
|
|
@@ -19786,7 +20698,7 @@ async function confirmRestore() {
|
|
|
19786
20698
|
function isTool2(value) {
|
|
19787
20699
|
return TOOLS.includes(value);
|
|
19788
20700
|
}
|
|
19789
|
-
async function
|
|
20701
|
+
async function fetchReachableChatModels3(creds) {
|
|
19790
20702
|
const client = gatewayClient(creds.gatewayUrl, creds.apiKey, 1e4);
|
|
19791
20703
|
const s = ft();
|
|
19792
20704
|
s.start("Loading reachable models\u2026");
|
|
@@ -19839,7 +20751,7 @@ import { spawnSync } from "child_process";
|
|
|
19839
20751
|
// package.json
|
|
19840
20752
|
var package_default = {
|
|
19841
20753
|
name: "@codevector/cli",
|
|
19842
|
-
version: "0.
|
|
20754
|
+
version: "0.5.1",
|
|
19843
20755
|
description: "CodeVector CLI \u2014 installs and configures first-party coding-tool integrations.",
|
|
19844
20756
|
license: "UNLICENSED",
|
|
19845
20757
|
bin: {
|
|
@@ -19884,43 +20796,98 @@ var package_default = {
|
|
|
19884
20796
|
}
|
|
19885
20797
|
};
|
|
19886
20798
|
|
|
19887
|
-
// src/lib/
|
|
19888
|
-
import { existsSync as
|
|
19889
|
-
import { join as
|
|
19890
|
-
var
|
|
19891
|
-
var
|
|
19892
|
-
|
|
19893
|
-
|
|
19894
|
-
|
|
19895
|
-
function
|
|
19896
|
-
if (!
|
|
20799
|
+
// src/lib/update-notifier.ts
|
|
20800
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as readFileSync11, writeFileSync as writeFileSync8 } from "fs";
|
|
20801
|
+
import { join as join13 } from "path";
|
|
20802
|
+
var PKG_NAME = "@codevector/cli";
|
|
20803
|
+
var REGISTRY_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
|
|
20804
|
+
var CHECK_CACHE_FILE = join13(CODEVECTOR_CONFIG_DIR, "update-check.json");
|
|
20805
|
+
var CHECK_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
20806
|
+
var FETCH_TIMEOUT_MS = 2e3;
|
|
20807
|
+
function readCache() {
|
|
20808
|
+
if (!existsSync13(CHECK_CACHE_FILE)) return void 0;
|
|
19897
20809
|
try {
|
|
19898
|
-
const raw =
|
|
20810
|
+
const raw = readFileSync11(CHECK_CACHE_FILE, "utf8");
|
|
19899
20811
|
const parsed = JSON.parse(raw);
|
|
19900
|
-
if (
|
|
19901
|
-
const source = parsed.source === "user" ? "user" : "auto";
|
|
20812
|
+
if (typeof parsed.checkedAt !== "number") return void 0;
|
|
19902
20813
|
return {
|
|
19903
|
-
|
|
19904
|
-
|
|
19905
|
-
detectedAt: typeof parsed.detectedAt === "string" ? parsed.detectedAt : (/* @__PURE__ */ new Date()).toISOString()
|
|
20814
|
+
latest: typeof parsed.latest === "string" ? parsed.latest : null,
|
|
20815
|
+
checkedAt: parsed.checkedAt
|
|
19906
20816
|
};
|
|
19907
20817
|
} catch {
|
|
19908
20818
|
return void 0;
|
|
19909
20819
|
}
|
|
19910
20820
|
}
|
|
19911
|
-
function
|
|
19912
|
-
|
|
19913
|
-
|
|
19914
|
-
|
|
19915
|
-
|
|
20821
|
+
function readCachedLatestVersion() {
|
|
20822
|
+
return readCache()?.latest ?? null;
|
|
20823
|
+
}
|
|
20824
|
+
function writeCache(cache) {
|
|
20825
|
+
try {
|
|
20826
|
+
mkdirSync8(CODEVECTOR_CONFIG_DIR, { recursive: true, mode: 448 });
|
|
20827
|
+
writeFileSync8(CHECK_CACHE_FILE, JSON.stringify(cache));
|
|
20828
|
+
} catch {
|
|
20829
|
+
}
|
|
20830
|
+
}
|
|
20831
|
+
function isNewer(current, latest) {
|
|
20832
|
+
const a = current.split(".").map((p2) => Number.parseInt(p2, 10));
|
|
20833
|
+
const b2 = latest.split(".").map((p2) => Number.parseInt(p2, 10));
|
|
20834
|
+
for (let i = 0; i < Math.max(a.length, b2.length); i++) {
|
|
20835
|
+
const ai = a[i] ?? 0;
|
|
20836
|
+
const bi = b2[i] ?? 0;
|
|
20837
|
+
if (Number.isNaN(ai) || Number.isNaN(bi)) return latest > current;
|
|
20838
|
+
if (bi > ai) return true;
|
|
20839
|
+
if (bi < ai) return false;
|
|
20840
|
+
}
|
|
20841
|
+
return false;
|
|
20842
|
+
}
|
|
20843
|
+
function printUpdateNoticeIfCached(currentVersion) {
|
|
20844
|
+
if (process.env.CODEVECTOR_NO_UPDATE_CHECK === "1") return;
|
|
20845
|
+
try {
|
|
20846
|
+
const cache = readCache();
|
|
20847
|
+
if (!cache || !cache.latest) return;
|
|
20848
|
+
if (isNewer(currentVersion, cache.latest)) {
|
|
20849
|
+
process.stderr.write(
|
|
20850
|
+
`\x1B[33m\u203A\x1B[0m A new version of @codevector/cli is available: ${currentVersion} \u2192 ${cache.latest}. Run \`codevector update\` to install.
|
|
20851
|
+
`
|
|
20852
|
+
);
|
|
20853
|
+
}
|
|
20854
|
+
} catch {
|
|
20855
|
+
}
|
|
20856
|
+
}
|
|
20857
|
+
function scheduleUpdateCheck() {
|
|
20858
|
+
if (process.env.CODEVECTOR_NO_UPDATE_CHECK === "1") return;
|
|
20859
|
+
const cache = readCache();
|
|
20860
|
+
const now = Date.now();
|
|
20861
|
+
if (cache && now - cache.checkedAt < CHECK_TTL_MS) return;
|
|
20862
|
+
void fetchLatestVersion().then((latest) => {
|
|
20863
|
+
writeCache({ latest, checkedAt: Date.now() });
|
|
20864
|
+
}).catch(() => {
|
|
20865
|
+
writeCache({ latest: cache?.latest ?? null, checkedAt: Date.now() });
|
|
20866
|
+
});
|
|
20867
|
+
}
|
|
20868
|
+
async function fetchLatestVersion() {
|
|
20869
|
+
const controller = new AbortController();
|
|
20870
|
+
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
20871
|
+
timer.unref?.();
|
|
20872
|
+
try {
|
|
20873
|
+
const res = await fetch(REGISTRY_URL, {
|
|
20874
|
+
signal: controller.signal,
|
|
20875
|
+
headers: { accept: "application/json" }
|
|
20876
|
+
});
|
|
20877
|
+
if (!res.ok) return null;
|
|
20878
|
+
const body = await res.json();
|
|
20879
|
+
return typeof body.version === "string" ? body.version : null;
|
|
20880
|
+
} finally {
|
|
20881
|
+
clearTimeout(timer);
|
|
20882
|
+
}
|
|
19916
20883
|
}
|
|
19917
20884
|
|
|
19918
20885
|
// src/commands/update.ts
|
|
19919
|
-
var
|
|
20886
|
+
var PKG_NAME2 = "@codevector/cli";
|
|
19920
20887
|
var updateCommand = defineCommand({
|
|
19921
20888
|
meta: {
|
|
19922
20889
|
name: "update",
|
|
19923
|
-
description: `Update ${
|
|
20890
|
+
description: `Update ${PKG_NAME2} to the latest version on npm. Uses the package manager that installed it (auto-detected on install) unless --with overrides.`
|
|
19924
20891
|
},
|
|
19925
20892
|
args: {
|
|
19926
20893
|
with: {
|
|
@@ -19946,10 +20913,35 @@ var updateCommand = defineCommand({
|
|
|
19946
20913
|
`${command.cmd} exited with status ${result.status}. The new version was not installed.`
|
|
19947
20914
|
);
|
|
19948
20915
|
}
|
|
19949
|
-
|
|
20916
|
+
verifyOnPathVersion(package_default.version, manager);
|
|
19950
20917
|
ye("Done.");
|
|
19951
20918
|
}
|
|
19952
20919
|
});
|
|
20920
|
+
function verifyOnPathVersion(previousVersion, manager) {
|
|
20921
|
+
const after = readOnPathVersion();
|
|
20922
|
+
if (!after) {
|
|
20923
|
+
R2.success("Update complete. Run `codevector --version` to confirm.");
|
|
20924
|
+
return;
|
|
20925
|
+
}
|
|
20926
|
+
if (after !== previousVersion) {
|
|
20927
|
+
R2.success(`Update complete: ${previousVersion} \u2192 ${after}.`);
|
|
20928
|
+
return;
|
|
20929
|
+
}
|
|
20930
|
+
const latest = readCachedLatestVersion();
|
|
20931
|
+
if (latest && isNewer(previousVersion, latest)) {
|
|
20932
|
+
R2.warn(
|
|
20933
|
+
`The \`codevector\` on your PATH is still ${previousVersion}, but ${latest} is available. ${manager} likely installed into a different global location than the binary on your PATH. Try \`codevector update --with <the manager that owns the binary on your PATH>\`, or reinstall manually.`
|
|
20934
|
+
);
|
|
20935
|
+
return;
|
|
20936
|
+
}
|
|
20937
|
+
R2.success(`Already on the latest version (${previousVersion}).`);
|
|
20938
|
+
}
|
|
20939
|
+
function readOnPathVersion() {
|
|
20940
|
+
const probe = spawnSync("codevector", ["version"], { encoding: "utf8" });
|
|
20941
|
+
if (probe.error || probe.status !== 0) return void 0;
|
|
20942
|
+
const out = probe.stdout?.trim();
|
|
20943
|
+
return out && /^\d+\.\d+\.\d+/.test(out) ? out : void 0;
|
|
20944
|
+
}
|
|
19953
20945
|
async function resolvePackageManager(raw) {
|
|
19954
20946
|
if (raw) {
|
|
19955
20947
|
const pm = parsePackageManager(raw);
|
|
@@ -19959,10 +20951,10 @@ async function resolvePackageManager(raw) {
|
|
|
19959
20951
|
writeInstallPref({ packageManager: pm, source: "user", detectedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
19960
20952
|
return pm;
|
|
19961
20953
|
}
|
|
19962
|
-
const
|
|
19963
|
-
if (
|
|
19964
|
-
R2.
|
|
19965
|
-
`Couldn't
|
|
20954
|
+
const resolved = resolveInstallManager();
|
|
20955
|
+
if (resolved.packageManager) return resolved.packageManager;
|
|
20956
|
+
R2.warn(
|
|
20957
|
+
`Couldn't determine which package manager installed ${PKG_NAME2} (no record at ${INSTALL_PREF_FILE}, and the install path didn't reveal it). Picking the wrong one installs into a location that isn't on your PATH.`
|
|
19966
20958
|
);
|
|
19967
20959
|
const picked = unwrap(
|
|
19968
20960
|
await xe({
|
|
@@ -19993,7 +20985,7 @@ function parsePackageManager(value) {
|
|
|
19993
20985
|
}
|
|
19994
20986
|
}
|
|
19995
20987
|
function installCommand(manager) {
|
|
19996
|
-
const target = `${
|
|
20988
|
+
const target = `${PKG_NAME2}@latest`;
|
|
19997
20989
|
switch (manager) {
|
|
19998
20990
|
case "npm":
|
|
19999
20991
|
return { cmd: "npm", args: ["install", "-g", target] };
|
|
@@ -20102,89 +21094,6 @@ var versionCommand = defineCommand({
|
|
|
20102
21094
|
}
|
|
20103
21095
|
});
|
|
20104
21096
|
|
|
20105
|
-
// src/lib/update-notifier.ts
|
|
20106
|
-
import { existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as readFileSync11, writeFileSync as writeFileSync8 } from "fs";
|
|
20107
|
-
import { join as join11 } from "path";
|
|
20108
|
-
var PKG_NAME2 = "@codevector/cli";
|
|
20109
|
-
var REGISTRY_URL = `https://registry.npmjs.org/${PKG_NAME2}/latest`;
|
|
20110
|
-
var CHECK_CACHE_FILE = join11(CODEVECTOR_CONFIG_DIR, "update-check.json");
|
|
20111
|
-
var CHECK_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
20112
|
-
var FETCH_TIMEOUT_MS = 2e3;
|
|
20113
|
-
function readCache() {
|
|
20114
|
-
if (!existsSync13(CHECK_CACHE_FILE)) return void 0;
|
|
20115
|
-
try {
|
|
20116
|
-
const raw = readFileSync11(CHECK_CACHE_FILE, "utf8");
|
|
20117
|
-
const parsed = JSON.parse(raw);
|
|
20118
|
-
if (typeof parsed.checkedAt !== "number") return void 0;
|
|
20119
|
-
return {
|
|
20120
|
-
latest: typeof parsed.latest === "string" ? parsed.latest : null,
|
|
20121
|
-
checkedAt: parsed.checkedAt
|
|
20122
|
-
};
|
|
20123
|
-
} catch {
|
|
20124
|
-
return void 0;
|
|
20125
|
-
}
|
|
20126
|
-
}
|
|
20127
|
-
function writeCache(cache) {
|
|
20128
|
-
try {
|
|
20129
|
-
mkdirSync8(CODEVECTOR_CONFIG_DIR, { recursive: true, mode: 448 });
|
|
20130
|
-
writeFileSync8(CHECK_CACHE_FILE, JSON.stringify(cache));
|
|
20131
|
-
} catch {
|
|
20132
|
-
}
|
|
20133
|
-
}
|
|
20134
|
-
function isNewer(current, latest) {
|
|
20135
|
-
const a = current.split(".").map((p2) => Number.parseInt(p2, 10));
|
|
20136
|
-
const b2 = latest.split(".").map((p2) => Number.parseInt(p2, 10));
|
|
20137
|
-
for (let i = 0; i < Math.max(a.length, b2.length); i++) {
|
|
20138
|
-
const ai = a[i] ?? 0;
|
|
20139
|
-
const bi = b2[i] ?? 0;
|
|
20140
|
-
if (Number.isNaN(ai) || Number.isNaN(bi)) return latest > current;
|
|
20141
|
-
if (bi > ai) return true;
|
|
20142
|
-
if (bi < ai) return false;
|
|
20143
|
-
}
|
|
20144
|
-
return false;
|
|
20145
|
-
}
|
|
20146
|
-
function printUpdateNoticeIfCached(currentVersion) {
|
|
20147
|
-
if (process.env.CODEVECTOR_NO_UPDATE_CHECK === "1") return;
|
|
20148
|
-
try {
|
|
20149
|
-
const cache = readCache();
|
|
20150
|
-
if (!cache || !cache.latest) return;
|
|
20151
|
-
if (isNewer(currentVersion, cache.latest)) {
|
|
20152
|
-
process.stderr.write(
|
|
20153
|
-
`\x1B[33m\u203A\x1B[0m A new version of @codevector/cli is available: ${currentVersion} \u2192 ${cache.latest}. Run \`codevector update\` to install.
|
|
20154
|
-
`
|
|
20155
|
-
);
|
|
20156
|
-
}
|
|
20157
|
-
} catch {
|
|
20158
|
-
}
|
|
20159
|
-
}
|
|
20160
|
-
function scheduleUpdateCheck() {
|
|
20161
|
-
if (process.env.CODEVECTOR_NO_UPDATE_CHECK === "1") return;
|
|
20162
|
-
const cache = readCache();
|
|
20163
|
-
const now = Date.now();
|
|
20164
|
-
if (cache && now - cache.checkedAt < CHECK_TTL_MS) return;
|
|
20165
|
-
void fetchLatestVersion().then((latest) => {
|
|
20166
|
-
writeCache({ latest, checkedAt: Date.now() });
|
|
20167
|
-
}).catch(() => {
|
|
20168
|
-
writeCache({ latest: cache?.latest ?? null, checkedAt: Date.now() });
|
|
20169
|
-
});
|
|
20170
|
-
}
|
|
20171
|
-
async function fetchLatestVersion() {
|
|
20172
|
-
const controller = new AbortController();
|
|
20173
|
-
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
20174
|
-
timer.unref?.();
|
|
20175
|
-
try {
|
|
20176
|
-
const res = await fetch(REGISTRY_URL, {
|
|
20177
|
-
signal: controller.signal,
|
|
20178
|
-
headers: { accept: "application/json" }
|
|
20179
|
-
});
|
|
20180
|
-
if (!res.ok) return null;
|
|
20181
|
-
const body = await res.json();
|
|
20182
|
-
return typeof body.version === "string" ? body.version : null;
|
|
20183
|
-
} finally {
|
|
20184
|
-
clearTimeout(timer);
|
|
20185
|
-
}
|
|
20186
|
-
}
|
|
20187
|
-
|
|
20188
21097
|
// src/index.ts
|
|
20189
21098
|
var main = defineCommand({
|
|
20190
21099
|
meta: {
|
|
@@ -20194,13 +21103,17 @@ var main = defineCommand({
|
|
|
20194
21103
|
},
|
|
20195
21104
|
subCommands: {
|
|
20196
21105
|
auth: authCommand,
|
|
21106
|
+
config: configCommand,
|
|
20197
21107
|
configure: configureCommand,
|
|
20198
21108
|
init: initCommand,
|
|
20199
21109
|
doctor: doctorCommand,
|
|
21110
|
+
env: envCommand,
|
|
21111
|
+
hook: hookCommand,
|
|
20200
21112
|
status: statusCommand,
|
|
20201
21113
|
system: systemCommand,
|
|
20202
21114
|
models: modelsCommand,
|
|
20203
21115
|
profile: profileCommand,
|
|
21116
|
+
skills: skillsCommand,
|
|
20204
21117
|
update: updateCommand,
|
|
20205
21118
|
usage: usageCommand,
|
|
20206
21119
|
version: versionCommand
|