mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-28 14:15:24 +00:00
feat: finish the fleets plugin, add ctx.hosts and host-commands SDK (B4)
This commit is contained in:
1 parent
d2ff5fc78f
commit
3b2c849514
59 files changed
+31807
-2977
No files matched your search
@@ -52,6 +52,60 @@ export async function getNetworkTopology(
|
||||
}
|
||||
}
|
||||
|
||||
/** Another plugin's fleets, as ctx.services.get("fleets.access") returns them. */
|
||||
interface FleetsAccess {
|
||||
list: () => Promise<
|
||||
Array<{ id: number; name: string; color: string | null }>
|
||||
>;
|
||||
create: (input: {
|
||||
name: string;
|
||||
description?: string | null;
|
||||
}) => Promise<{ id: number; name: string }>;
|
||||
addMember: (fleetId: number, hostId: number) => Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* The user's fleets, or null when the fleets plugin is off or the user may
|
||||
* not use it. Optional: the list_fleets tool disappears without it.
|
||||
*/
|
||||
export async function listFleets(
|
||||
userId: string,
|
||||
): Promise<Array<{ id: number; name: string; color: string | null }> | null> {
|
||||
if (!current) return null;
|
||||
try {
|
||||
return await current.get<FleetsAccess>("fleets.access", { userId }).list();
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Requires the fleets plugin, unlike listFleets above: a proposal apply must
|
||||
* surface a failure as an error the user sees, not silently no-op.
|
||||
*/
|
||||
function requireFleetsAccess(userId: string): FleetsAccess {
|
||||
if (!current) {
|
||||
throw new Error("The fleets plugin is disabled");
|
||||
}
|
||||
return current.get<FleetsAccess>("fleets.access", { userId });
|
||||
}
|
||||
|
||||
export async function createFleet(
|
||||
userId: string,
|
||||
input: { name: string; description?: string | null },
|
||||
): Promise<{ id: number; name: string } | null> {
|
||||
if (!current) return null;
|
||||
return requireFleetsAccess(userId).create(input);
|
||||
}
|
||||
|
||||
export async function addFleetMember(
|
||||
userId: string,
|
||||
fleetId: number,
|
||||
hostId: number,
|
||||
): Promise<void> {
|
||||
await requireFleetsAccess(userId).addMember(fleetId, hostId);
|
||||
}
|
||||
|
||||
export interface SnippetSummary {
|
||||
id: number;
|
||||
name: string;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import {
|
||||
createCurrentAutomationRepository,
|
||||
createCurrentFleetRepository,
|
||||
createCurrentHostRepository,
|
||||
} from "../../../../../src/backend/database/repositories/factory.js";
|
||||
import { validateDefinition } from "../../../../automations/src/backend/routes.js";
|
||||
@@ -13,6 +12,8 @@ import {
|
||||
updateSnippet,
|
||||
deleteSnippet,
|
||||
getSnippet,
|
||||
createFleet,
|
||||
addFleetMember,
|
||||
} from "../services.js";
|
||||
|
||||
/** Approved commands get a bounded window rather than hanging the request. */
|
||||
@@ -175,10 +176,11 @@ export async function applyProposal(
|
||||
}
|
||||
|
||||
case "propose_create_fleet": {
|
||||
const fleet = await createCurrentFleetRepository().create(userId, {
|
||||
const fleet = await createFleet(userId, {
|
||||
name: requireString(payload.name, "name"),
|
||||
description: optionalString(payload.description),
|
||||
} as any);
|
||||
});
|
||||
if (!fleet) throw new Error("The fleets plugin is disabled");
|
||||
|
||||
const hostIds = Array.isArray(payload.hostIds) ? payload.hostIds : [];
|
||||
let added = 0;
|
||||
@@ -191,16 +193,13 @@ export async function applyProposal(
|
||||
hostId,
|
||||
);
|
||||
if (!host) continue;
|
||||
await createCurrentFleetRepository().addMember(
|
||||
(fleet as any).id,
|
||||
hostId,
|
||||
);
|
||||
await addFleetMember(userId, fleet.id, hostId);
|
||||
added += 1;
|
||||
}
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
summary: `Created fleet ${(fleet as any).name} with ${added} host${added === 1 ? "" : "s"}`,
|
||||
summary: `Created fleet ${fleet.name} with ${added} host${added === 1 ? "" : "s"}`,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import {
|
||||
createCurrentNotificationChannelRepository,
|
||||
createCurrentAutomationRepository,
|
||||
createCurrentCommandHistoryRepository,
|
||||
createCurrentFleetRepository,
|
||||
createCurrentHomepageItemRepository,
|
||||
createCurrentHostRepository,
|
||||
} from "../../../../../src/backend/database/repositories/factory.js";
|
||||
@@ -11,6 +10,7 @@ import {
|
||||
listSavedWorkspaces,
|
||||
getNetworkTopology,
|
||||
listSnippets,
|
||||
listFleets,
|
||||
} from "../services.js";
|
||||
|
||||
/**
|
||||
@@ -87,18 +87,16 @@ export const readTools: AiTool[] = [
|
||||
{
|
||||
name: "list_fleets",
|
||||
description:
|
||||
"List the user's fleets. Fleets group hosts for bulk operations and inventory.",
|
||||
"List the user's fleets. Fleets group hosts for bulk operations and inventory. Unavailable if the fleets plugin is disabled.",
|
||||
category: "read",
|
||||
parameters: objectSchema({}),
|
||||
handler: async (_args, context) => {
|
||||
const fleets = await createCurrentFleetRepository().listByUser(
|
||||
context.userId,
|
||||
);
|
||||
const fleets = await listFleets(context.userId);
|
||||
if (fleets === null) return { unavailable: true, fleets: [] };
|
||||
return {
|
||||
fleets: fleets.map((fleet: any) => ({
|
||||
fleets: fleets.map((fleet) => ({
|
||||
id: fleet.id,
|
||||
name: fleet.name,
|
||||
description: fleet.description ?? null,
|
||||
})),
|
||||
};
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user