Skip to content

Commit 677a153

Browse files
ruibabyCopilot
andcommitted
Check PluginMoments plugin installation before running moment subcommands
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent c632136 commit 677a153

File tree

2 files changed

+45
-6
lines changed

2 files changed

+45
-6
lines changed

src/commands/moment/__test__/moment-entry.spec.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,22 @@ test("tryRunMomentCommand dispatches list subcommands", async () => {
2929
total: 0,
3030
},
3131
});
32+
const getPlugin = vi.fn().mockResolvedValue({
33+
data: { spec: { enabled: true } },
34+
});
3235
const runtimeMock = {
3336
getClientsForOptions: vi.fn().mockResolvedValue({
3437
clients: {
3538
axios: {
3639
get,
3740
},
41+
core: {
42+
plugin: {
43+
plugin: {
44+
getPlugin,
45+
},
46+
},
47+
},
3848
},
3949
}),
4050
};

src/commands/moment/index.ts

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { input } from "@inquirer/prompts";
2+
import axios from "axios";
23
import cac, { type CAC } from "cac";
34

45
import { tryRunCommandCliRoute } from "../../utils/command-router.js";
@@ -11,10 +12,11 @@ import {
1112
parseNumberOption,
1213
} from "../../utils/options.js";
1314
import { printJson } from "../../utils/output.js";
14-
import { RuntimeContext } from "../../utils/runtime.js";
15+
import { type HaloClients, RuntimeContext } from "../../utils/runtime.js";
1516
import { printMoment, printMomentList } from "./format.js";
1617
import type { ListedMomentList, Moment, MomentVisible } from "./types.js";
1718

19+
const MOMENTS_PLUGIN_NAME = "PluginMoments";
1820
const MOMENT_API_VERSION = "moment.halo.run/v1alpha1";
1921
const MOMENT_KIND = "Moment";
2022
const MOMENT_API_BASE = "/apis/uc.api.moment.halo.run/v1alpha1/moments";
@@ -46,6 +48,27 @@ interface MomentDeleteOptions extends MomentCommandOptions {
4648
force?: boolean;
4749
}
4850

51+
async function ensureMomentsPluginInstalled(clients: HaloClients): Promise<void> {
52+
try {
53+
const response = await clients.core.plugin.plugin.getPlugin({ name: MOMENTS_PLUGIN_NAME });
54+
if (!response.data.spec.enabled) {
55+
throw new CliError(
56+
`The ${MOMENTS_PLUGIN_NAME} plugin is installed but not enabled. Enable it with: halo plugin enable ${MOMENTS_PLUGIN_NAME}`,
57+
);
58+
}
59+
} catch (error) {
60+
if (error instanceof CliError) {
61+
throw error;
62+
}
63+
if (axios.isAxiosError(error) && error.response?.status === 404) {
64+
throw new CliError(
65+
`The ${MOMENTS_PLUGIN_NAME} plugin is not installed. Install it from the App Store or via: halo plugin install`,
66+
);
67+
}
68+
throw error;
69+
}
70+
}
71+
4972
async function resolveMomentContent(content?: string): Promise<string | undefined> {
5073
return content;
5174
}
@@ -142,6 +165,12 @@ export function buildMomentPayload(
142165
async function buildMomentCli(runtime: RuntimeContext): Promise<CAC> {
143166
const momentCli = cac("halo moment");
144167

168+
async function getCheckedClients(options: MomentCommandOptions) {
169+
const result = await runtime.getClientsForOptions(options);
170+
await ensureMomentsPluginInstalled(result.clients);
171+
return result;
172+
}
173+
145174
momentCli
146175
.command("list", "List moments")
147176
.option("--profile <name>", "Halo profile name")
@@ -153,7 +182,7 @@ async function buildMomentCli(runtime: RuntimeContext): Promise<CAC> {
153182
.option("--visible <state>", "Filter by visibility: PUBLIC or PRIVATE")
154183
.option("--approved <boolean>", "Filter by approval state")
155184
.action(async (options: MomentListOptions) => {
156-
const { clients } = await runtime.getClientsForOptions(options);
185+
const { clients } = await getCheckedClients(options);
157186
const response = await clients.axios.get<ListedMomentList>(MOMENT_API_BASE, {
158187
params: {
159188
page: parseNumberOption(options.page),
@@ -173,7 +202,7 @@ async function buildMomentCli(runtime: RuntimeContext): Promise<CAC> {
173202
.option("--profile <name>", "Halo profile name")
174203
.option("--json", "Output JSON")
175204
.action(async (name: string, options: MomentCommandOptions) => {
176-
const { clients } = await runtime.getClientsForOptions(options);
205+
const { clients } = await getCheckedClients(options);
177206
const response = await clients.axios.get<Moment>(
178207
`${MOMENT_API_BASE}/${encodeURIComponent(name)}`,
179208
);
@@ -191,7 +220,7 @@ async function buildMomentCli(runtime: RuntimeContext): Promise<CAC> {
191220
.option("--release-time <datetime>", "Release time in ISO-8601 format")
192221
.option("--approved <boolean>", "Initial approval state")
193222
.action(async (options: MomentMutationOptions) => {
194-
const { clients } = await runtime.getClientsForOptions(options);
223+
const { clients } = await getCheckedClients(options);
195224
const resolvedContent = await resolveMomentContent(options.content);
196225
const content = (await promptForMomentContent(resolvedContent?.trim(), "create"))?.trim();
197226

@@ -216,7 +245,7 @@ async function buildMomentCli(runtime: RuntimeContext): Promise<CAC> {
216245
.option("--release-time <datetime>", "Release time in ISO-8601 format")
217246
.option("--approved <boolean>", "Approval state")
218247
.action(async (name: string, options: MomentMutationOptions) => {
219-
const { clients } = await runtime.getClientsForOptions(options);
248+
const { clients } = await getCheckedClients(options);
220249
const existingResponse = await clients.axios.get<Moment>(
221250
`${MOMENT_API_BASE}/${encodeURIComponent(name)}`,
222251
);
@@ -260,7 +289,7 @@ async function buildMomentCli(runtime: RuntimeContext): Promise<CAC> {
260289
.option("--json", "Output JSON")
261290
.option("--force", "Delete without confirmation")
262291
.action(async (name: string, options: MomentDeleteOptions) => {
263-
const { clients } = await runtime.getClientsForOptions(options);
292+
const { clients } = await getCheckedClients(options);
264293

265294
if (
266295
!(await confirmDangerousAction(

0 commit comments

Comments
 (0)