hillclimb 0.1.7 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli.js +342 -291
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/cli.js
CHANGED
|
@@ -30,6 +30,29 @@ import os from "os";
|
|
|
30
30
|
import path from "path";
|
|
31
31
|
var CONFIG_DIR = path.join(os.homedir(), ".hillclimb");
|
|
32
32
|
var CONFIG_PATH = path.join(CONFIG_DIR, "projects.json");
|
|
33
|
+
function normalizeProjectConfig(raw) {
|
|
34
|
+
if (!raw || typeof raw !== "object") return null;
|
|
35
|
+
const config = raw;
|
|
36
|
+
const projectId = config.projectId ?? config.workspaceId;
|
|
37
|
+
const projectSlug = config.projectSlug ?? config.workspaceSlug;
|
|
38
|
+
const projectName = config.projectName ?? config.workspaceName;
|
|
39
|
+
if (!config.apiBaseUrl || !projectId || !projectSlug || !projectName || !config.contributionTypeSlug || !config.contributionTypeName || typeof config.autoSubmit !== "boolean") {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
...config,
|
|
44
|
+
apiBaseUrl: config.apiBaseUrl,
|
|
45
|
+
projectId,
|
|
46
|
+
projectSlug,
|
|
47
|
+
projectName,
|
|
48
|
+
workspaceId: config.workspaceId ?? projectId,
|
|
49
|
+
workspaceSlug: config.workspaceSlug ?? projectSlug,
|
|
50
|
+
workspaceName: config.workspaceName ?? projectName,
|
|
51
|
+
contributionTypeSlug: config.contributionTypeSlug,
|
|
52
|
+
contributionTypeName: config.contributionTypeName,
|
|
53
|
+
autoSubmit: config.autoSubmit
|
|
54
|
+
};
|
|
55
|
+
}
|
|
33
56
|
function configDir() {
|
|
34
57
|
return CONFIG_DIR;
|
|
35
58
|
}
|
|
@@ -43,7 +66,12 @@ async function loadProjects() {
|
|
|
43
66
|
if (!parsed.projects || typeof parsed.projects !== "object") {
|
|
44
67
|
return { projects: {} };
|
|
45
68
|
}
|
|
46
|
-
|
|
69
|
+
const projects = {};
|
|
70
|
+
for (const [repoRoot, config] of Object.entries(parsed.projects)) {
|
|
71
|
+
const normalized = normalizeProjectConfig(config);
|
|
72
|
+
if (normalized) projects[repoRoot] = normalized;
|
|
73
|
+
}
|
|
74
|
+
return { projects };
|
|
47
75
|
} catch {
|
|
48
76
|
return { projects: {} };
|
|
49
77
|
}
|
|
@@ -58,7 +86,9 @@ async function saveProjects(file) {
|
|
|
58
86
|
}
|
|
59
87
|
async function upsertProject(repoRoot, config) {
|
|
60
88
|
const file = await loadProjects();
|
|
61
|
-
|
|
89
|
+
const normalized = normalizeProjectConfig(config);
|
|
90
|
+
if (!normalized) throw new Error("Invalid project config.");
|
|
91
|
+
file.projects[path.resolve(repoRoot)] = normalized;
|
|
62
92
|
await saveProjects(file);
|
|
63
93
|
}
|
|
64
94
|
async function findProjectForCwd(cwd) {
|
|
@@ -398,30 +428,51 @@ var PlatformClient = class {
|
|
|
398
428
|
};
|
|
399
429
|
}
|
|
400
430
|
async listProjects() {
|
|
401
|
-
const data = await this.request(
|
|
402
|
-
|
|
403
|
-
"/api/v1/projects"
|
|
404
|
-
);
|
|
405
|
-
return data.workspaces ?? [];
|
|
431
|
+
const data = await this.request("GET", "/api/v1/projects");
|
|
432
|
+
return data.projects ?? data.workspaces ?? [];
|
|
406
433
|
}
|
|
407
|
-
async
|
|
408
|
-
|
|
409
|
-
"GET",
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
434
|
+
async getProjectBySlug(slug) {
|
|
435
|
+
try {
|
|
436
|
+
const data = await this.request("GET", `/api/v1/projects/by-slug/${encodeURIComponent(slug)}`);
|
|
437
|
+
const project = data.project ?? data.workspace;
|
|
438
|
+
if (!project) throw new PlatformError("Project response was empty.");
|
|
439
|
+
return project;
|
|
440
|
+
} catch (err) {
|
|
441
|
+
if (!(err instanceof PlatformError) || err.status !== 404) throw err;
|
|
442
|
+
const data = await this.request(
|
|
443
|
+
"GET",
|
|
444
|
+
`/api/v1/workspaces/${encodeURIComponent(slug)}`
|
|
445
|
+
);
|
|
446
|
+
return data.workspace;
|
|
447
|
+
}
|
|
413
448
|
}
|
|
414
|
-
async listContributionTypes(
|
|
415
|
-
|
|
416
|
-
|
|
449
|
+
async listContributionTypes(projectId) {
|
|
450
|
+
try {
|
|
451
|
+
const data = await this.request("GET", `/api/v1/projects/${projectId}/contribution-types`);
|
|
452
|
+
return data.contributionTypes ?? [];
|
|
453
|
+
} catch (err) {
|
|
454
|
+
if (!(err instanceof PlatformError) || err.status !== 404) throw err;
|
|
455
|
+
const data = await this.request("GET", `/api/v1/workspaces/${projectId}/contribution-types`);
|
|
456
|
+
return data.contributionTypes ?? [];
|
|
457
|
+
}
|
|
417
458
|
}
|
|
418
|
-
async createContribution(
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
459
|
+
async createContribution(projectId, input) {
|
|
460
|
+
try {
|
|
461
|
+
const data = await this.request(
|
|
462
|
+
"POST",
|
|
463
|
+
`/api/v1/projects/${projectId}/contributions`,
|
|
464
|
+
input
|
|
465
|
+
);
|
|
466
|
+
return data.contribution;
|
|
467
|
+
} catch (err) {
|
|
468
|
+
if (!(err instanceof PlatformError) || err.status !== 404) throw err;
|
|
469
|
+
const data = await this.request(
|
|
470
|
+
"POST",
|
|
471
|
+
`/api/v1/workspaces/${projectId}/contributions`,
|
|
472
|
+
input
|
|
473
|
+
);
|
|
474
|
+
return data.contribution;
|
|
475
|
+
}
|
|
425
476
|
}
|
|
426
477
|
async createUpload(contributionId, input) {
|
|
427
478
|
return this.request(
|
|
@@ -1271,10 +1322,10 @@ async function tryReuseIdentity(apiBaseUrl, client, identity) {
|
|
|
1271
1322
|
}
|
|
1272
1323
|
async function runInit(args = []) {
|
|
1273
1324
|
const forceLogin = args.includes("--login");
|
|
1274
|
-
const
|
|
1325
|
+
const projectSlugFlag = parseStringFlag(args, "project") ?? parseStringFlag(args, "workspace");
|
|
1275
1326
|
appendLog(
|
|
1276
1327
|
"info",
|
|
1277
|
-
`init: started (cwd=${process.cwd()}, forceLogin=${forceLogin},
|
|
1328
|
+
`init: started (cwd=${process.cwd()}, forceLogin=${forceLogin}, projectSlug=${projectSlugFlag ?? "<none>"})`
|
|
1278
1329
|
);
|
|
1279
1330
|
p3.intro("hillclimb");
|
|
1280
1331
|
const repo = detectRepoRoot();
|
|
@@ -1295,8 +1346,8 @@ async function runInit(args = []) {
|
|
|
1295
1346
|
bootstrap = await tryReuseIdentity(apiBaseUrl, client, saved);
|
|
1296
1347
|
}
|
|
1297
1348
|
if (!bootstrap) {
|
|
1298
|
-
let method =
|
|
1299
|
-
if (IS_TTY && !
|
|
1349
|
+
let method = projectSlugFlag ? "web" : "email";
|
|
1350
|
+
if (IS_TTY && !projectSlugFlag) {
|
|
1300
1351
|
const selected = await p3.select({
|
|
1301
1352
|
message: "How would you like to sign in?",
|
|
1302
1353
|
options: [
|
|
@@ -1356,13 +1407,13 @@ async function runInit(args = []) {
|
|
|
1356
1407
|
);
|
|
1357
1408
|
}
|
|
1358
1409
|
}
|
|
1359
|
-
let
|
|
1360
|
-
if (
|
|
1410
|
+
let project;
|
|
1411
|
+
if (projectSlugFlag) {
|
|
1361
1412
|
const resolved = await withSpinner(
|
|
1362
|
-
`Looking up project "${
|
|
1413
|
+
`Looking up project "${projectSlugFlag}"...`,
|
|
1363
1414
|
(w) => `Found project: ${w.name}`,
|
|
1364
|
-
`Could not find project "${
|
|
1365
|
-
() => client.
|
|
1415
|
+
`Could not find project "${projectSlugFlag}".`,
|
|
1416
|
+
() => client.getProjectBySlug(projectSlugFlag)
|
|
1366
1417
|
);
|
|
1367
1418
|
if (resolved.status !== "active") {
|
|
1368
1419
|
p3.log.error(
|
|
@@ -1371,21 +1422,21 @@ async function runInit(args = []) {
|
|
|
1371
1422
|
p3.outro("Aborted.");
|
|
1372
1423
|
process.exit(1);
|
|
1373
1424
|
}
|
|
1374
|
-
|
|
1425
|
+
project = resolved;
|
|
1375
1426
|
appendLog(
|
|
1376
1427
|
"info",
|
|
1377
|
-
`init: project pre-scoped (slug=${
|
|
1428
|
+
`init: project pre-scoped (slug=${projectSlugFlag}, id=${resolved.id})`
|
|
1378
1429
|
);
|
|
1379
1430
|
} else {
|
|
1380
|
-
const
|
|
1431
|
+
const projects = await withSpinner(
|
|
1381
1432
|
"Loading projects...",
|
|
1382
1433
|
(ws) => `Found ${ws.length} project(s).`,
|
|
1383
1434
|
"Could not list projects.",
|
|
1384
1435
|
() => client.listProjects()
|
|
1385
1436
|
);
|
|
1386
|
-
const
|
|
1387
|
-
const
|
|
1388
|
-
if (
|
|
1437
|
+
const activeProjects = projects.filter((w) => w.status === "active");
|
|
1438
|
+
const projectChoices = activeProjects.length > 0 ? activeProjects : projects;
|
|
1439
|
+
if (projectChoices.length === 0) {
|
|
1389
1440
|
p3.log.error(
|
|
1390
1441
|
"You do not have access to any projects. Create one in the web UI first."
|
|
1391
1442
|
);
|
|
@@ -1393,44 +1444,44 @@ async function runInit(args = []) {
|
|
|
1393
1444
|
process.exit(1);
|
|
1394
1445
|
}
|
|
1395
1446
|
let picked;
|
|
1396
|
-
if (
|
|
1397
|
-
picked =
|
|
1447
|
+
if (projectChoices.length === 1) {
|
|
1448
|
+
picked = projectChoices[0];
|
|
1398
1449
|
p3.log.info(`Using project: ${picked.name}`);
|
|
1399
1450
|
} else {
|
|
1400
|
-
const
|
|
1451
|
+
const selectedProjectId = await p3.select({
|
|
1401
1452
|
message: "Select a project",
|
|
1402
|
-
options:
|
|
1453
|
+
options: projectChoices.map((w) => ({
|
|
1403
1454
|
value: w.id,
|
|
1404
1455
|
label: w.name,
|
|
1405
1456
|
hint: `${w.slug} (${w.status})`
|
|
1406
1457
|
}))
|
|
1407
1458
|
});
|
|
1408
|
-
if (p3.isCancel(
|
|
1459
|
+
if (p3.isCancel(selectedProjectId)) {
|
|
1409
1460
|
p3.cancel("Cancelled.");
|
|
1410
1461
|
process.exit(0);
|
|
1411
1462
|
}
|
|
1412
|
-
picked =
|
|
1463
|
+
picked = projectChoices.find((w) => w.id === selectedProjectId);
|
|
1413
1464
|
}
|
|
1414
1465
|
if (!picked) {
|
|
1415
|
-
p3.log.error("Invalid
|
|
1466
|
+
p3.log.error("Invalid project selection.");
|
|
1416
1467
|
p3.outro("Aborted.");
|
|
1417
1468
|
process.exit(1);
|
|
1418
1469
|
}
|
|
1419
|
-
|
|
1470
|
+
project = picked;
|
|
1420
1471
|
appendLog(
|
|
1421
1472
|
"info",
|
|
1422
|
-
`init: project selected (slug=${
|
|
1473
|
+
`init: project selected (slug=${project.slug}, id=${project.id})`
|
|
1423
1474
|
);
|
|
1424
1475
|
}
|
|
1425
1476
|
const types = await withSpinner(
|
|
1426
1477
|
"Loading contribution types...",
|
|
1427
1478
|
(t) => `Found ${t.length} contribution type(s).`,
|
|
1428
1479
|
"Could not list contribution types.",
|
|
1429
|
-
() => client.listContributionTypes(
|
|
1480
|
+
() => client.listContributionTypes(project.id)
|
|
1430
1481
|
);
|
|
1431
1482
|
if (types.length === 0) {
|
|
1432
1483
|
p3.log.error(
|
|
1433
|
-
`Project "${
|
|
1484
|
+
`Project "${project.name}" has no contribution types. Create one in the web UI first.`
|
|
1434
1485
|
);
|
|
1435
1486
|
p3.outro("Aborted.");
|
|
1436
1487
|
process.exit(1);
|
|
@@ -1477,10 +1528,13 @@ async function runInit(args = []) {
|
|
|
1477
1528
|
);
|
|
1478
1529
|
await upsertProject(repoRoot, {
|
|
1479
1530
|
apiBaseUrl,
|
|
1480
|
-
organizationId:
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1531
|
+
organizationId: project.organizationId,
|
|
1532
|
+
projectId: project.id,
|
|
1533
|
+
projectSlug: project.slug,
|
|
1534
|
+
projectName: project.name,
|
|
1535
|
+
workspaceId: project.id,
|
|
1536
|
+
workspaceSlug: project.slug,
|
|
1537
|
+
workspaceName: project.name,
|
|
1484
1538
|
contributionTypeSlug: type.slug,
|
|
1485
1539
|
contributionTypeName: type.name,
|
|
1486
1540
|
autoSubmit: true
|
|
@@ -1545,10 +1599,10 @@ async function runInit(args = []) {
|
|
|
1545
1599
|
}
|
|
1546
1600
|
appendLog(
|
|
1547
1601
|
"info",
|
|
1548
|
-
`init: completed (project=${
|
|
1602
|
+
`init: completed (project=${project.slug}, contributionType=${type.slug})`
|
|
1549
1603
|
);
|
|
1550
1604
|
p3.outro(
|
|
1551
|
-
`Done. Your next coding session in this repo will upload automatically to ${
|
|
1605
|
+
`Done. Your next coding session in this repo will upload automatically to ${project.name}.`
|
|
1552
1606
|
);
|
|
1553
1607
|
}
|
|
1554
1608
|
|
|
@@ -1618,10 +1672,7 @@ async function runLogin(_args = []) {
|
|
|
1618
1672
|
process.stdout.write("\r\x1B[K");
|
|
1619
1673
|
}
|
|
1620
1674
|
if (verified) {
|
|
1621
|
-
appendLog(
|
|
1622
|
-
"info",
|
|
1623
|
-
`login: reused saved login (email=${verified.email})`
|
|
1624
|
-
);
|
|
1675
|
+
appendLog("info", `login: reused saved login (email=${verified.email})`);
|
|
1625
1676
|
row(CHECK, "Signed in", cyan(verified.email));
|
|
1626
1677
|
const fresh = probe.getSessionCookie();
|
|
1627
1678
|
if (fresh) {
|
|
@@ -1762,9 +1813,7 @@ async function runLogout(args = []) {
|
|
|
1762
1813
|
footer("Nothing to do.");
|
|
1763
1814
|
return;
|
|
1764
1815
|
}
|
|
1765
|
-
println(
|
|
1766
|
-
` ${CHECK} ${url ? `Signed out from ${bold(url)}` : "Signed out"}`
|
|
1767
|
-
);
|
|
1816
|
+
println(` ${CHECK} ${url ? `Signed out from ${bold(url)}` : "Signed out"}`);
|
|
1768
1817
|
footer(
|
|
1769
1818
|
"Per-repo upload hooks still active.",
|
|
1770
1819
|
"Re-run `npx hillclimb` to re-authenticate a repo."
|
|
@@ -1829,7 +1878,7 @@ async function runStatus(args = []) {
|
|
|
1829
1878
|
} else {
|
|
1830
1879
|
row(CROSS, "Login", dim(`couldn't reach ${config.apiBaseUrl}`));
|
|
1831
1880
|
}
|
|
1832
|
-
row(CHECK, "Project", bold(config.
|
|
1881
|
+
row(CHECK, "Project", bold(config.projectName));
|
|
1833
1882
|
row(CHECK, "Contribution", bold(config.contributionTypeName));
|
|
1834
1883
|
row(CHECK, "Repo", bold(repoName));
|
|
1835
1884
|
let anyHookInstalled = false;
|
|
@@ -1870,8 +1919,8 @@ async function runStatus(args = []) {
|
|
|
1870
1919
|
if (config.organizationId) {
|
|
1871
1920
|
debugRow("Legacy org ID:", config.organizationId);
|
|
1872
1921
|
}
|
|
1873
|
-
const
|
|
1874
|
-
debugRow("Project:", `${config.
|
|
1922
|
+
const projectIdent = config.projectSlug === config.projectId ? config.projectId : `${config.projectSlug} / ${config.projectId}`;
|
|
1923
|
+
debugRow("Project:", `${config.projectName} (${projectIdent})`);
|
|
1875
1924
|
debugRow(
|
|
1876
1925
|
"Contribution type:",
|
|
1877
1926
|
`${config.contributionTypeName} (${config.contributionTypeSlug})`
|
|
@@ -10390,8 +10439,214 @@ var RedactMiddleware = class {
|
|
|
10390
10439
|
// src/middleware/index.ts
|
|
10391
10440
|
var middleware = [];
|
|
10392
10441
|
|
|
10393
|
-
// src/
|
|
10442
|
+
// src/middleware/secrets.ts
|
|
10443
|
+
import fs6 from "fs";
|
|
10394
10444
|
import path7 from "path";
|
|
10445
|
+
var KNOWN_NON_SECRETS = /* @__PURE__ */ new Set([
|
|
10446
|
+
"true",
|
|
10447
|
+
"false",
|
|
10448
|
+
"null",
|
|
10449
|
+
"undefined",
|
|
10450
|
+
"yes",
|
|
10451
|
+
"no",
|
|
10452
|
+
"on",
|
|
10453
|
+
"off",
|
|
10454
|
+
"localhost",
|
|
10455
|
+
"127.0.0.1",
|
|
10456
|
+
"0.0.0.0",
|
|
10457
|
+
"::1",
|
|
10458
|
+
"development",
|
|
10459
|
+
"production",
|
|
10460
|
+
"staging",
|
|
10461
|
+
"test"
|
|
10462
|
+
]);
|
|
10463
|
+
var STRUCTURAL_VARS = /* @__PURE__ */ new Set([
|
|
10464
|
+
"PATH",
|
|
10465
|
+
"HOME",
|
|
10466
|
+
"SHELL",
|
|
10467
|
+
"USER",
|
|
10468
|
+
"LOGNAME",
|
|
10469
|
+
"LANG",
|
|
10470
|
+
"TERM",
|
|
10471
|
+
"PWD",
|
|
10472
|
+
"OLDPWD",
|
|
10473
|
+
"HOSTNAME",
|
|
10474
|
+
"DISPLAY",
|
|
10475
|
+
"EDITOR",
|
|
10476
|
+
"VISUAL",
|
|
10477
|
+
"PAGER",
|
|
10478
|
+
"SHLVL",
|
|
10479
|
+
"_",
|
|
10480
|
+
"NODE_ENV"
|
|
10481
|
+
]);
|
|
10482
|
+
var SENSITIVE_PATTERNS = [
|
|
10483
|
+
/_TOKEN$/,
|
|
10484
|
+
/_SECRET$/,
|
|
10485
|
+
/_KEY$/,
|
|
10486
|
+
/_PASSWORD$/,
|
|
10487
|
+
/_API$/,
|
|
10488
|
+
/_AUTH$/,
|
|
10489
|
+
/_CREDENTIAL$/,
|
|
10490
|
+
/_PASS$/,
|
|
10491
|
+
/SECRET/,
|
|
10492
|
+
/PASSWORD/,
|
|
10493
|
+
/PRIVATE/
|
|
10494
|
+
];
|
|
10495
|
+
var SENSITIVE_EXACT = /* @__PURE__ */ new Set([
|
|
10496
|
+
"DATABASE_URL",
|
|
10497
|
+
"REDIS_URL",
|
|
10498
|
+
"MONGO_URI",
|
|
10499
|
+
"AWS_ACCESS_KEY_ID",
|
|
10500
|
+
"AWS_SECRET_ACCESS_KEY",
|
|
10501
|
+
"SENTRY_DSN",
|
|
10502
|
+
"SLACK_WEBHOOK_URL",
|
|
10503
|
+
"STRIPE_SK"
|
|
10504
|
+
]);
|
|
10505
|
+
function isSensitiveKey(key) {
|
|
10506
|
+
if (SENSITIVE_EXACT.has(key)) return true;
|
|
10507
|
+
return SENSITIVE_PATTERNS.some((pattern) => pattern.test(key));
|
|
10508
|
+
}
|
|
10509
|
+
function isStructuralVar(key) {
|
|
10510
|
+
if (STRUCTURAL_VARS.has(key)) return true;
|
|
10511
|
+
if (key.startsWith("XDG_")) return true;
|
|
10512
|
+
return false;
|
|
10513
|
+
}
|
|
10514
|
+
function isUsableValue(value) {
|
|
10515
|
+
if (value.length < 4) return false;
|
|
10516
|
+
if (KNOWN_NON_SECRETS.has(value.toLowerCase())) return false;
|
|
10517
|
+
if (/^\d+$/.test(value)) return false;
|
|
10518
|
+
return true;
|
|
10519
|
+
}
|
|
10520
|
+
async function parseEnvFile(filePath) {
|
|
10521
|
+
const values = [];
|
|
10522
|
+
let content;
|
|
10523
|
+
try {
|
|
10524
|
+
content = await fs6.promises.readFile(filePath, "utf-8");
|
|
10525
|
+
} catch {
|
|
10526
|
+
return values;
|
|
10527
|
+
}
|
|
10528
|
+
for (const line of content.split("\n")) {
|
|
10529
|
+
const trimmed = line.trim();
|
|
10530
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
10531
|
+
const eqIndex = trimmed.indexOf("=");
|
|
10532
|
+
if (eqIndex === -1) continue;
|
|
10533
|
+
let value = trimmed.slice(eqIndex + 1).trim();
|
|
10534
|
+
if (value.startsWith('"') || value.startsWith("'")) {
|
|
10535
|
+
const quote = value[0];
|
|
10536
|
+
let end = -1;
|
|
10537
|
+
for (let i = 1; i < value.length; i++) {
|
|
10538
|
+
if (value[i] === "\\" && i + 1 < value.length) {
|
|
10539
|
+
i++;
|
|
10540
|
+
continue;
|
|
10541
|
+
}
|
|
10542
|
+
if (value[i] === quote) {
|
|
10543
|
+
end = i;
|
|
10544
|
+
break;
|
|
10545
|
+
}
|
|
10546
|
+
}
|
|
10547
|
+
if (end !== -1) {
|
|
10548
|
+
value = value.slice(1, end).replace(/\\(.)/g, "$1");
|
|
10549
|
+
} else {
|
|
10550
|
+
value = value.slice(1);
|
|
10551
|
+
}
|
|
10552
|
+
} else {
|
|
10553
|
+
const commentIndex = value.indexOf(" #");
|
|
10554
|
+
if (commentIndex !== -1) {
|
|
10555
|
+
value = value.slice(0, commentIndex);
|
|
10556
|
+
}
|
|
10557
|
+
value = value.trim();
|
|
10558
|
+
}
|
|
10559
|
+
if (!value) continue;
|
|
10560
|
+
values.push(value);
|
|
10561
|
+
}
|
|
10562
|
+
return values;
|
|
10563
|
+
}
|
|
10564
|
+
function addWithVariants(set, value) {
|
|
10565
|
+
set.add(value);
|
|
10566
|
+
const encoded = encodeURIComponent(value);
|
|
10567
|
+
if (encoded !== value) {
|
|
10568
|
+
set.add(encoded);
|
|
10569
|
+
}
|
|
10570
|
+
if (value.includes("://")) {
|
|
10571
|
+
try {
|
|
10572
|
+
const url = new URL(value);
|
|
10573
|
+
if (url.password) {
|
|
10574
|
+
const rawPassword = url.password;
|
|
10575
|
+
set.add(rawPassword);
|
|
10576
|
+
const decodedPassword = decodeURIComponent(rawPassword);
|
|
10577
|
+
if (decodedPassword !== rawPassword) {
|
|
10578
|
+
set.add(decodedPassword);
|
|
10579
|
+
}
|
|
10580
|
+
const encodedPw = encodeURIComponent(decodedPassword);
|
|
10581
|
+
if (encodedPw !== decodedPassword && encodedPw !== rawPassword) {
|
|
10582
|
+
set.add(encodedPw);
|
|
10583
|
+
}
|
|
10584
|
+
}
|
|
10585
|
+
} catch {
|
|
10586
|
+
}
|
|
10587
|
+
}
|
|
10588
|
+
}
|
|
10589
|
+
async function discoverEnvFiles(repoRoot) {
|
|
10590
|
+
let entries;
|
|
10591
|
+
try {
|
|
10592
|
+
entries = await fs6.promises.readdir(repoRoot);
|
|
10593
|
+
} catch {
|
|
10594
|
+
return [];
|
|
10595
|
+
}
|
|
10596
|
+
const envFiles = [];
|
|
10597
|
+
for (const name of entries) {
|
|
10598
|
+
if (!name.startsWith(".env")) continue;
|
|
10599
|
+
const filePath = path7.join(repoRoot, name);
|
|
10600
|
+
try {
|
|
10601
|
+
const stat = await fs6.promises.stat(filePath);
|
|
10602
|
+
if (stat.isFile()) envFiles.push(name);
|
|
10603
|
+
} catch {
|
|
10604
|
+
}
|
|
10605
|
+
}
|
|
10606
|
+
return envFiles;
|
|
10607
|
+
}
|
|
10608
|
+
async function collectSecrets(repoRoot, envFiles, additionalFiles) {
|
|
10609
|
+
const values = /* @__PURE__ */ new Set();
|
|
10610
|
+
const sourceFiles = [];
|
|
10611
|
+
let processEnvCount = 0;
|
|
10612
|
+
let skippedCount = 0;
|
|
10613
|
+
for (const filePath of envFiles) {
|
|
10614
|
+
sourceFiles.push(filePath);
|
|
10615
|
+
for (const value of await parseEnvFile(filePath)) {
|
|
10616
|
+
if (isUsableValue(value)) {
|
|
10617
|
+
addWithVariants(values, value);
|
|
10618
|
+
} else {
|
|
10619
|
+
skippedCount++;
|
|
10620
|
+
}
|
|
10621
|
+
}
|
|
10622
|
+
}
|
|
10623
|
+
for (const filePath of additionalFiles) {
|
|
10624
|
+
const resolved = path7.resolve(repoRoot, filePath);
|
|
10625
|
+
sourceFiles.push(resolved);
|
|
10626
|
+
for (const value of await parseEnvFile(resolved)) {
|
|
10627
|
+
if (isUsableValue(value)) {
|
|
10628
|
+
addWithVariants(values, value);
|
|
10629
|
+
} else {
|
|
10630
|
+
skippedCount++;
|
|
10631
|
+
}
|
|
10632
|
+
}
|
|
10633
|
+
}
|
|
10634
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
10635
|
+
if (!value) continue;
|
|
10636
|
+
if (isStructuralVar(key)) continue;
|
|
10637
|
+
if (!isSensitiveKey(key)) continue;
|
|
10638
|
+
if (isUsableValue(value)) {
|
|
10639
|
+
addWithVariants(values, value);
|
|
10640
|
+
processEnvCount++;
|
|
10641
|
+
} else {
|
|
10642
|
+
skippedCount++;
|
|
10643
|
+
}
|
|
10644
|
+
}
|
|
10645
|
+
return { values, sourceFiles, processEnvCount, skippedCount };
|
|
10646
|
+
}
|
|
10647
|
+
|
|
10648
|
+
// src/normalizer/index.ts
|
|
10649
|
+
import path8 from "path";
|
|
10395
10650
|
|
|
10396
10651
|
// src/normalizer/claude.ts
|
|
10397
10652
|
function stringify(value) {
|
|
@@ -10796,8 +11051,7 @@ function convertClaudeToTrajectory(jsonlContent, sessionId) {
|
|
|
10796
11051
|
finalExtra.service_tiers = [...serviceTiers].sort();
|
|
10797
11052
|
if (cacheCreationSeen)
|
|
10798
11053
|
finalExtra.total_cache_creation_input_tokens = cacheCreationTotal;
|
|
10799
|
-
if (cacheReadSeen)
|
|
10800
|
-
finalExtra.total_cache_read_input_tokens = cacheReadTotal;
|
|
11054
|
+
if (cacheReadSeen) finalExtra.total_cache_read_input_tokens = cacheReadTotal;
|
|
10801
11055
|
const finalMetrics = {
|
|
10802
11056
|
total_prompt_tokens: promptValues.length > 0 ? promptValues.reduce((a, b) => a + b, 0) : void 0,
|
|
10803
11057
|
total_completion_tokens: completionValues.length > 0 ? completionValues.reduce((a, b) => a + b, 0) : void 0,
|
|
@@ -10855,7 +11109,8 @@ function convertEventToStep(event, stepId, defaultModelName) {
|
|
|
10855
11109
|
};
|
|
10856
11110
|
const observation = event.output !== void 0 ? { results: [observationResult] } : void 0;
|
|
10857
11111
|
const extra = { ...event.extra ?? {} };
|
|
10858
|
-
if (event.metadata !== void 0)
|
|
11112
|
+
if (event.metadata !== void 0)
|
|
11113
|
+
extra.metadata = extra.metadata ?? event.metadata;
|
|
10859
11114
|
if (event.raw_arguments !== void 0)
|
|
10860
11115
|
extra.raw_arguments = extra.raw_arguments ?? event.raw_arguments;
|
|
10861
11116
|
if (event.status !== void 0) extra.status = extra.status ?? event.status;
|
|
@@ -10927,7 +11182,7 @@ function convertCodexToTrajectory(jsonlContent, sessionId) {
|
|
|
10927
11182
|
const sessionMeta = rawEvents.find((e) => e.type === "session_meta");
|
|
10928
11183
|
const metaPayload = sessionMeta?.payload ?? {};
|
|
10929
11184
|
const sid = sessionId ?? metaPayload.id ?? "";
|
|
10930
|
-
|
|
11185
|
+
const agentVersion = metaPayload.cli_version ?? "unknown";
|
|
10931
11186
|
const agentExtra = {};
|
|
10932
11187
|
for (const key of ["originator", "cwd", "git", "instructions"]) {
|
|
10933
11188
|
const value = metaPayload[key];
|
|
@@ -11045,7 +11300,6 @@ function convertCodexToTrajectory(jsonlContent, sessionId) {
|
|
|
11045
11300
|
callInfo.timestamp = callInfo.timestamp ?? timestamp;
|
|
11046
11301
|
normalizedEvents.push(callInfo);
|
|
11047
11302
|
pendingReasoning = void 0;
|
|
11048
|
-
continue;
|
|
11049
11303
|
}
|
|
11050
11304
|
}
|
|
11051
11305
|
const steps = [];
|
|
@@ -11260,7 +11514,7 @@ var NormalizeMiddleware = class {
|
|
|
11260
11514
|
if (!["claude", "codex", "cursor"].includes(file.sourceName)) continue;
|
|
11261
11515
|
const content = file.content ? file.content.toString("utf-8") : null;
|
|
11262
11516
|
if (!content) continue;
|
|
11263
|
-
const sessionId = file.metadata?.sessionId ??
|
|
11517
|
+
const sessionId = file.metadata?.sessionId ?? path8.basename(file.absolutePath, ".jsonl");
|
|
11264
11518
|
try {
|
|
11265
11519
|
const trajectory = normalizeContent(
|
|
11266
11520
|
file.sourceName,
|
|
@@ -11273,10 +11527,7 @@ var NormalizeMiddleware = class {
|
|
|
11273
11527
|
null,
|
|
11274
11528
|
2
|
|
11275
11529
|
);
|
|
11276
|
-
const atifPath = file.absolutePath.replace(
|
|
11277
|
-
/\.jsonl$/,
|
|
11278
|
-
".atif.json"
|
|
11279
|
-
);
|
|
11530
|
+
const atifPath = file.absolutePath.replace(/\.jsonl$/, ".atif.json");
|
|
11280
11531
|
newFiles.push({
|
|
11281
11532
|
sourceName: file.sourceName,
|
|
11282
11533
|
absolutePath: atifPath,
|
|
@@ -11291,212 +11542,6 @@ var NormalizeMiddleware = class {
|
|
|
11291
11542
|
}
|
|
11292
11543
|
};
|
|
11293
11544
|
|
|
11294
|
-
// src/middleware/secrets.ts
|
|
11295
|
-
import fs6 from "fs";
|
|
11296
|
-
import path8 from "path";
|
|
11297
|
-
var KNOWN_NON_SECRETS = /* @__PURE__ */ new Set([
|
|
11298
|
-
"true",
|
|
11299
|
-
"false",
|
|
11300
|
-
"null",
|
|
11301
|
-
"undefined",
|
|
11302
|
-
"yes",
|
|
11303
|
-
"no",
|
|
11304
|
-
"on",
|
|
11305
|
-
"off",
|
|
11306
|
-
"localhost",
|
|
11307
|
-
"127.0.0.1",
|
|
11308
|
-
"0.0.0.0",
|
|
11309
|
-
"::1",
|
|
11310
|
-
"development",
|
|
11311
|
-
"production",
|
|
11312
|
-
"staging",
|
|
11313
|
-
"test"
|
|
11314
|
-
]);
|
|
11315
|
-
var STRUCTURAL_VARS = /* @__PURE__ */ new Set([
|
|
11316
|
-
"PATH",
|
|
11317
|
-
"HOME",
|
|
11318
|
-
"SHELL",
|
|
11319
|
-
"USER",
|
|
11320
|
-
"LOGNAME",
|
|
11321
|
-
"LANG",
|
|
11322
|
-
"TERM",
|
|
11323
|
-
"PWD",
|
|
11324
|
-
"OLDPWD",
|
|
11325
|
-
"HOSTNAME",
|
|
11326
|
-
"DISPLAY",
|
|
11327
|
-
"EDITOR",
|
|
11328
|
-
"VISUAL",
|
|
11329
|
-
"PAGER",
|
|
11330
|
-
"SHLVL",
|
|
11331
|
-
"_",
|
|
11332
|
-
"NODE_ENV"
|
|
11333
|
-
]);
|
|
11334
|
-
var SENSITIVE_PATTERNS = [
|
|
11335
|
-
/_TOKEN$/,
|
|
11336
|
-
/_SECRET$/,
|
|
11337
|
-
/_KEY$/,
|
|
11338
|
-
/_PASSWORD$/,
|
|
11339
|
-
/_API$/,
|
|
11340
|
-
/_AUTH$/,
|
|
11341
|
-
/_CREDENTIAL$/,
|
|
11342
|
-
/_PASS$/,
|
|
11343
|
-
/SECRET/,
|
|
11344
|
-
/PASSWORD/,
|
|
11345
|
-
/PRIVATE/
|
|
11346
|
-
];
|
|
11347
|
-
var SENSITIVE_EXACT = /* @__PURE__ */ new Set([
|
|
11348
|
-
"DATABASE_URL",
|
|
11349
|
-
"REDIS_URL",
|
|
11350
|
-
"MONGO_URI",
|
|
11351
|
-
"AWS_ACCESS_KEY_ID",
|
|
11352
|
-
"AWS_SECRET_ACCESS_KEY",
|
|
11353
|
-
"SENTRY_DSN",
|
|
11354
|
-
"SLACK_WEBHOOK_URL",
|
|
11355
|
-
"STRIPE_SK"
|
|
11356
|
-
]);
|
|
11357
|
-
function isSensitiveKey(key) {
|
|
11358
|
-
if (SENSITIVE_EXACT.has(key)) return true;
|
|
11359
|
-
return SENSITIVE_PATTERNS.some((pattern) => pattern.test(key));
|
|
11360
|
-
}
|
|
11361
|
-
function isStructuralVar(key) {
|
|
11362
|
-
if (STRUCTURAL_VARS.has(key)) return true;
|
|
11363
|
-
if (key.startsWith("XDG_")) return true;
|
|
11364
|
-
return false;
|
|
11365
|
-
}
|
|
11366
|
-
function isUsableValue(value) {
|
|
11367
|
-
if (value.length < 4) return false;
|
|
11368
|
-
if (KNOWN_NON_SECRETS.has(value.toLowerCase())) return false;
|
|
11369
|
-
if (/^\d+$/.test(value)) return false;
|
|
11370
|
-
return true;
|
|
11371
|
-
}
|
|
11372
|
-
async function parseEnvFile(filePath) {
|
|
11373
|
-
const values = [];
|
|
11374
|
-
let content;
|
|
11375
|
-
try {
|
|
11376
|
-
content = await fs6.promises.readFile(filePath, "utf-8");
|
|
11377
|
-
} catch {
|
|
11378
|
-
return values;
|
|
11379
|
-
}
|
|
11380
|
-
for (const line of content.split("\n")) {
|
|
11381
|
-
const trimmed = line.trim();
|
|
11382
|
-
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
11383
|
-
const eqIndex = trimmed.indexOf("=");
|
|
11384
|
-
if (eqIndex === -1) continue;
|
|
11385
|
-
let value = trimmed.slice(eqIndex + 1).trim();
|
|
11386
|
-
if (value.startsWith('"') || value.startsWith("'")) {
|
|
11387
|
-
const quote = value[0];
|
|
11388
|
-
let end = -1;
|
|
11389
|
-
for (let i = 1; i < value.length; i++) {
|
|
11390
|
-
if (value[i] === "\\" && i + 1 < value.length) {
|
|
11391
|
-
i++;
|
|
11392
|
-
continue;
|
|
11393
|
-
}
|
|
11394
|
-
if (value[i] === quote) {
|
|
11395
|
-
end = i;
|
|
11396
|
-
break;
|
|
11397
|
-
}
|
|
11398
|
-
}
|
|
11399
|
-
if (end !== -1) {
|
|
11400
|
-
value = value.slice(1, end).replace(/\\(.)/g, "$1");
|
|
11401
|
-
} else {
|
|
11402
|
-
value = value.slice(1);
|
|
11403
|
-
}
|
|
11404
|
-
} else {
|
|
11405
|
-
const commentIndex = value.indexOf(" #");
|
|
11406
|
-
if (commentIndex !== -1) {
|
|
11407
|
-
value = value.slice(0, commentIndex);
|
|
11408
|
-
}
|
|
11409
|
-
value = value.trim();
|
|
11410
|
-
}
|
|
11411
|
-
if (!value) continue;
|
|
11412
|
-
values.push(value);
|
|
11413
|
-
}
|
|
11414
|
-
return values;
|
|
11415
|
-
}
|
|
11416
|
-
function addWithVariants(set, value) {
|
|
11417
|
-
set.add(value);
|
|
11418
|
-
const encoded = encodeURIComponent(value);
|
|
11419
|
-
if (encoded !== value) {
|
|
11420
|
-
set.add(encoded);
|
|
11421
|
-
}
|
|
11422
|
-
if (value.includes("://")) {
|
|
11423
|
-
try {
|
|
11424
|
-
const url = new URL(value);
|
|
11425
|
-
if (url.password) {
|
|
11426
|
-
const rawPassword = url.password;
|
|
11427
|
-
set.add(rawPassword);
|
|
11428
|
-
const decodedPassword = decodeURIComponent(rawPassword);
|
|
11429
|
-
if (decodedPassword !== rawPassword) {
|
|
11430
|
-
set.add(decodedPassword);
|
|
11431
|
-
}
|
|
11432
|
-
const encodedPw = encodeURIComponent(decodedPassword);
|
|
11433
|
-
if (encodedPw !== decodedPassword && encodedPw !== rawPassword) {
|
|
11434
|
-
set.add(encodedPw);
|
|
11435
|
-
}
|
|
11436
|
-
}
|
|
11437
|
-
} catch {
|
|
11438
|
-
}
|
|
11439
|
-
}
|
|
11440
|
-
}
|
|
11441
|
-
async function discoverEnvFiles(repoRoot) {
|
|
11442
|
-
let entries;
|
|
11443
|
-
try {
|
|
11444
|
-
entries = await fs6.promises.readdir(repoRoot);
|
|
11445
|
-
} catch {
|
|
11446
|
-
return [];
|
|
11447
|
-
}
|
|
11448
|
-
const envFiles = [];
|
|
11449
|
-
for (const name of entries) {
|
|
11450
|
-
if (!name.startsWith(".env")) continue;
|
|
11451
|
-
const filePath = path8.join(repoRoot, name);
|
|
11452
|
-
try {
|
|
11453
|
-
const stat = await fs6.promises.stat(filePath);
|
|
11454
|
-
if (stat.isFile()) envFiles.push(name);
|
|
11455
|
-
} catch {
|
|
11456
|
-
}
|
|
11457
|
-
}
|
|
11458
|
-
return envFiles;
|
|
11459
|
-
}
|
|
11460
|
-
async function collectSecrets(repoRoot, envFiles, additionalFiles) {
|
|
11461
|
-
const values = /* @__PURE__ */ new Set();
|
|
11462
|
-
const sourceFiles = [];
|
|
11463
|
-
let processEnvCount = 0;
|
|
11464
|
-
let skippedCount = 0;
|
|
11465
|
-
for (const filePath of envFiles) {
|
|
11466
|
-
sourceFiles.push(filePath);
|
|
11467
|
-
for (const value of await parseEnvFile(filePath)) {
|
|
11468
|
-
if (isUsableValue(value)) {
|
|
11469
|
-
addWithVariants(values, value);
|
|
11470
|
-
} else {
|
|
11471
|
-
skippedCount++;
|
|
11472
|
-
}
|
|
11473
|
-
}
|
|
11474
|
-
}
|
|
11475
|
-
for (const filePath of additionalFiles) {
|
|
11476
|
-
const resolved = path8.resolve(repoRoot, filePath);
|
|
11477
|
-
sourceFiles.push(resolved);
|
|
11478
|
-
for (const value of await parseEnvFile(resolved)) {
|
|
11479
|
-
if (isUsableValue(value)) {
|
|
11480
|
-
addWithVariants(values, value);
|
|
11481
|
-
} else {
|
|
11482
|
-
skippedCount++;
|
|
11483
|
-
}
|
|
11484
|
-
}
|
|
11485
|
-
}
|
|
11486
|
-
for (const [key, value] of Object.entries(process.env)) {
|
|
11487
|
-
if (!value) continue;
|
|
11488
|
-
if (isStructuralVar(key)) continue;
|
|
11489
|
-
if (!isSensitiveKey(key)) continue;
|
|
11490
|
-
if (isUsableValue(value)) {
|
|
11491
|
-
addWithVariants(values, value);
|
|
11492
|
-
processEnvCount++;
|
|
11493
|
-
} else {
|
|
11494
|
-
skippedCount++;
|
|
11495
|
-
}
|
|
11496
|
-
}
|
|
11497
|
-
return { values, sourceFiles, processEnvCount, skippedCount };
|
|
11498
|
-
}
|
|
11499
|
-
|
|
11500
11545
|
// src/outputs/platform.ts
|
|
11501
11546
|
import { PassThrough } from "stream";
|
|
11502
11547
|
import archiver from "archiver";
|
|
@@ -11557,14 +11602,14 @@ var PlatformUploadOutput = class {
|
|
|
11557
11602
|
const buffer = await buildZipBuffer(group, selectedSources);
|
|
11558
11603
|
const {
|
|
11559
11604
|
client,
|
|
11560
|
-
|
|
11605
|
+
projectId,
|
|
11561
11606
|
contributionTypeSlug,
|
|
11562
11607
|
contributionTitle,
|
|
11563
11608
|
contributionBody,
|
|
11564
11609
|
zipFilename,
|
|
11565
11610
|
autoSubmit
|
|
11566
11611
|
} = this.opts;
|
|
11567
|
-
const contribution = await client.createContribution(
|
|
11612
|
+
const contribution = await client.createContribution(projectId, {
|
|
11568
11613
|
contributionTypeSlug,
|
|
11569
11614
|
title: contributionTitle,
|
|
11570
11615
|
body: contributionBody
|
|
@@ -11574,7 +11619,10 @@ var PlatformUploadOutput = class {
|
|
|
11574
11619
|
mimeType: "application/zip",
|
|
11575
11620
|
sizeBytes: buffer.byteLength
|
|
11576
11621
|
});
|
|
11577
|
-
appendLog(
|
|
11622
|
+
appendLog(
|
|
11623
|
+
"info",
|
|
11624
|
+
`uploading ${zipFilename} (${buffer.byteLength} bytes) to presigned URL`
|
|
11625
|
+
);
|
|
11578
11626
|
await client.uploadToPresignedUrl(
|
|
11579
11627
|
presigned.presignedUrl,
|
|
11580
11628
|
presigned.headers,
|
|
@@ -11846,7 +11894,7 @@ Uploaded: ${now.toISOString()}`;
|
|
|
11846
11894
|
const zipFilename = `${sourceTool}-${sanitize(shortId)}-${epochSeconds}.zip`;
|
|
11847
11895
|
const output = new PlatformUploadOutput({
|
|
11848
11896
|
client,
|
|
11849
|
-
|
|
11897
|
+
projectId: config.projectId,
|
|
11850
11898
|
contributionTypeSlug: config.contributionTypeSlug,
|
|
11851
11899
|
contributionTitle: title,
|
|
11852
11900
|
contributionBody: body,
|
|
@@ -11860,7 +11908,7 @@ Uploaded: ${now.toISOString()}`;
|
|
|
11860
11908
|
});
|
|
11861
11909
|
appendLog(
|
|
11862
11910
|
"info",
|
|
11863
|
-
`Uploaded session ${sessionId} to
|
|
11911
|
+
`Uploaded session ${sessionId} to project ${config.projectSlug} (${config.projectId}) as contribution ${contributionId}`
|
|
11864
11912
|
);
|
|
11865
11913
|
} catch (err) {
|
|
11866
11914
|
if (err instanceof PlatformError && err.status === 401) {
|
|
@@ -12461,7 +12509,10 @@ async function acquireLock(repoRoot, tool, retries = 3, delayMs = 200) {
|
|
|
12461
12509
|
await fs10.promises.mkdir(STATE_DIR, { recursive: true, mode: 448 });
|
|
12462
12510
|
for (let i = 0; i < retries; i++) {
|
|
12463
12511
|
try {
|
|
12464
|
-
const fd = await fs10.promises.open(
|
|
12512
|
+
const fd = await fs10.promises.open(
|
|
12513
|
+
lockPath,
|
|
12514
|
+
fs10.constants.O_CREAT | fs10.constants.O_EXCL | fs10.constants.O_WRONLY
|
|
12515
|
+
);
|
|
12465
12516
|
await fd.write(String(process.pid));
|
|
12466
12517
|
await fd.close();
|
|
12467
12518
|
return fd.fd;
|
|
@@ -12737,7 +12788,7 @@ async function handleStop(payload, tool) {
|
|
|
12737
12788
|
const epochSeconds = formatEpochSeconds2(now);
|
|
12738
12789
|
const shortId = state.sessionId.slice(0, 12);
|
|
12739
12790
|
const contribution = await client.createContribution(
|
|
12740
|
-
project.config.
|
|
12791
|
+
project.config.projectId,
|
|
12741
12792
|
{
|
|
12742
12793
|
contributionTypeSlug: GIT_TRACES_SLUG,
|
|
12743
12794
|
title: `${toolLabel} session ${shortId} \u2014 ${epochSeconds}`,
|
|
@@ -13798,12 +13849,12 @@ async function main() {
|
|
|
13798
13849
|
case "-h":
|
|
13799
13850
|
case "help": {
|
|
13800
13851
|
process.stdout.write(
|
|
13801
|
-
"Usage: npx hillclimb [subcommand]\n\nSubcommands:\n (none) Configure this repo for automatic upload\n init Configure this repo for automatic upload (--login forces fresh sign-in)\n export Interactive export flow (manual use)\n login Sign in and save the credential for reuse across repos\n logout Clear the saved sign-in (pass --url <api> to scope to one instance)\n status Show whether you're signed in and this repo is connected (pass --debug for details)\n upload Hook entry point \u2014 reads JSON payload from stdin and uploads one session\n help Show this message\n"
|
|
13852
|
+
"Usage: npx hillclimb [subcommand]\n\nSubcommands:\n (none) Configure this repo for automatic upload\n init Configure this repo for automatic upload (--project scopes setup, --login forces fresh sign-in)\n export Interactive export flow (manual use)\n login Sign in and save the credential for reuse across repos\n logout Clear the saved sign-in (pass --url <api> to scope to one instance)\n status Show whether you're signed in and this repo is connected (pass --debug for details)\n upload Hook entry point \u2014 reads JSON payload from stdin and uploads one session\n help Show this message\n"
|
|
13802
13853
|
);
|
|
13803
13854
|
return;
|
|
13804
13855
|
}
|
|
13805
13856
|
default:
|
|
13806
|
-
if (subcommand === "--login" || subcommand === "--workspace" || subcommand.startsWith("--workspace=")) {
|
|
13857
|
+
if (subcommand === "--login" || subcommand === "--project" || subcommand.startsWith("--project=") || subcommand === "--workspace" || subcommand.startsWith("--workspace=")) {
|
|
13807
13858
|
await runInit([subcommand, ...rest]);
|
|
13808
13859
|
return;
|
|
13809
13860
|
}
|