Skip to content
Open
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
2 changes: 2 additions & 0 deletions extensions/cloudflare-warp/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
vicinae-env.d.ts
4 changes: 4 additions & 0 deletions extensions/cloudflare-warp/.oxfmtrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"$schema": "./node_modules/oxfmt/configuration_schema.json",
"ignorePatterns": []
}
40 changes: 40 additions & 0 deletions extensions/cloudflare-warp/.oxlintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"plugins": null,
"categories": {},
"rules": {},
"settings": {
"jsx-a11y": {
"polymorphicPropName": null,
"components": {},
"attributes": {}
},
"next": {
"rootDir": []
},
"react": {
"formComponents": [],
"linkComponents": [],
"version": null,
"componentWrapperFunctions": []
},
"jsdoc": {
"ignorePrivate": false,
"ignoreInternal": false,
"ignoreReplacesDocs": true,
"overrideReplacesDocs": true,
"augmentsExtendsReplacesDocs": false,
"implementsReplacesDocs": false,
"exemptDestructuredRootsFromChecks": false,
"tagNamePreference": {}
},
"vitest": {
"typecheck": false
}
},
"env": {
"builtin": true
},
"globals": {},
"ignorePatterns": []
}
16 changes: 16 additions & 0 deletions extensions/cloudflare-warp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Vicinae Extension

Congratulations on generating your new Vicinae extension!

You can install the required dependencies and run your extension in development mode like so:

```bash
npm install
npm run dev
```

If you want to build the production bundle, simply run:

```bash
npm run build
```
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 36 additions & 0 deletions extensions/cloudflare-warp/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"$schema": "https://raw.githubusercontent.com/vicinaehq/vicinae/refs/heads/main/extra/schemas/extension.json",
"name": "cloudflare-warp",
"title": "Cloudflare Warp",
"description": "Manage your Cloudflare Warp connection — toggle, check status, and stay in control from your launcher.",
"categories": ["System"],
"license": "MIT",
"author": "anuraglol",
"contributors": [],
"pastContributors": [],
"preferences": [],
"icon": "extension_icon.png",
"scripts": {
"build": "vici build",
"dev": "vici develop",
"format": "oxfmt",
"lint": "vici lint"
},
"dependencies": {
"@vicinae/api": "^0.20.7"
},
"devDependencies": {
"oxfmt": "^0.41.0",
"oxlint": "^1.56.0",
"typescript": "^5.9.2"
},
"commands": [
{
"name": "cloudflare-warp-tool",
"title": "Cloudflare Warp",
"subtitle": "Cloudflare",
"description": "Toggle your Warp connection and check its current status.",
"mode": "view"
}
]
}
113 changes: 113 additions & 0 deletions extensions/cloudflare-warp/src/cloudflare-warp-tool.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { Action, ActionPanel, Icon, List, showToast, Toast } from "@vicinae/api";
import { exec } from "child_process";
import { promisify } from "util";
import { useEffect, useState } from "react";

const execAsync = promisify(exec);

async function checkWarpCli(): Promise<boolean> {
try {
await execAsync("which warp-cli");
return true;
} catch {
return false;
}
}

async function getStatus(): Promise<{ connected: boolean }> {
const { stdout } = await execAsync("warp-cli status");
const connected = !stdout.includes("Disconnected");
return { connected };
}

export default function WarpHelper() {
const [cliAvailable, setCliAvailable] = useState<boolean | null>(null);

useEffect(() => {
checkWarpCli().then(setCliAvailable);
}, []);

if (cliAvailable === null) {
return <List isLoading />;
}

if (!cliAvailable) {
return (
<List>
<List.EmptyView
icon={Icon.Warning}
title="warp-cli not found"
description={
"This extension requires the Cloudflare Warp CLI.\n" +
"Install it from: https://pkg.cloudflareclient.com"
}
/>
</List>
);
}

return (
<List searchBarPlaceholder="Search Warp commands...">
<List.Section title="Warp CLI">
<List.Item
key="toggle"
title="Toggle"
subtitle="Connect or disconnect Warp"
actions={
<ActionPanel>
<Action
title="Toggle"
onAction={async () => {
const toast = await showToast(Toast.Style.Animated, "Warp", "Checking status...");
try {
const { connected } = await getStatus();
if (connected) {
await execAsync("warp-cli disconnect");
toast.style = Toast.Style.Success;
toast.title = "Disconnected";
toast.message = "Warp disconnected";
} else {
await execAsync("warp-cli connect");
toast.style = Toast.Style.Success;
toast.title = "Connected";
toast.message = "Warp connected";
}
} catch (e) {
toast.style = Toast.Style.Failure;
toast.title = "Error";
toast.message = String(e);
}
}}
/>
</ActionPanel>
}
/>
<List.Item
key="status"
title="Status"
subtitle="Check Warp connection status"
actions={
<ActionPanel>
<Action
title="Check Status"
onAction={async () => {
const toast = await showToast(Toast.Style.Animated, "Warp", "Fetching status...");
try {
const { connected } = await getStatus();
toast.style = Toast.Style.Success;
toast.title = connected ? "Connected" : "Disconnected";
toast.message = connected ? "Warp is active" : "Warp is inactive";
} catch (e) {
toast.style = Toast.Style.Failure;
toast.title = "Error";
toast.message = String(e);
}
}}
/>
</ActionPanel>
}
/>
</List.Section>
</List>
);
}
17 changes: 17 additions & 0 deletions extensions/cloudflare-warp/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Node 16",
"include": ["src/**/*"],
"compilerOptions": {
//"lib": ["es2020"],
"module": "commonjs",
"target": "es2020",
"strict": true,
"isolatedModules": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"jsx": "react-jsx"
}
}
Loading