Open imported company after import
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
f9927bdaaa
commit
b5fde733b0
3 changed files with 63 additions and 2 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import type { CompanyPortabilityPreviewResult } from "@paperclipai/shared";
|
import type { CompanyPortabilityPreviewResult } from "@paperclipai/shared";
|
||||||
import {
|
import {
|
||||||
|
buildCompanyDashboardUrl,
|
||||||
buildDefaultImportAdapterOverrides,
|
buildDefaultImportAdapterOverrides,
|
||||||
buildDefaultImportSelectionState,
|
buildDefaultImportSelectionState,
|
||||||
buildImportSelectionCatalog,
|
buildImportSelectionCatalog,
|
||||||
|
|
@ -101,6 +102,14 @@ describe("resolveCompanyImportApplyConfirmationMode", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("buildCompanyDashboardUrl", () => {
|
||||||
|
it("preserves the configured base path when building a dashboard URL", () => {
|
||||||
|
expect(buildCompanyDashboardUrl("https://paperclip.example/app/", "PAP")).toBe(
|
||||||
|
"https://paperclip.example/app/PAP/dashboard",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("renderCompanyImportPreview", () => {
|
describe("renderCompanyImportPreview", () => {
|
||||||
it("summarizes the preview with counts, selection info, and truncated examples", () => {
|
it("summarizes the preview with counts, selection info, and truncated examples", () => {
|
||||||
const preview: CompanyPortabilityPreviewResult = {
|
const preview: CompanyPortabilityPreviewResult = {
|
||||||
|
|
@ -155,6 +164,10 @@ describe("renderCompanyImportPreview", () => {
|
||||||
logoPath: null,
|
logoPath: null,
|
||||||
requireBoardApprovalForNewAgents: false,
|
requireBoardApprovalForNewAgents: false,
|
||||||
},
|
},
|
||||||
|
sidebar: {
|
||||||
|
agents: ["ceo"],
|
||||||
|
projects: ["alpha"],
|
||||||
|
},
|
||||||
agents: [
|
agents: [
|
||||||
{
|
{
|
||||||
slug: "ceo",
|
slug: "ceo",
|
||||||
|
|
@ -291,16 +304,19 @@ describe("renderCompanyImportResult", () => {
|
||||||
{ slug: "cto", id: "agent-2", action: "updated", name: "CTO", reason: "replace strategy" },
|
{ slug: "cto", id: "agent-2", action: "updated", name: "CTO", reason: "replace strategy" },
|
||||||
{ slug: "ops", id: null, action: "skipped", name: "Ops", reason: "skip strategy" },
|
{ slug: "ops", id: null, action: "skipped", name: "Ops", reason: "skip strategy" },
|
||||||
],
|
],
|
||||||
|
projects: [],
|
||||||
envInputs: [],
|
envInputs: [],
|
||||||
warnings: ["Review API keys"],
|
warnings: ["Review API keys"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
targetLabel: "Imported Co (company-123)",
|
targetLabel: "Imported Co (company-123)",
|
||||||
|
companyUrl: "https://paperclip.example/PAP/dashboard",
|
||||||
infoMessages: ["Using claude-local adapter"],
|
infoMessages: ["Using claude-local adapter"],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(rendered).toContain("Company");
|
expect(rendered).toContain("Company");
|
||||||
|
expect(rendered).toContain("https://paperclip.example/PAP/dashboard");
|
||||||
expect(rendered).toContain("3 agents total (1 created, 1 updated, 1 skipped)");
|
expect(rendered).toContain("3 agents total (1 created, 1 updated, 1 skipped)");
|
||||||
expect(rendered).toContain("Agent results");
|
expect(rendered).toContain("Agent results");
|
||||||
expect(rendered).toContain("Using claude-local adapter");
|
expect(rendered).toContain("Using claude-local adapter");
|
||||||
|
|
@ -350,6 +366,10 @@ describe("import selection catalog", () => {
|
||||||
logoPath: "images/company-logo.png",
|
logoPath: "images/company-logo.png",
|
||||||
requireBoardApprovalForNewAgents: false,
|
requireBoardApprovalForNewAgents: false,
|
||||||
},
|
},
|
||||||
|
sidebar: {
|
||||||
|
agents: ["ceo"],
|
||||||
|
projects: ["alpha"],
|
||||||
|
},
|
||||||
agents: [
|
agents: [
|
||||||
{
|
{
|
||||||
slug: "ceo",
|
slug: "ceo",
|
||||||
|
|
@ -504,6 +524,7 @@ describe("default adapter overrides", () => {
|
||||||
skills: false,
|
skills: false,
|
||||||
},
|
},
|
||||||
company: null,
|
company: null,
|
||||||
|
sidebar: null,
|
||||||
agents: [
|
agents: [
|
||||||
{
|
{
|
||||||
slug: "legacy-agent",
|
slug: "legacy-agent",
|
||||||
|
|
|
||||||
|
|
@ -169,7 +169,7 @@ async function requestJson<T>(url: string, init?: RequestInit): Promise<T> {
|
||||||
return response.json() as Promise<T>;
|
return response.json() as Promise<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function openUrl(url: string): boolean {
|
export function openUrl(url: string): boolean {
|
||||||
const platform = process.platform;
|
const platform = process.platform;
|
||||||
try {
|
try {
|
||||||
if (platform === "darwin") {
|
if (platform === "darwin") {
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import type {
|
||||||
CompanyPortabilityImportResult,
|
CompanyPortabilityImportResult,
|
||||||
} from "@paperclipai/shared";
|
} from "@paperclipai/shared";
|
||||||
import { ApiRequestError } from "../../client/http.js";
|
import { ApiRequestError } from "../../client/http.js";
|
||||||
|
import { openUrl } from "../../client/board-auth.js";
|
||||||
import { readZipArchive } from "./zip.js";
|
import { readZipArchive } from "./zip.js";
|
||||||
import {
|
import {
|
||||||
addCommonClientOptions,
|
addCommonClientOptions,
|
||||||
|
|
@ -654,7 +655,7 @@ export function renderCompanyImportPreview(
|
||||||
|
|
||||||
export function renderCompanyImportResult(
|
export function renderCompanyImportResult(
|
||||||
result: CompanyPortabilityImportResult,
|
result: CompanyPortabilityImportResult,
|
||||||
meta: { targetLabel: string; infoMessages?: string[] },
|
meta: { targetLabel: string; companyUrl?: string; infoMessages?: string[] },
|
||||||
): string {
|
): string {
|
||||||
const lines: string[] = [
|
const lines: string[] = [
|
||||||
`${pc.bold("Target")} ${meta.targetLabel}`,
|
`${pc.bold("Target")} ${meta.targetLabel}`,
|
||||||
|
|
@ -662,6 +663,10 @@ export function renderCompanyImportResult(
|
||||||
`${pc.bold("Agents")} ${summarizeImportAgentResults(result.agents)}`,
|
`${pc.bold("Agents")} ${summarizeImportAgentResults(result.agents)}`,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (meta.companyUrl) {
|
||||||
|
lines.splice(1, 0, `${pc.bold("URL")} ${meta.companyUrl}`);
|
||||||
|
}
|
||||||
|
|
||||||
appendPreviewExamples(
|
appendPreviewExamples(
|
||||||
lines,
|
lines,
|
||||||
"Agent results",
|
"Agent results",
|
||||||
|
|
@ -713,6 +718,15 @@ export function resolveCompanyImportApiPath(input: {
|
||||||
return input.dryRun ? "/api/companies/import/preview" : "/api/companies/import";
|
return input.dryRun ? "/api/companies/import/preview" : "/api/companies/import";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function buildCompanyDashboardUrl(apiBase: string, issuePrefix: string): string {
|
||||||
|
const url = new URL(apiBase);
|
||||||
|
const normalizedPrefix = issuePrefix.trim().replace(/^\/+|\/+$/g, "");
|
||||||
|
url.pathname = `${url.pathname.replace(/\/+$/, "")}/${normalizedPrefix}/dashboard`;
|
||||||
|
url.search = "";
|
||||||
|
url.hash = "";
|
||||||
|
return url.toString();
|
||||||
|
}
|
||||||
|
|
||||||
export function resolveCompanyImportApplyConfirmationMode(input: {
|
export function resolveCompanyImportApplyConfirmationMode(input: {
|
||||||
yes?: boolean;
|
yes?: boolean;
|
||||||
interactive: boolean;
|
interactive: boolean;
|
||||||
|
|
@ -1298,6 +1312,18 @@ export function registerCompanyCommands(program: Command): void {
|
||||||
if (!imported) {
|
if (!imported) {
|
||||||
throw new Error("Import request returned no data.");
|
throw new Error("Import request returned no data.");
|
||||||
}
|
}
|
||||||
|
let companyUrl: string | undefined;
|
||||||
|
if (!ctx.json) {
|
||||||
|
try {
|
||||||
|
const importedCompany = await ctx.api.get<Company>(`/api/companies/${imported.company.id}`);
|
||||||
|
const issuePrefix = importedCompany?.issuePrefix?.trim();
|
||||||
|
if (issuePrefix) {
|
||||||
|
companyUrl = buildCompanyDashboardUrl(ctx.api.apiBase, issuePrefix);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
companyUrl = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (ctx.json) {
|
if (ctx.json) {
|
||||||
printOutput(imported, { json: true });
|
printOutput(imported, { json: true });
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1305,10 +1331,24 @@ export function registerCompanyCommands(program: Command): void {
|
||||||
"Import Result",
|
"Import Result",
|
||||||
renderCompanyImportResult(imported, {
|
renderCompanyImportResult(imported, {
|
||||||
targetLabel,
|
targetLabel,
|
||||||
|
companyUrl,
|
||||||
infoMessages: adapterMessages,
|
infoMessages: adapterMessages,
|
||||||
}),
|
}),
|
||||||
{ interactive: interactiveView },
|
{ interactive: interactiveView },
|
||||||
);
|
);
|
||||||
|
if (interactiveView && companyUrl) {
|
||||||
|
const openImportedCompany = await p.confirm({
|
||||||
|
message: "Open the imported company in your browser?",
|
||||||
|
initialValue: true,
|
||||||
|
});
|
||||||
|
if (!p.isCancel(openImportedCompany) && openImportedCompany) {
|
||||||
|
if (openUrl(companyUrl)) {
|
||||||
|
p.log.info(`Opened ${companyUrl}`);
|
||||||
|
} else {
|
||||||
|
p.log.warn(`Could not open your browser automatically. Open this URL manually:\n${companyUrl}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
handleCommandError(err);
|
handleCommandError(err);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue