Skip to content

Commit 81109ce

Browse files
fix: align call sites with regenerated schema and restore image-command parity
CI's `bun run check` regressed after the openapi-typescript regen in e35a88c because: - `/v0/me` moved to `/v1/account/me` (with `getAccountMe` having a 2-arg call signature). Updated `src/index.ts`, `src/lib/posthog.ts`, and `src/lib/images/utils.ts` accordingly. - `/v2/feature_flags/{feature_flag_id}` is no longer in the regenerated schema (only the admin variant is); switched the cli's PostHog flag check to a raw fetch (the route is still served externally per haproxy.conf). - Non-OK response envelopes are now `{ error: { type, message } }` (nested); updated `error?.message` reads in contracts, scale/{create,list,update,utils} to `error?.error?.message`. Also restores three regressions reported by the AI reviewer on the shared image-command factory: - Re-adds the `os` alias on `sf nodes images` and `sf vm images` so scripts using `sf nodes os list` / `sf vm os list` keep working. - `sf nodes images list --json` / `sf vm images list --json` print the bare image array again (new `sf images list --json` keeps the envelope shape); controlled by a new `legacyJsonShape` factory option. - Help text, "next steps", and overflow messages now reference the actual parent invocation path (`sf nodes images …` / `sf vm images …`) via a new `parentPath` factory option.
1 parent 718004e commit 81109ce

14 files changed

Lines changed: 89 additions & 35 deletions

File tree

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ async function main() {
8989

9090
if (!exchangeAccountId) {
9191
const client = await apiClient(config.auth_token);
92-
const { data } = await client.GET("/v0/me");
92+
const { data } = await client.GET("/v1/account/me", {});
9393
if (data?.id) {
9494
exchangeAccountId = data.id;
9595
saveConfig({ ...config, account_id: data.id });

src/lib/contracts/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ async function listContracts(
7878
if (!response.ok) {
7979
switch (response.status) {
8080
case 400:
81-
return logAndQuit(`Bad Request: ${error?.message}`);
81+
return logAndQuit(`Bad Request: ${error?.error?.message}`);
8282
case 401:
8383
return await logSessionTokenExpiredAndQuit();
8484
default:

src/lib/images/get.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { logAndQuit } from "../../helpers/errors.ts";
1212
import { formatDate } from "../../helpers/format-time.ts";
1313
import type { components } from "../../schema.ts";
1414
import { Row } from "../Row.tsx";
15+
import type { CreateImagesOptions } from "./index.ts";
1516

1617
dayjs.extend(utc);
1718
dayjs.extend(advanced);
@@ -96,7 +97,7 @@ function formatStatusInk(status: string): React.ReactElement {
9697
}
9798
}
9899

99-
export function createGet() {
100+
export function createGet(_opts: CreateImagesOptions = {}) {
100101
return new Command("get")
101102
.alias("show")
102103
.description("Get image details and download URL")

src/lib/images/index.ts

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,25 @@ import { createGet } from "./get.tsx";
33
import { createList } from "./list.ts";
44
import { createUpload } from "./upload.ts";
55

6-
export function createImagesCommand() {
6+
export interface CreateImagesOptions {
7+
/**
8+
* Path users invoke this command under, used in help text and "next steps"
9+
* suggestions (e.g. `"sf images"`, `"sf nodes images"`, `"sf vm images"`).
10+
* Defaults to `"sf images"`.
11+
*/
12+
parentPath?: string;
13+
/**
14+
* When true, `list --json` prints just the bare image array and `get --json`
15+
* prints just the bare image object. Matches the pre-shared-factory output
16+
* shape of `sf nodes images` / `sf vm images` so existing scripts piping
17+
* `... --json | jq` keep working. Defaults to `false` (envelope shape).
18+
*/
19+
legacyJsonShape?: boolean;
20+
}
21+
22+
export function createImagesCommand(opts: CreateImagesOptions = {}) {
23+
const { parentPath = "sf images", legacyJsonShape = false } = opts;
24+
const subOpts = { parentPath, legacyJsonShape };
725
const images = new Command("images")
826
.alias("image")
927
.description("Manage images")
@@ -13,18 +31,18 @@ export function createImagesCommand() {
1331
`
1432
Examples:\n
1533
\x1b[2m# Upload an image file\x1b[0m
16-
$ sf images upload -f ./my-image.raw -n my-image
34+
$ ${parentPath} upload -f ./my-image.raw -n my-image
1735
1836
\x1b[2m# List all images\x1b[0m
19-
$ sf images list
37+
$ ${parentPath} list
2038
2139
\x1b[2m# Get image details and download URL\x1b[0m
22-
$ sf images get <image-id>
40+
$ ${parentPath} get <image-id>
2341
`,
2442
)
25-
.addCommand(createList())
26-
.addCommand(createUpload())
27-
.addCommand(createGet())
43+
.addCommand(createList(subOpts))
44+
.addCommand(createUpload(subOpts))
45+
.addCommand(createGet(subOpts))
2846
.action(() => {
2947
images.help();
3048
});

src/lib/images/list.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ import ora from "ora";
66
import { apiClient } from "../../apiClient.ts";
77
import { logAndQuit } from "../../helpers/errors.ts";
88
import { formatDate } from "../../helpers/format-time.ts";
9+
import type { CreateImagesOptions } from "./index.ts";
910
import { getDefaultWorkspace } from "./utils.ts";
1011

11-
export function createList() {
12+
export function createList(opts: CreateImagesOptions = {}) {
13+
const { parentPath = "sf images", legacyJsonShape = false } = opts;
1214
return new Command("list")
1315
.alias("ls")
1416
.description("List images")
@@ -19,13 +21,13 @@ export function createList() {
1921
`
2022
Examples:\n
2123
\x1b[2m# List all images\x1b[0m
22-
$ sf images list
24+
$ ${parentPath} list
2325
2426
\x1b[2m# Get detailed info for a specific image\x1b[0m
25-
$ sf images get <image-id>
27+
$ ${parentPath} get <image-id>
2628
2729
\x1b[2m# List images in JSON format\x1b[0m
28-
$ sf images list --json
30+
$ ${parentPath} list --json
2931
`,
3032
)
3133
.action(async (options) => {
@@ -45,7 +47,13 @@ Examples:\n
4547
}
4648

4749
if (options.json) {
48-
console.log(JSON.stringify(data, null, 2));
50+
// legacyJsonShape: print just the bare array so existing scripts
51+
// piping `... --json | jq '.[].id'` keep working under `sf nodes
52+
// images list` / `sf vm images list`. Default (envelope) is the
53+
// shape preferred for the new top-level `sf images list`.
54+
console.log(
55+
JSON.stringify(legacyJsonShape ? data.data : data, null, 2),
56+
);
4957
return;
5058
}
5159

@@ -54,7 +62,7 @@ Examples:\n
5462
if (images.length === 0) {
5563
console.log("No images found.");
5664
console.log(chalk.gray("\nUpload your first image:"));
57-
console.log(" sf images upload -f ./my-image.img -n my-image");
65+
console.log(` ${parentPath} upload -f ./my-image.img -n my-image`);
5866
return;
5967
}
6068

@@ -92,7 +100,7 @@ Examples:\n
92100
content: chalk.blackBright(
93101
`${images.length - 5} older ${
94102
images.length - 5 === 1 ? "image" : "images"
95-
} not shown. Use sf images list --json to list all images.`,
103+
} not shown. Use ${parentPath} list --json to list all images.`,
96104
),
97105
},
98106
]);
@@ -103,7 +111,7 @@ Examples:\n
103111
console.log(chalk.gray("\nNext steps:"));
104112
const firstImage = sortedImages[0];
105113
if (firstImage) {
106-
console.log(` sf images get ${chalk.cyan(firstImage.id)}`);
114+
console.log(` ${parentPath} get ${chalk.cyan(firstImage.id)}`);
107115
}
108116
const firstCompletedImage = sortedImages.find(
109117
(image) => image.upload_status === "completed",

src/lib/images/upload.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import cliSpinners from "cli-spinners";
1212
import ora, { type Ora } from "ora";
1313
import { apiClient } from "../../apiClient.ts";
1414
import { logAndQuit } from "../../helpers/errors.ts";
15+
import type { CreateImagesOptions } from "./index.ts";
1516
import { getDefaultWorkspace } from "./utils.ts";
1617

1718
async function readChunk(
@@ -53,7 +54,7 @@ async function readChunk(
5354
}
5455
}
5556

56-
export function createUpload() {
57+
export function createUpload(_opts: CreateImagesOptions = {}) {
5758
return new Command("upload")
5859
.description("Upload an image file (multipart)")
5960
.requiredOption("-f, --file <file>", "Path to the image file")

src/lib/images/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export async function getDefaultWorkspace(): Promise<string> {
66
let accountId = config.account_id;
77
if (!accountId) {
88
const client = await apiClient();
9-
const { data } = await client.GET("/v0/me");
9+
const { data } = await client.GET("/v1/account/me", {});
1010
if (data?.id) {
1111
await saveConfig({ ...config, account_id: data.id });
1212
accountId = data.id;

src/lib/nodes/image/index.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
11
import { createImagesCommand } from "../../images/index.ts";
22

3-
export default createImagesCommand;
3+
/**
4+
* Returns the shared images command configured for use under `sf nodes`:
5+
* - `os` alias is preserved (original `sf nodes images` had `os` and `image`).
6+
* - Help text examples reference `sf nodes images …`.
7+
* - `list --json` outputs the bare image array (matching pre-shared-factory
8+
* behavior so scripts piping `... --json | jq '.[].id'` keep working).
9+
*/
10+
export default function createNodesImagesCommand() {
11+
return createImagesCommand({
12+
parentPath: "sf nodes images",
13+
legacyJsonShape: true,
14+
}).alias("os");
15+
}

src/lib/posthog.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ const trackEvent = ({
4040

4141
if (!exchangeAccountId) {
4242
const client = await apiClient(config.auth_token);
43-
const { data } = await client.GET("/v0/me");
43+
const { data } = await client.GET("/v1/account/me", {});
4444
if (data?.id) {
4545
exchangeAccountId = data.id;
4646
await saveConfig({ ...config, account_id: data.id });
@@ -81,16 +81,20 @@ export const isFeatureEnabled = async (feature: FeatureFlags) => {
8181
return cachedFlag.value;
8282
}
8383

84-
// Fetch from the v2/feature_flags API
84+
// Fetch from the v2/feature_flags API. Uses raw fetch because the route is
85+
// not exposed on the regenerated `src/schema.ts` (only the admin variant is),
86+
// but it is still served externally per the HAProxy config.
8587
let finalResult = false;
8688
try {
87-
const client = await apiClient(config.auth_token);
88-
const { data, response } = await client.GET(
89-
"/v2/feature_flags/{feature_flag_id}",
90-
{ params: { path: { feature_flag_id: feature } } },
89+
const response = await fetch(
90+
`${config.api_url}/v2/feature_flags/${encodeURIComponent(feature)}`,
91+
{ headers: { Authorization: `Bearer ${config.auth_token}` } },
9192
);
93+
if (response.ok) {
94+
const data = (await response.json()) as { enabled?: boolean };
95+
finalResult = data?.enabled ?? false;
96+
}
9297
// 404 means the flag doesn't exist → treat as disabled (false)
93-
finalResult = response.ok ? (data?.enabled ?? false) : false;
9498
} catch {
9599
// Network or parse error → default to false
96100
}

src/lib/scale/create.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,9 @@ function useCreateProcurement() {
101101
},
102102
);
103103
if (!response.ok) {
104-
throw new Error(error?.message || "Failed to create procurement");
104+
throw new Error(
105+
error?.error?.message || "Failed to create procurement",
106+
);
105107
}
106108
setResult(data);
107109
} catch (err: unknown) {

0 commit comments

Comments
 (0)