Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"@formatjs/intl-segmenter": "^12.1.0",
"@inkjs/ui": "^1.0.0",
"@inquirer/prompts": "^8.2.0",
"@sfcompute/nodes-sdk-alpha": "0.1.0-alpha.27",
"@sfcompute/nodes-sdk-alpha": "0.1.0-alpha.31",
"@types/ms": "^0.7.34",
"async-retry": "^1.3.3",
"axios": "^1.8.4",
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ async function main() {

if (!exchangeAccountId) {
const client = await apiClient(config.auth_token);
const { data } = await client.GET("/v0/me");
const { data } = await client.GET("/v1/account/me", {});
if (data?.id) {
exchangeAccountId = data.id;
saveConfig({ ...config, account_id: data.id });
Expand Down
2 changes: 1 addition & 1 deletion src/lib/contracts/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ async function listContracts(
if (!response.ok) {
switch (response.status) {
case 400:
return logAndQuit(`Bad Request: ${error?.message}`);
return logAndQuit(`Bad Request: ${error?.error?.message}`);
case 401:
return await logSessionTokenExpiredAndQuit();
default:
Expand Down
89 changes: 42 additions & 47 deletions src/lib/images/get.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,24 @@ import Link from "ink-link";
import { apiClient } from "../../apiClient.ts";
import { logAndQuit } from "../../helpers/errors.ts";
import { formatDate } from "../../helpers/format-time.ts";
import type { components } from "../../schema.ts";
import { Row } from "../Row.tsx";

dayjs.extend(utc);
dayjs.extend(advanced);
dayjs.extend(timezone);

type Image = components["schemas"]["sfc-api_ImageListEntry"];
type Download = components["schemas"]["sfc-api_ImageDownloadResponse"];

function ImageDisplay({
image,
download,
}: {
image: {
name: string;
id: string;
upload_status: string;
sha256_hash: string | null;
};
download: { url: string; expires_at: number } | null;
image: Image;
download: Download | null;
}) {
const expiresAt = download?.expires_at
? new Date(download.expires_at * 1000)
: null;
const expiresAt = download ? new Date(download.expires_at * 1000) : null;
const isExpired = expiresAt ? expiresAt < new Date() : false;

return (
Expand All @@ -43,7 +40,7 @@ function ImageDisplay({

<Box paddingX={1} flexDirection="column">
<Row head="Status: " value={formatStatusInk(image.upload_status)} />
{image.sha256_hash && <Row head="SHA256: " value={image.sha256_hash} />}
{image.sha256 && <Row head="SHA256: " value={image.sha256} />}
{download && (
<>
<Row
Expand Down Expand Up @@ -92,50 +89,48 @@ function formatStatusInk(status: string): React.ReactElement {
return <Text color="cyan">Completed</Text>;
case "failed":
return <Text color="red">Failed</Text>;
case "revoked":
return <Text color="red">Revoked</Text>;
default:
return <Text dimColor>Unknown</Text>;
}
}

const get = new Command("get")
.description("Get image details and download URL")
.argument("<id>", "Image ID or name")
.option("--json", "Output JSON")
.action(async (id, opts) => {
const client = await apiClient();
export function createGet() {
return new Command("get")
.alias("show")
.description("Get image details and download URL")
.argument("<id>", "Image ID or name")
.option("--json", "Output JSON")
.action(async (id, opts) => {
const client = await apiClient();

const { data: image, response } = await client.GET("/v2/images/{id}", {
params: { path: { id } },
});
if (!response.ok || !image) {
logAndQuit(
`Failed to get image: ${response.status} ${response.statusText}`,
const { data: image, response } = await client.GET(
"/preview/v2/images/{id}",
{ params: { path: { id } } },
);
}

// Fetch download URL if image is completed
let download = null;
if (image.upload_status === "completed") {
const { data: downloadData, response: downloadResponse } =
await client.GET("/v2/images/{id}/download", {
params: { path: { id } },
});
if (downloadResponse.ok && downloadData) {
download = downloadData;
if (!response.ok || !image) {
logAndQuit(
`Failed to get image: ${response.status} ${response.statusText}`,
);
}
}

if (opts.json) {
console.log(JSON.stringify({ ...image, download }, null, 2));
return;
}
let download: Download | null = null;
if (image.upload_status === "completed") {
const { data: downloadData } = await client.GET(
"/preview/v2/images/{id}/download",
{ params: { path: { id } } },
);
if (downloadData) {
download = downloadData;
}
}

render(
<ImageDisplay
image={{ ...image, sha256_hash: image.sha256_hash ?? null }}
download={download}
/>,
);
});
if (opts.json) {
console.log(JSON.stringify({ ...image, download }, null, 2));
return;
}

export default get;
render(<ImageDisplay image={image} download={download} />);
});
}
25 changes: 15 additions & 10 deletions src/lib/images/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type { Command } from "@commander-js/extra-typings";
import get from "./get.tsx";
import list from "./list.ts";
import upload from "./upload.ts";
import { Command } from "@commander-js/extra-typings";
import { createGet } from "./get.tsx";
import { createList } from "./list.ts";
import { createUpload } from "./upload.ts";

export function registerImages(program: Command) {
const images = program
.command("images")
export function createImagesCommand() {
const images = new Command("images")
.alias("image")
Comment thread
indent[bot] marked this conversation as resolved.
.alias("os")
.description("Manage images")
.showHelpAfterError()
.addHelpText(
Expand All @@ -23,10 +23,15 @@ Examples:\n
$ sf images get <image-id>
`,
)
.addCommand(list)
.addCommand(upload)
.addCommand(get)
.addCommand(createList())
.addCommand(createUpload())
.addCommand(createGet())
.action(() => {
images.help();
});
return images;
}

export function registerImages(program: Command) {
program.addCommand(createImagesCommand());
}
Loading
Loading