@synkro-sh/cli 1.3.8 → 1.3.10
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/bootstrap.js +100 -47
- package/dist/bootstrap.js.map +1 -1
- package/package.json +1 -1
package/dist/bootstrap.js
CHANGED
|
@@ -2438,10 +2438,13 @@ var init_auth = __esm({
|
|
|
2438
2438
|
|
|
2439
2439
|
// cli/api/projects.ts
|
|
2440
2440
|
import { config } from "dotenv";
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2441
|
+
function resolveApiUrl() {
|
|
2442
|
+
for (const v of [process.env.SYNKRO_CRUD_URL, process.env.SYNKRO_API_URL]) {
|
|
2443
|
+
if (v && /^https?:\/\//.test(v)) return v;
|
|
2444
2444
|
}
|
|
2445
|
+
return "https://api.synkro.sh/api";
|
|
2446
|
+
}
|
|
2447
|
+
async function callApi(method, endpoint, body) {
|
|
2445
2448
|
const url = `${API_URL}${endpoint}`;
|
|
2446
2449
|
const accessToken = getAccessToken();
|
|
2447
2450
|
const headers = {
|
|
@@ -2478,7 +2481,7 @@ var init_projects = __esm({
|
|
|
2478
2481
|
"use strict";
|
|
2479
2482
|
init_auth();
|
|
2480
2483
|
config({ quiet: true });
|
|
2481
|
-
API_URL =
|
|
2484
|
+
API_URL = resolveApiUrl();
|
|
2482
2485
|
}
|
|
2483
2486
|
});
|
|
2484
2487
|
|
|
@@ -2752,8 +2755,30 @@ async function connectGithubAndSelectRepos() {
|
|
|
2752
2755
|
rl.close();
|
|
2753
2756
|
}
|
|
2754
2757
|
}
|
|
2755
|
-
async function promptRepoConnection() {
|
|
2758
|
+
async function promptRepoConnection(opts) {
|
|
2756
2759
|
const localRepo = detectGitRepo();
|
|
2760
|
+
if (opts?.linkRepo !== void 0) {
|
|
2761
|
+
if (opts.linkRepo && localRepo) {
|
|
2762
|
+
try {
|
|
2763
|
+
const existing = await listProjects();
|
|
2764
|
+
const alreadyLinked = existing.some(
|
|
2765
|
+
(p) => p.repos?.some((r) => r.full_name === localRepo.fullName)
|
|
2766
|
+
);
|
|
2767
|
+
if (!alreadyLinked) {
|
|
2768
|
+
await createProject(localRepo.shortName, [{ full_name: localRepo.fullName }]);
|
|
2769
|
+
console.log(` \u2713 Created project "${localRepo.shortName}" linked to ${localRepo.fullName}`);
|
|
2770
|
+
} else {
|
|
2771
|
+
console.log(` \u2713 ${localRepo.fullName} is already linked to a Synkro project.`);
|
|
2772
|
+
}
|
|
2773
|
+
} catch (err) {
|
|
2774
|
+
console.warn(` \u26A0 Could not link repo: ${err.message}`);
|
|
2775
|
+
}
|
|
2776
|
+
} else if (opts.linkRepo) {
|
|
2777
|
+
console.warn(" \u26A0 --link-repo specified but not in a git repo. Skipping.");
|
|
2778
|
+
}
|
|
2779
|
+
console.log();
|
|
2780
|
+
return;
|
|
2781
|
+
}
|
|
2757
2782
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
2758
2783
|
try {
|
|
2759
2784
|
console.log("Connect repos to Synkro:\n");
|
|
@@ -2918,11 +2943,14 @@ async function setupGithubCommand(opts = {}) {
|
|
|
2918
2943
|
process.exit(1);
|
|
2919
2944
|
}
|
|
2920
2945
|
const rl = createInterface2({ input, output });
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2946
|
+
let ghToken = opts.githubToken || "";
|
|
2947
|
+
if (!ghToken) {
|
|
2948
|
+
console.log("Synkro PR scan setup\n");
|
|
2949
|
+
console.log("Requirements:");
|
|
2950
|
+
console.log(" \u2022 A GitHub personal access token with `repo` scope");
|
|
2951
|
+
console.log(" (create at https://github.com/settings/tokens?type=beta)\n");
|
|
2952
|
+
ghToken = (await prompt(rl, "GitHub token (paste): ", { silent: true })).trim();
|
|
2953
|
+
}
|
|
2926
2954
|
if (!ghToken || !ghToken.startsWith("ghp_") && !ghToken.startsWith("github_pat_")) {
|
|
2927
2955
|
console.error("Invalid GitHub token format. Expected ghp_... or github_pat_...");
|
|
2928
2956
|
rl.close();
|
|
@@ -3061,6 +3089,17 @@ function parseArgs(argv) {
|
|
|
3061
3089
|
else if (a === "--skip-auth") opts.skipAuth = true;
|
|
3062
3090
|
else if (a === "--no-mcp") opts.noMcp = true;
|
|
3063
3091
|
else if (a === "--force" || a === "-f") opts.force = true;
|
|
3092
|
+
else if (a === "--non-interactive") opts.nonInteractive = true;
|
|
3093
|
+
else if (a === "--link-repo") opts.linkRepo = true;
|
|
3094
|
+
else if (a === "--sync-transcripts") opts.syncTranscripts = true;
|
|
3095
|
+
else if (a === "--no-sync-transcripts") opts.syncTranscripts = false;
|
|
3096
|
+
else if (a === "--pr-scan=claude-oauth") opts.prScan = "claude-oauth";
|
|
3097
|
+
else if (a === "--pr-scan=byok") opts.prScan = "byok";
|
|
3098
|
+
else if (a === "--pr-scan=skip") opts.prScan = "skip";
|
|
3099
|
+
else if (a.startsWith("--pr-scan=")) opts.prScan = a.slice("--pr-scan=".length);
|
|
3100
|
+
else if (a.startsWith("--inference-key=")) opts.inferenceKey = a.slice("--inference-key=".length);
|
|
3101
|
+
else if (a.startsWith("--inference-model=")) opts.inferenceModel = a.slice("--inference-model=".length);
|
|
3102
|
+
else if (a.startsWith("--github-token=")) opts.githubToken = a.slice("--github-token=".length);
|
|
3064
3103
|
}
|
|
3065
3104
|
if (!opts.gatewayUrl) {
|
|
3066
3105
|
const fromEnv = sanitizeGatewayCandidate(process.env.SYNKRO_GATEWAY_URL);
|
|
@@ -3135,7 +3174,7 @@ function writeConfigEnv(opts) {
|
|
|
3135
3174
|
`SYNKRO_GATEWAY_URL=${shellQuoteSingle(safeGateway)}`,
|
|
3136
3175
|
`SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
|
|
3137
3176
|
`SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
|
|
3138
|
-
`SYNKRO_VERSION=${shellQuoteSingle("1.3.
|
|
3177
|
+
`SYNKRO_VERSION=${shellQuoteSingle("1.3.10")}`
|
|
3139
3178
|
];
|
|
3140
3179
|
if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
|
|
3141
3180
|
if (safeOrgId) lines.push(`SYNKRO_ORG_ID=${shellQuoteSingle(safeOrgId)}`);
|
|
@@ -3238,7 +3277,11 @@ async function installCommand(opts = {}) {
|
|
|
3238
3277
|
console.error("No access token available after auth.");
|
|
3239
3278
|
process.exit(1);
|
|
3240
3279
|
}
|
|
3241
|
-
|
|
3280
|
+
if (opts.nonInteractive) {
|
|
3281
|
+
await promptRepoConnection({ linkRepo: opts.linkRepo ?? true });
|
|
3282
|
+
} else {
|
|
3283
|
+
await promptRepoConnection();
|
|
3284
|
+
}
|
|
3242
3285
|
const agents = detectAgents();
|
|
3243
3286
|
if (agents.length === 0) {
|
|
3244
3287
|
console.error("No AI coding agents detected. Install Claude Code first: https://docs.claude.com/claude-code");
|
|
@@ -3336,7 +3379,7 @@ async function installCommand(opts = {}) {
|
|
|
3336
3379
|
`);
|
|
3337
3380
|
const repo = detectGitRepo2();
|
|
3338
3381
|
if (repo && getClaudeProjectsFolder()) {
|
|
3339
|
-
const syncHistory = await promptYesNo("Sync Claude Code session history for this repo?");
|
|
3382
|
+
const syncHistory = opts.nonInteractive ? opts.syncTranscripts ?? true : await promptYesNo("Sync Claude Code session history for this repo?");
|
|
3340
3383
|
if (syncHistory) {
|
|
3341
3384
|
try {
|
|
3342
3385
|
const ingested = await ingestSessionTranscripts(gatewayUrl, token, repo);
|
|
@@ -3390,51 +3433,52 @@ async function installCommand(opts = {}) {
|
|
|
3390
3433
|
if (ghConnected) {
|
|
3391
3434
|
console.log(`GitHub is connected (${ghLogin || "linked via dashboard"}).`);
|
|
3392
3435
|
}
|
|
3393
|
-
const
|
|
3436
|
+
const prScanChoice = opts.nonInteractive ? opts.prScan ?? "skip" : null;
|
|
3437
|
+
const setupGh = prScanChoice !== null ? prScanChoice !== "skip" : await promptYesNo("Set up GitHub PR scanning?");
|
|
3394
3438
|
if (setupGh) {
|
|
3395
|
-
let
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
});
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3439
|
+
let choice;
|
|
3440
|
+
if (prScanChoice) {
|
|
3441
|
+
choice = prScanChoice === "claude-oauth" ? "1" : prScanChoice === "byok" ? "2" : "3";
|
|
3442
|
+
} else {
|
|
3443
|
+
const rl = createInterface3({ input: stdinStream, output: stdoutStream });
|
|
3444
|
+
console.log("\nHow should PR scans authenticate for AI review?\n");
|
|
3445
|
+
console.log(" 1. Claude Code OAuth \u2014 opens browser, auto-captures token");
|
|
3446
|
+
console.log(" 2. Use your own API key \u2014 paste an Anthropic, OpenAI, or Gemini key");
|
|
3447
|
+
console.log(" 3. Skip for now\n");
|
|
3448
|
+
choice = (await rl.question("Choice [1/2/3]: ")).trim();
|
|
3449
|
+
rl.close();
|
|
3405
3450
|
}
|
|
3406
|
-
const rl = createInterface3({ input: stdinStream, output: stdoutStream });
|
|
3407
|
-
console.log("\nHow should PR scans authenticate for AI review?\n");
|
|
3408
|
-
console.log(" 1. Claude Code OAuth \u2014 opens browser, auto-captures token");
|
|
3409
|
-
console.log(" 2. Use your own API key \u2014 paste an Anthropic, OpenAI, or Gemini key");
|
|
3410
|
-
console.log(" 3. Skip for now\n");
|
|
3411
|
-
const choice = (await rl.question("Choice [1/2/3]: ")).trim();
|
|
3412
|
-
rl.close();
|
|
3413
3451
|
if (choice === "1") {
|
|
3414
3452
|
try {
|
|
3415
|
-
await setupGithubCommand();
|
|
3453
|
+
await setupGithubCommand({ githubToken: opts.githubToken });
|
|
3416
3454
|
} catch (err) {
|
|
3417
3455
|
console.warn(` \u26A0 GitHub setup failed: ${err.message}`);
|
|
3418
3456
|
console.warn(" Run `synkro-cli setup-github` to retry.\n");
|
|
3419
3457
|
}
|
|
3420
3458
|
} else if (choice === "2") {
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3459
|
+
let apiKey = opts.inferenceKey || "";
|
|
3460
|
+
if (!apiKey) {
|
|
3461
|
+
const rl2 = createInterface3({ input: stdinStream, output: stdoutStream });
|
|
3462
|
+
apiKey = (await rl2.question("Paste your API key: ")).trim();
|
|
3463
|
+
rl2.close();
|
|
3464
|
+
}
|
|
3424
3465
|
if (apiKey) {
|
|
3425
3466
|
const provider = detectProviderFromKey(apiKey);
|
|
3426
3467
|
if (provider) {
|
|
3427
|
-
|
|
3428
|
-
|
|
3468
|
+
let selectedModel = opts.inferenceModel || "";
|
|
3469
|
+
if (!selectedModel) {
|
|
3470
|
+
const models = PROVIDER_MODELS[provider];
|
|
3471
|
+
console.log(`
|
|
3429
3472
|
Detected provider: ${provider}`);
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3473
|
+
console.log(" Select a model for classification + grading:\n");
|
|
3474
|
+
models.forEach((m, i) => console.log(` ${i + 1}. ${m}`));
|
|
3475
|
+
console.log();
|
|
3476
|
+
const rl3 = createInterface3({ input: stdinStream, output: stdoutStream });
|
|
3477
|
+
const modelChoice = (await rl3.question(` Model [1-${models.length}]: `)).trim();
|
|
3478
|
+
rl3.close();
|
|
3479
|
+
const modelIdx = parseInt(modelChoice, 10) - 1;
|
|
3480
|
+
selectedModel = modelIdx >= 0 && modelIdx < models.length ? models[modelIdx] : models[0];
|
|
3481
|
+
}
|
|
3438
3482
|
console.log(` Using: ${selectedModel}`);
|
|
3439
3483
|
try {
|
|
3440
3484
|
const resp = await fetch(`${gatewayUrl}/api/v1/settings/inference`, {
|
|
@@ -3468,7 +3512,7 @@ async function installCommand(opts = {}) {
|
|
|
3468
3512
|
}
|
|
3469
3513
|
}
|
|
3470
3514
|
try {
|
|
3471
|
-
await setupGithubCommand({ skipClaudeToken: true });
|
|
3515
|
+
await setupGithubCommand({ skipClaudeToken: true, githubToken: opts.githubToken });
|
|
3472
3516
|
} catch (err) {
|
|
3473
3517
|
console.warn(` \u26A0 GitHub setup failed: ${err.message}`);
|
|
3474
3518
|
console.warn(" Run `synkro-cli setup-github` to retry.\n");
|
|
@@ -4488,7 +4532,16 @@ Usage:
|
|
|
4488
4532
|
synkro <command> [options] (alias)
|
|
4489
4533
|
|
|
4490
4534
|
Commands:
|
|
4491
|
-
install [
|
|
4535
|
+
install [options] Install Synkro hooks for detected agents (Claude Code, etc.)
|
|
4536
|
+
--force Reinstall from scratch
|
|
4537
|
+
--non-interactive Skip all interactive prompts (use flags)
|
|
4538
|
+
--link-repo Auto-link the local git repo
|
|
4539
|
+
--sync-transcripts Sync CC session history (default in non-interactive)
|
|
4540
|
+
--no-sync-transcripts Skip transcript sync
|
|
4541
|
+
--pr-scan=MODE PR scan auth: claude-oauth, byok, or skip
|
|
4542
|
+
--inference-key=KEY BYOK API key (Anthropic, OpenAI, or Gemini)
|
|
4543
|
+
--inference-model=M Model for classification + grading
|
|
4544
|
+
--github-token=TOK GitHub PAT for pushing secrets + workflow
|
|
4492
4545
|
login Authenticate with Synkro (browser OAuth via WorkOS)
|
|
4493
4546
|
logout Clear local credentials
|
|
4494
4547
|
status Show current setup state
|