Skip to content

Commit

Permalink
add cli
Browse files Browse the repository at this point in the history
  • Loading branch information
Flaque committed Feb 14, 2025
1 parent 8f36112 commit e8a6016
Show file tree
Hide file tree
Showing 2 changed files with 159 additions and 83 deletions.
144 changes: 72 additions & 72 deletions src/lib/posthog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@ import process from "node:process";
import { PostHog } from "posthog-node";
import { loadConfig, saveConfig } from "../helpers/config.ts";
import {
cacheFeatureFlag,
getCachedFeatureFlag,
cacheFeatureFlag,
getCachedFeatureFlag,
} from "../helpers/feature-flags.ts";
import { getApiUrl } from "../helpers/urls.ts";

const postHogClient = new PostHog(
"phc_ErsIQYNj6gPFTkHfupfuUGeKjabwtk3WTPdkTDktbU4",
{
host: "https://us.posthog.com",
flushAt: 1,
flushInterval: 0,
}
"phc_ErsIQYNj6gPFTkHfupfuUGeKjabwtk3WTPdkTDktbU4",
{
host: "https://us.posthog.com",
flushAt: 1,
flushInterval: 0,
},
);
// Uncomment this out to see Posthog debugging logs.
// postHogClient.debug();
Expand All @@ -22,49 +22,49 @@ const postHogClient = new PostHog(
* Whether the user has opted out of telemetry collection.
*/
export const IS_TRACKING_DISABLED =
process.env.SF_CLI_TELEMETRY_OPTOUT === "1" ||
process.env.SF_CLI_TELEMETRY_OPTOUT === "true";
process.env.SF_CLI_TELEMETRY_OPTOUT === "1" ||
process.env.SF_CLI_TELEMETRY_OPTOUT === "true";

type EventMessage = Parameters<typeof postHogClient.capture>[0];

const trackEvent = ({
properties,
event,
...payload
properties,
event,
...payload
}: Omit<EventMessage, "distinctId">) => {
const runner = async () => {
const config = await loadConfig();
let exchangeAccountId = config.account_id;

if (!exchangeAccountId) {
const response = await fetch(await getApiUrl("me"), {
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${config.auth_token}`,
},
});

const data = await response.json();
if (data.id) {
exchangeAccountId = data.id;
saveConfig({ ...config, account_id: data.id });
}
}

if (exchangeAccountId) {
postHogClient.capture({
...payload,
distinctId: exchangeAccountId,
event: `cli_sf_${event}`,
properties: { ...properties, source: "cli" },
});
}
};

if (!IS_TRACKING_DISABLED) {
runner();
}
const runner = async () => {
const config = await loadConfig();
let exchangeAccountId = config.account_id;

if (!exchangeAccountId) {
const response = await fetch(await getApiUrl("me"), {
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${config.auth_token}`,
},
});

const data = await response.json();
if (data.id) {
exchangeAccountId = data.id;
saveConfig({ ...config, account_id: data.id });
}
}

if (exchangeAccountId) {
postHogClient.capture({
...payload,
distinctId: exchangeAccountId,
event: `cli_sf_${event}`,
properties: { ...properties, source: "cli" },
});
}
};

if (!IS_TRACKING_DISABLED) {
runner();
}
};

type FeatureFlags = "vms";
Expand All @@ -73,33 +73,33 @@ type FeatureFlags = "vms";
* Checks if a feature is enabled for the current user.
*/
export const isFeatureEnabled = async (feature: FeatureFlags) => {
const config = await loadConfig();
const exchangeAccountId = config.account_id;

if (!exchangeAccountId) {
return false;
}

// Check cache first
const cachedFlag = await getCachedFeatureFlag(feature, exchangeAccountId);
if (cachedFlag) {
return cachedFlag.value;
}

// If not in cache or expired, fetch from PostHog
const result = await postHogClient.isFeatureEnabled(
feature,
exchangeAccountId
);

// Cache the result (PostHog returns undefined if there's an error, default to false)
const finalResult = result ?? false;
await cacheFeatureFlag(feature, exchangeAccountId, finalResult);

return finalResult;
const config = await loadConfig();
const exchangeAccountId = config.account_id;

if (!exchangeAccountId) {
return false;
}

// Check cache first
const cachedFlag = await getCachedFeatureFlag(feature, exchangeAccountId);
if (cachedFlag) {
return cachedFlag.value;
}

// If not in cache or expired, fetch from PostHog
const result = await postHogClient.isFeatureEnabled(
feature,
exchangeAccountId,
);

// Cache the result (PostHog returns undefined if there's an error, default to false)
const finalResult = result ?? false;
await cacheFeatureFlag(feature, exchangeAccountId, finalResult);

return finalResult;
};

export const analytics = {
track: trackEvent,
shutdown: () => postHogClient.shutdown(),
track: trackEvent,
shutdown: () => postHogClient.shutdown(),
};
98 changes: 87 additions & 11 deletions src/lib/vm.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,95 @@
import type { Command } from "@commander-js/extra-typings";
import { isFeatureEnabled } from "./posthog.ts";
import { apiClient } from "../apiClient.ts";
import { readFileSync } from "node:fs";
import {
logAndQuit,
logSessionTokenExpiredAndQuit,
} from "../helpers/errors.ts";

export async function registerVM(program: Command) {
const isEnabled = await isFeatureEnabled("vms");
const isEnabled = await isFeatureEnabled("vms");

if (!isEnabled) {
return;
}
if (!isEnabled) {
return;
}

program
.command("vm")
.description("Manage virtual machines")
.action(async () => {
console.log("VMs!!!");
const vm = program
.command("vm")
.aliases(["v", "vms"])
.description("Manage virtual machines");

process.exit(0);
});
const client = await apiClient();

vm.command("list")
.description("List all virtual machines")
.action(async () => {
const { data, error, response } = await client.GET("/v0/vm/nodes");

if (!response.ok) {
if (response.status === 401) {
await logSessionTokenExpiredAndQuit();
}
logAndQuit(`Failed to list VMs: ${error?.message}`);
}

if (!data?.data) {
logAndQuit("No VMs found");
}

console.table(
data.data.map((node) => ({
id: node.id,
status: node.status,
created_at: node.created_at,
})),
);
});

vm.command("script")
.description("Push a startup script to VMs")
.requiredOption("-f, --file <file>", "Path to startup script file")
.action(async (options) => {
let script: string;
try {
script = readFileSync(options.file, "utf-8");
} catch (err) {
logAndQuit(`Failed to read script file: ${err.message}`);
}

const { error, response } = await client.POST("/v0/vm/script", {
body: { script },
});

if (!response.ok) {
if (response.status === 401) {
await logSessionTokenExpiredAndQuit();
}
logAndQuit(`Failed to upload script: ${error?.message}`);
}

console.log("Successfully uploaded startup script");
});

vm.command("logs")
.description("View VM logs")
.action(async () => {
const { data, error, response } = await client.GET("/v0/vm/logs");

if (!response.ok) {
if (response.status === 401) {
await logSessionTokenExpiredAndQuit();
}
logAndQuit(`Failed to fetch logs: ${error?.message}`);
}

if (!data?.data?.length) {
console.log("No logs found");
return;
}

for (const log of data.data) {
console.log(`[${log.timestamp}] ${log.message}`);
}
});
}

0 comments on commit e8a6016

Please sign in to comment.