@merittdev/horus 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +119 -28
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -57239,7 +57239,7 @@ init_cjs_shims();
|
|
|
57239
57239
|
|
|
57240
57240
|
// ../../packages/core/src/version.ts
|
|
57241
57241
|
init_cjs_shims();
|
|
57242
|
-
var HORUS_VERSION = true ? "0.2.
|
|
57242
|
+
var HORUS_VERSION = true ? "0.2.2" : "dev";
|
|
57243
57243
|
var PINNED_AXON_VERSION = "1.0.7";
|
|
57244
57244
|
var PINNED_SOURCE_VERSION = PINNED_AXON_VERSION;
|
|
57245
57245
|
|
|
@@ -92205,6 +92205,18 @@ var CloudClient = class {
|
|
|
92205
92205
|
context() {
|
|
92206
92206
|
return this.request("GET", "/v1/context");
|
|
92207
92207
|
}
|
|
92208
|
+
/** Create a workspace in an org (HOR-307 — used by `cloud link` create-from-repo). */
|
|
92209
|
+
createWorkspace(organizationId, body) {
|
|
92210
|
+
return this.request("POST", `/v1/organizations/${organizationId}/workspaces`, body);
|
|
92211
|
+
}
|
|
92212
|
+
/** Create a project in a workspace, optionally with its repo identity (HOR-307). */
|
|
92213
|
+
createProject(organizationId, workspaceId, body) {
|
|
92214
|
+
return this.request(
|
|
92215
|
+
"POST",
|
|
92216
|
+
`/v1/organizations/${organizationId}/workspaces/${workspaceId}/projects`,
|
|
92217
|
+
body
|
|
92218
|
+
);
|
|
92219
|
+
}
|
|
92208
92220
|
listTokens() {
|
|
92209
92221
|
return this.request("GET", "/v1/cli/tokens");
|
|
92210
92222
|
}
|
|
@@ -93746,7 +93758,7 @@ async function uploadInvestigationToCloud(client, cfg, report) {
|
|
|
93746
93758
|
await client.createAgentRun(projectId, investigation.id, {
|
|
93747
93759
|
repositoryId: cfg.repository?.id,
|
|
93748
93760
|
status: "completed",
|
|
93749
|
-
cliVersion:
|
|
93761
|
+
cliVersion: HORUS_VERSION,
|
|
93750
93762
|
summary: report.summary,
|
|
93751
93763
|
idempotencyKey: idempotencyKey(report.id, "run")
|
|
93752
93764
|
});
|
|
@@ -98420,6 +98432,22 @@ function parseRepoName(remoteUrl) {
|
|
|
98420
98432
|
const lastSegment = cleaned.split(/[/:]/).filter(Boolean).pop() ?? "";
|
|
98421
98433
|
return lastSegment;
|
|
98422
98434
|
}
|
|
98435
|
+
function normalizeRemote(remoteUrl) {
|
|
98436
|
+
let s = remoteUrl.trim().replace(/\.git$/, "");
|
|
98437
|
+
s = s.replace(/^[a-z]+:\/\//i, "");
|
|
98438
|
+
s = s.replace(/^[^@/]+@/, "");
|
|
98439
|
+
s = s.replace(":", "/");
|
|
98440
|
+
return s.replace(/\/+$/, "").toLowerCase();
|
|
98441
|
+
}
|
|
98442
|
+
function remotesMatch(a, b2) {
|
|
98443
|
+
return normalizeRemote(a) === normalizeRemote(b2);
|
|
98444
|
+
}
|
|
98445
|
+
function inferProvider(remoteUrl) {
|
|
98446
|
+
const host = normalizeRemote(remoteUrl).split("/")[0] ?? "";
|
|
98447
|
+
if (host.includes("github")) return "github";
|
|
98448
|
+
if (host.includes("gitlab")) return "gitlab";
|
|
98449
|
+
return "other";
|
|
98450
|
+
}
|
|
98423
98451
|
function detectGitRemote(root) {
|
|
98424
98452
|
try {
|
|
98425
98453
|
const remoteUrl = (0, import_node_child_process10.execFileSync)("git", ["-C", root, "remote", "get-url", "origin"], {
|
|
@@ -98449,6 +98477,7 @@ async function runCloudLink(opts = {}) {
|
|
|
98449
98477
|
}
|
|
98450
98478
|
const remote = detectGitRemote(root);
|
|
98451
98479
|
if (remote) console.log(import_picocolors42.default.dim(`Detected git remote: ${remote.remoteUrl}`));
|
|
98480
|
+
let resolved = null;
|
|
98452
98481
|
let target = opts.project;
|
|
98453
98482
|
if (!target) {
|
|
98454
98483
|
const existing = readCloudConfig(root);
|
|
@@ -98456,24 +98485,40 @@ async function runCloudLink(opts = {}) {
|
|
|
98456
98485
|
target = `${existing.organization.slug}/${existing.workspace.slug}/${existing.project.slug}`;
|
|
98457
98486
|
}
|
|
98458
98487
|
}
|
|
98459
|
-
if (!target) {
|
|
98460
|
-
|
|
98488
|
+
if (!target && remote) {
|
|
98489
|
+
const match = ctx.projects.find((p) => p.remoteUrl && remotesMatch(p.remoteUrl, remote.remoteUrl));
|
|
98490
|
+
if (match) {
|
|
98491
|
+
const ws = ctx.workspaces.find((w) => w.id === match.workspaceId);
|
|
98492
|
+
const org = ctx.organizations.find((o) => o.id === match.organizationId);
|
|
98493
|
+
if (ws && org) {
|
|
98494
|
+
target = `${org.slug}/${ws.slug}/${match.slug}`;
|
|
98495
|
+
console.log(import_picocolors42.default.dim(`Matched existing project by remote: ${import_picocolors42.default.bold(target)}`));
|
|
98496
|
+
}
|
|
98497
|
+
}
|
|
98461
98498
|
}
|
|
98462
98499
|
if (!target) {
|
|
98463
|
-
|
|
98464
|
-
|
|
98465
|
-
|
|
98466
|
-
|
|
98467
|
-
|
|
98500
|
+
const created = await createProjectFromRepo(session.client, ctx, remote, opts.yes);
|
|
98501
|
+
if (created === "aborted") return 1;
|
|
98502
|
+
if (!created) {
|
|
98503
|
+
console.error(
|
|
98504
|
+
import_picocolors42.default.red("No project to link.") + `
|
|
98505
|
+
Run ${import_picocolors42.default.bold("horus cloud link")} in a TTY to create one, pass ${import_picocolors42.default.bold("--project <org/workspace/project>")}, or create a project at ${import_picocolors42.default.bold("https://cloud.horus.sh")}.`
|
|
98506
|
+
);
|
|
98507
|
+
return 1;
|
|
98508
|
+
}
|
|
98509
|
+
resolved = created;
|
|
98468
98510
|
}
|
|
98469
|
-
const resolved = resolveTriple(ctx, target);
|
|
98470
98511
|
if (!resolved) {
|
|
98471
|
-
|
|
98472
|
-
|
|
98512
|
+
resolved = resolveTriple(ctx, target);
|
|
98513
|
+
if (!resolved) {
|
|
98514
|
+
console.error(
|
|
98515
|
+
import_picocolors42.default.red(`You don't have access to ${import_picocolors42.default.bold(target)}.`) + `
|
|
98473
98516
|
Run ${import_picocolors42.default.bold("horus context list")} to see available projects.`
|
|
98474
|
-
|
|
98475
|
-
|
|
98517
|
+
);
|
|
98518
|
+
return 1;
|
|
98519
|
+
}
|
|
98476
98520
|
}
|
|
98521
|
+
const label = `${resolved.organization.slug}/${resolved.workspace.slug}/${resolved.project.slug}`;
|
|
98477
98522
|
writeCloudConfig(root, {
|
|
98478
98523
|
context: "cloud",
|
|
98479
98524
|
organization: resolved.organization,
|
|
@@ -98481,7 +98526,7 @@ async function runCloudLink(opts = {}) {
|
|
|
98481
98526
|
project: resolved.project
|
|
98482
98527
|
});
|
|
98483
98528
|
console.log(
|
|
98484
|
-
`${import_picocolors42.default.green("\u2713")} Linked. Context for this repo is now ${import_picocolors42.default.bold(
|
|
98529
|
+
`${import_picocolors42.default.green("\u2713")} Linked. Context for this repo is now ${import_picocolors42.default.bold(label)} ${import_picocolors42.default.dim("(cloud)")}.`
|
|
98485
98530
|
);
|
|
98486
98531
|
console.log(
|
|
98487
98532
|
import_picocolors42.default.dim(` Investigations here save to Horus Cloud. ${import_picocolors42.default.bold("horus context use local")} to switch back.`)
|
|
@@ -98622,21 +98667,67 @@ async function runCloudSync(opts = {}) {
|
|
|
98622
98667
|
);
|
|
98623
98668
|
return failed > 0 ? 1 : 0;
|
|
98624
98669
|
}
|
|
98625
|
-
async function
|
|
98626
|
-
if (!process.stdin.isTTY) return
|
|
98627
|
-
|
|
98628
|
-
|
|
98629
|
-
|
|
98630
|
-
|
|
98631
|
-
|
|
98632
|
-
|
|
98633
|
-
|
|
98634
|
-
labels.forEach((label, i) => console.log(` ${i + 1}) ${label}`));
|
|
98670
|
+
async function createProjectFromRepo(client, ctx, remote, yes) {
|
|
98671
|
+
if (!process.stdin.isTTY && !yes) return null;
|
|
98672
|
+
if (ctx.organizations.length === 0) {
|
|
98673
|
+
console.error(
|
|
98674
|
+
import_picocolors42.default.red("You're not a member of any organization yet.") + `
|
|
98675
|
+
Create one at ${import_picocolors42.default.bold("https://cloud.horus.sh")} first.`
|
|
98676
|
+
);
|
|
98677
|
+
return null;
|
|
98678
|
+
}
|
|
98635
98679
|
const rl = (0, import_promises2.createInterface)({ input: process.stdin, output: process.stdout });
|
|
98680
|
+
const ask2 = async (q, def) => {
|
|
98681
|
+
if (yes && def !== void 0) return def;
|
|
98682
|
+
const a = (await rl.question(def ? `${q} (${def}): ` : `${q}: `)).trim();
|
|
98683
|
+
return a || def || "";
|
|
98684
|
+
};
|
|
98636
98685
|
try {
|
|
98637
|
-
|
|
98638
|
-
|
|
98639
|
-
|
|
98686
|
+
let org = ctx.organizations[0];
|
|
98687
|
+
if (ctx.organizations.length > 1) {
|
|
98688
|
+
console.log("Organizations:");
|
|
98689
|
+
ctx.organizations.forEach((o, i) => console.log(` ${i + 1}) ${o.slug}`));
|
|
98690
|
+
const idx = Number(await ask2("Org number", "1")) - 1;
|
|
98691
|
+
org = ctx.organizations[idx] ?? org;
|
|
98692
|
+
}
|
|
98693
|
+
const orgWorkspaces = ctx.workspaces.filter((w) => w.organizationId === org.id);
|
|
98694
|
+
let workspace;
|
|
98695
|
+
const repoName = remote ? parseRepoName(remote.remoteUrl) : "";
|
|
98696
|
+
if (orgWorkspaces.length === 0) {
|
|
98697
|
+
const wsName = await ask2("New workspace name", repoName || "default");
|
|
98698
|
+
const ws = await client.createWorkspace(org.id, { name: wsName });
|
|
98699
|
+
workspace = { id: ws.id, slug: ws.slug };
|
|
98700
|
+
console.log(import_picocolors42.default.dim(`Created workspace ${import_picocolors42.default.bold(ws.slug)}.`));
|
|
98701
|
+
} else {
|
|
98702
|
+
console.log(`Workspaces in ${import_picocolors42.default.bold(org.slug)}:`);
|
|
98703
|
+
orgWorkspaces.forEach((w, i) => console.log(` ${i + 1}) ${w.slug}`));
|
|
98704
|
+
console.log(` ${orgWorkspaces.length + 1}) + create a new workspace`);
|
|
98705
|
+
const pick2 = Number(await ask2("Workspace number", "1"));
|
|
98706
|
+
if (pick2 === orgWorkspaces.length + 1) {
|
|
98707
|
+
const wsName = await ask2("New workspace name", repoName || "default");
|
|
98708
|
+
const ws = await client.createWorkspace(org.id, { name: wsName });
|
|
98709
|
+
workspace = { id: ws.id, slug: ws.slug };
|
|
98710
|
+
console.log(import_picocolors42.default.dim(`Created workspace ${import_picocolors42.default.bold(ws.slug)}.`));
|
|
98711
|
+
} else {
|
|
98712
|
+
const chosen = orgWorkspaces[pick2 - 1] ?? orgWorkspaces[0];
|
|
98713
|
+
workspace = { id: chosen.id, slug: chosen.slug };
|
|
98714
|
+
}
|
|
98715
|
+
}
|
|
98716
|
+
const projName = await ask2("Project name", repoName || "project");
|
|
98717
|
+
const project = await client.createProject(org.id, workspace.id, {
|
|
98718
|
+
name: projName,
|
|
98719
|
+
remoteUrl: remote?.remoteUrl,
|
|
98720
|
+
provider: remote ? inferProvider(remote.remoteUrl) : void 0
|
|
98721
|
+
});
|
|
98722
|
+
console.log(import_picocolors42.default.dim(`Created project ${import_picocolors42.default.bold(project.slug)}.`));
|
|
98723
|
+
return {
|
|
98724
|
+
organization: { id: org.id, slug: org.slug },
|
|
98725
|
+
workspace,
|
|
98726
|
+
project: { id: project.id, slug: project.slug }
|
|
98727
|
+
};
|
|
98728
|
+
} catch (err) {
|
|
98729
|
+
reportCloudError(err);
|
|
98730
|
+
return "aborted";
|
|
98640
98731
|
} finally {
|
|
98641
98732
|
rl.close();
|
|
98642
98733
|
}
|