@elizaos/plugin-tailscale 2.0.0-beta.1 → 2.0.3-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Shaw Walters and elizaOS Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -7,16 +7,18 @@ Tailscale-backed implementations:
7
7
  `tailscale` CLI (`tailscale serve` for tailnet-internal HTTPS, `tailscale
8
8
  funnel` for public Internet exposure). The user must already be authenticated
9
9
  to a tailnet.
10
- - **Cloud backend (`CloudTailscaleService`)** — calls
11
- `POST /v1/apis/tunnels/tailscale/auth-key` on Eliza Cloud to mint a scoped
10
+ - **Cloud backend (`CloudTailscaleService`)** — POSTs to
11
+ `apis/tunnels/tailscale/auth-key` (relative to `ELIZAOS_CLOUD_BASE_URL`,
12
+ default `https://api.elizacloud.ai/api/v1`) on Eliza Cloud to mint a scoped
12
13
  ephemeral auth key for the configured `tag:eliza-tunnel` ACL, then runs
13
14
  `tailscale up --auth-key=...` followed by `tailscale serve`/`funnel` against
14
- the local port. The cloud holds the OAuth client credentials and Tailnet
15
- identity.
15
+ the local port. The cloud holds the Headscale credential and returns a
16
+ `magicDnsName` the agent serves on. When the response includes a `billing`
17
+ payload it is retained and exposed via `getLastProvisioningBilling()`.
16
18
 
17
19
  Both backends register under `serviceType = "tunnel"` and implement the same
18
- `ITunnelService` shape, so consumers always go through
19
- `runtime.getService("tunnel")` and never reach for backend-specific APIs.
20
+ `ITunnelService` shape, so consumers always go through `getTunnelService(runtime)`
21
+ from `@elizaos/plugin-tunnel` and never reach for backend-specific APIs.
20
22
 
21
23
  > **Mutually exclusive with `@elizaos/plugin-ngrok`.** Both plugins register
22
24
  > under `serviceType = "tunnel"`. Enable only one at a time.
@@ -25,13 +27,11 @@ Both backends register under `serviceType = "tunnel"` and implement the same
25
27
 
26
28
  The plugin reads `TAILSCALE_BACKEND` from runtime settings:
27
29
 
28
- | Value | Behavior |
29
- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
30
- | `local` | Always register `LocalTailscaleService`. |
31
- | `cloud` | Always register `CloudTailscaleService`. |
32
- | `auto` (default) | Register `CloudTailscaleService` when Eliza Cloud is connected (`ELIZAOS_CLOUD_API_KEY` set + `ELIZAOS_CLOUD_ENABLED=true`); otherwise fall back to `LocalTailscaleService`. |
33
-
34
- `isCloudConnected` from `@elizaos/cloud-routing` is the source of truth.
30
+ | Value | Behavior |
31
+ | ---------------- | --------------------------------------------------------------------------------------------------------------------------------- |
32
+ | `local` | Always register `LocalTailscaleService`. |
33
+ | `cloud` | Always register `CloudTailscaleService`. |
34
+ | `auto` (default) | Register `CloudTailscaleService` when `isCloudConnected(runtime)` from `@elizaos/cloud-routing` returns true; otherwise `LocalTailscaleService`. |
35
35
 
36
36
  ## Settings
37
37
 
@@ -44,35 +44,46 @@ The plugin reads `TAILSCALE_BACKEND` from runtime settings:
44
44
  | `TAILSCALE_DEFAULT_PORT` | `3000` | Used when no port is extracted from the user message. |
45
45
  | `TAILSCALE_AUTH_KEY_EXPIRY_SECONDS` | `3600` | Expiry hint passed to the cloud auth-key minter. |
46
46
  | `ELIZAOS_CLOUD_API_KEY` | — | Required for the cloud backend. |
47
- | `ELIZAOS_CLOUD_BASE_URL` | `https://www.elizacloud.ai/api/v1` | Cloud base URL override. |
48
- | `ELIZAOS_CLOUD_ENABLED` | `false` | Required (truthy) for `auto` mode to pick the cloud backend. |
47
+ | `ELIZAOS_CLOUD_BASE_URL` | `https://api.elizacloud.ai/api/v1` | Cloud base URL override. |
49
48
 
50
- ## Actions
49
+ Billing is owned by Eliza Cloud, not this plugin. The auth-key mint response
50
+ may carry a `billing` object (`model: "on_demand"`, `unit`, `charged`,
51
+ `amountUsd`, `subscription`); the plugin only records it for
52
+ `getLastProvisioningBilling()`.
51
53
 
52
- - `START_TAILSCALE` (similes: `START_TUNNEL`, `OPEN_TUNNEL`, `CREATE_TUNNEL`, `TAILSCALE_UP`)
53
- - `STOP_TAILSCALE` (similes: `STOP_TUNNEL`, `CLOSE_TUNNEL`, `TAILSCALE_DOWN`)
54
- - `GET_TAILSCALE_STATUS` (similes: `TAILSCALE_STATUS`, `CHECK_TUNNEL`, `TUNNEL_INFO`)
54
+ ## Actions
55
55
 
56
- All three resolve the active backend through `runtime.getService("tunnel")`, so
57
- they behave identically across local and cloud modes.
56
+ This package does not expose provider-specific actions. It only registers a
57
+ Tailscale-backed `serviceType = "tunnel"` implementation. User-facing tunnel
58
+ operations go through the canonical `TUNNEL` action with
59
+ `action=start | stop | status`.
58
60
 
59
61
  ## Cloud backend wire format
60
62
 
61
- `POST /v1/apis/tunnels/tailscale/auth-key`:
63
+ Request — `POST <ELIZAOS_CLOUD_BASE_URL>/apis/tunnels/tailscale/auth-key`
64
+ (`Authorization: Bearer <ELIZAOS_CLOUD_API_KEY>`):
62
65
 
63
66
  ```json
64
67
  { "tags": ["tag:eliza-tunnel"], "expirySeconds": 3600 }
65
68
  ```
66
69
 
67
- Response:
70
+ Response (`authKey`, `tailnet`, `magicDnsName` required; `loginServer`,
71
+ `hostname`, `billing` optional):
68
72
 
69
73
  ```json
70
74
  {
71
- "authKey": "tskey-auth-...",
75
+ "authKey": "hskey-auth-...",
72
76
  "tailnet": "https://headscale.elizacloud.ai",
73
77
  "loginServer": "https://headscale.elizacloud.ai",
74
- "hostname": "eliza-org-session",
75
- "magicDnsName": "eliza-agent-1234.tunnel.elizacloud.ai"
78
+ "hostname": "eliza-test-session",
79
+ "magicDnsName": "eliza-test-session.tunnel.elizacloud.ai",
80
+ "billing": {
81
+ "model": "on_demand",
82
+ "unit": "tunnel_auth_key",
83
+ "charged": true,
84
+ "amountUsd": 0.01,
85
+ "subscription": false
86
+ }
76
87
  }
77
88
  ```
78
89
 
@@ -80,7 +91,7 @@ The plugin then runs locally, in this order:
80
91
 
81
92
  ```bash
82
93
  tailscale up --auth-key=<authKey> --login-server=<loginServer> --hostname=<hostname>
83
- tailscale serve --bg --https=443 localhost:<port> # or `tailscale funnel <port>`
94
+ tailscale serve --bg --https=443 localhost:<port> # or `tailscale funnel --bg <port>`
84
95
  ```
85
96
 
86
97
  ## Development
@@ -91,7 +102,3 @@ bun run typecheck
91
102
  bun run lint
92
103
  bun run test
93
104
  ```
94
-
95
- ## License
96
-
97
- MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elizaos/plugin-tailscale",
3
- "version": "2.0.0-beta.1",
3
+ "version": "2.0.3-beta.3",
4
4
  "description": "Tunnel plugin for elizaOS — local Tailscale serve/funnel or Eliza Cloud-routed Tailscale auth-key minter",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -9,18 +9,35 @@
9
9
  "exports": {
10
10
  ".": {
11
11
  "types": "./dist/index.d.ts",
12
+ "eliza-source": {
13
+ "types": "./src/index.ts",
14
+ "import": "./src/index.ts",
15
+ "default": "./src/index.ts"
16
+ },
12
17
  "import": "./dist/index.js",
13
18
  "default": "./dist/index.js"
14
19
  },
15
- "./package.json": "./package.json"
20
+ "./package.json": "./package.json",
21
+ "./*.css": "./dist/*.css",
22
+ "./*": {
23
+ "types": "./dist/*.d.ts",
24
+ "eliza-source": {
25
+ "types": "./src/*.ts",
26
+ "import": "./src/*.ts",
27
+ "default": "./src/*.ts"
28
+ },
29
+ "import": "./dist/*.js",
30
+ "default": "./dist/*.js"
31
+ }
16
32
  },
17
33
  "files": [
34
+ "registry-entry.json",
18
35
  "dist"
19
36
  ],
20
37
  "scripts": {
21
38
  "build": "tsup src/index.ts --format esm --dts",
22
- "typecheck": "tsc --noEmit",
23
- "test": "bun run --cwd ../../packages/cloud-routing build && vitest run",
39
+ "typecheck": "tsgo --noEmit",
40
+ "test": "vitest run",
24
41
  "test:watch": "vitest",
25
42
  "test:coverage": "vitest run --coverage",
26
43
  "clean": "rm -rf dist coverage",
@@ -30,13 +47,13 @@
30
47
  "format:check": "bunx @biomejs/biome format ."
31
48
  },
32
49
  "peerDependencies": {
33
- "@elizaos/core": "2.0.0-beta.1",
34
- "@elizaos/plugin-tunnel": "2.0.0-beta.1"
50
+ "@elizaos/core": "2.0.3-beta.3",
51
+ "@elizaos/plugin-tunnel": "2.0.3-beta.3"
35
52
  },
36
53
  "dependencies": {
37
- "@elizaos/cloud-routing": "2.0.0-beta.1",
38
- "@elizaos/core": "2.0.0-beta.1",
39
- "@elizaos/plugin-tunnel": "2.0.0-beta.1",
54
+ "@elizaos/cloud-routing": "2.0.3-beta.3",
55
+ "@elizaos/core": "2.0.3-beta.3",
56
+ "@elizaos/plugin-tunnel": "2.0.3-beta.3",
40
57
  "zod": "^4.4.3"
41
58
  },
42
59
  "devDependencies": {
@@ -48,5 +65,6 @@
48
65
  },
49
66
  "publishConfig": {
50
67
  "access": "public"
51
- }
68
+ },
69
+ "gitHead": "f54b0f4eaed317d59fa7dbcdce20f4cdb0734420"
52
70
  }
@@ -0,0 +1,81 @@
1
+ {
2
+ "id": "tailscale",
3
+ "name": "Tailscale",
4
+ "description": "Tunnel plugin — local Tailscale serve/funnel or Eliza Cloud-routed Tailscale auth-key minter. Mutually exclusive with the ngrok tunnel plugin.",
5
+ "npmName": "@elizaos/plugin-tailscale",
6
+ "version": "2.0.0-beta.0",
7
+ "source": "bundled",
8
+ "tags": ["tunnel", "tailscale", "networking"],
9
+ "config": {
10
+ "TAILSCALE_BACKEND": {
11
+ "type": "string",
12
+ "required": false,
13
+ "sensitive": false,
14
+ "default": "auto",
15
+ "label": "Backend",
16
+ "help": "Backend to use: 'local' (tailscale CLI), 'cloud' (Eliza Cloud-minted ephemeral auth key), or 'auto' (cloud when Eliza Cloud is connected, else local).",
17
+ "advanced": false
18
+ },
19
+ "TAILSCALE_AUTH_KEY": {
20
+ "type": "secret",
21
+ "required": false,
22
+ "sensitive": true,
23
+ "label": "Auth Key",
24
+ "help": "Optional pre-minted Tailscale auth key for the local backend. Most users authenticate once via 'tailscale up' and never set this.",
25
+ "advanced": true
26
+ },
27
+ "TAILSCALE_TAGS": {
28
+ "type": "string",
29
+ "required": false,
30
+ "sensitive": false,
31
+ "default": "tag:eliza-tunnel",
32
+ "label": "Tags",
33
+ "help": "Comma-separated ACL tags applied to the cloud-minted ephemeral key.",
34
+ "advanced": true
35
+ },
36
+ "TAILSCALE_FUNNEL": {
37
+ "type": "boolean",
38
+ "required": false,
39
+ "sensitive": false,
40
+ "default": "False",
41
+ "label": "Use Funnel",
42
+ "help": "When true, use 'tailscale funnel' to expose to the public Internet. When false, use 'tailscale serve' (tailnet-internal only).",
43
+ "advanced": false
44
+ },
45
+ "TAILSCALE_DEFAULT_PORT": {
46
+ "type": "number",
47
+ "required": false,
48
+ "sensitive": false,
49
+ "default": "3000",
50
+ "label": "Default Port",
51
+ "help": "Used when no port is extracted from the user message.",
52
+ "advanced": false
53
+ },
54
+ "TAILSCALE_AUTH_KEY_EXPIRY_SECONDS": {
55
+ "type": "number",
56
+ "required": false,
57
+ "sensitive": false,
58
+ "default": "3600",
59
+ "label": "Auth Key Expiry (seconds)",
60
+ "help": "Expiry hint passed to the cloud auth-key minter.",
61
+ "advanced": true
62
+ }
63
+ },
64
+ "render": {
65
+ "visible": true,
66
+ "pinTo": [],
67
+ "style": "card",
68
+ "icon": "Network",
69
+ "group": "devtools",
70
+ "groupOrder": 6,
71
+ "actions": ["enable", "configure"]
72
+ },
73
+ "resources": {
74
+ "homepage": "https://github.com/eliza-ai/eliza/tree/develop/plugins/plugin-tailscale#readme",
75
+ "repository": "https://github.com/eliza-ai/eliza"
76
+ },
77
+ "dependsOn": [],
78
+ "kind": "plugin",
79
+ "subtype": "devtools",
80
+ "cloudBacked": true
81
+ }
package/dist/index.d.ts DELETED
@@ -1,147 +0,0 @@
1
- import { IAgentRuntime, ConnectorAccountProvider, Service, Plugin } from '@elizaos/core';
2
- import { ITunnelService, TunnelStatus } from '@elizaos/plugin-tunnel';
3
- export { ITunnelService, TunnelProvider, TunnelStatus } from '@elizaos/plugin-tunnel';
4
-
5
- declare const DEFAULT_TAILSCALE_ACCOUNT_ID = "default";
6
- interface TailscaleAccountConfig {
7
- accountId: string;
8
- authKey?: string;
9
- tags?: string | string[];
10
- funnel?: string | boolean;
11
- defaultPort?: string | number;
12
- backend?: "local" | "cloud" | "auto";
13
- authKeyExpirySeconds?: string | number;
14
- cloudApiKey?: string;
15
- cloudBaseUrl?: string;
16
- label?: string;
17
- }
18
- declare function normalizeTailscaleAccountId(value: unknown): string;
19
- declare function resolveTailscaleAccountId(runtime: IAgentRuntime, options?: Record<string, unknown>): string;
20
- declare function readTailscaleAccounts(runtime: IAgentRuntime): TailscaleAccountConfig[];
21
- declare function resolveTailscaleAccount(accounts: readonly TailscaleAccountConfig[], accountId: string): TailscaleAccountConfig | null;
22
-
23
- /**
24
- * Tailscale ConnectorAccountManager provider.
25
- *
26
- * Adapts the existing multi-account resolver in `accounts.ts` to the
27
- * ConnectorAccountManager CRUD surface. Legacy single-account env/settings
28
- * are surfaced as the default OWNER account; additional accounts can be
29
- * declared through character.settings.tailscale.accounts or TAILSCALE_ACCOUNTS.
30
- *
31
- * Tailscale does not use an OAuth redirect flow here. Local CLI login and
32
- * cloud auth-key/API-key provisioning remain owned by the backend services.
33
- */
34
-
35
- declare function createTailscaleConnectorAccountProvider(runtime: IAgentRuntime): ConnectorAccountProvider;
36
-
37
- /**
38
- * Tailscale plugin re-exports the canonical tunnel-service contract from
39
- * `@elizaos/plugin-tunnel`. Both backends (local CLI, cloud auth-key minter)
40
- * register under `serviceType="tunnel"`. Consumers should call
41
- * `getTunnelService(runtime)` from `@elizaos/plugin-tunnel` to stay
42
- * backend-agnostic.
43
- */
44
-
45
- type TailscaleBackendMode = "local" | "cloud" | "auto";
46
-
47
- interface CloudFetchInit {
48
- method: "POST";
49
- headers: Record<string, string>;
50
- body: string;
51
- }
52
- interface CloudFetchResponse {
53
- ok: boolean;
54
- status: number;
55
- statusText: string;
56
- json(): Promise<unknown>;
57
- text(): Promise<string>;
58
- }
59
- type CloudFetch = (url: string, init: CloudFetchInit) => Promise<CloudFetchResponse>;
60
- interface CloudTailscaleServiceOptions {
61
- /** Override fetch impl for tests. */
62
- fetch?: CloudFetch;
63
- /** Override CLI runner for tests. */
64
- cliRunner?: (cmd: string, args: string[]) => Promise<{
65
- code: number | null;
66
- stdout: string;
67
- stderr: string;
68
- }>;
69
- }
70
- declare class CloudTailscaleService extends Service implements ITunnelService {
71
- static serviceType: string;
72
- readonly capabilityDescription = "Provides Tailscale tunnel functionality via Eliza Cloud \u2014 auth keys are minted server-side and the local CLI joins the tailnet.";
73
- private readonly fetchImpl;
74
- private readonly cliRunner;
75
- private tunnelUrl;
76
- private tunnelPort;
77
- private startedAt;
78
- private isShuttingDown;
79
- private joinedTailnet;
80
- constructor(runtime?: IAgentRuntime, options?: CloudTailscaleServiceOptions);
81
- static start(runtime: IAgentRuntime): Promise<Service>;
82
- start(): Promise<void>;
83
- stop(): Promise<void>;
84
- startTunnel(port?: number, options?: {
85
- accountId?: string;
86
- }): Promise<string | undefined>;
87
- stopTunnel(_options?: {
88
- accountId?: string;
89
- }): Promise<void>;
90
- getUrl(): string | null;
91
- isActive(): boolean;
92
- getStatus(): TunnelStatus;
93
- private joinTailnet;
94
- private runServe;
95
- private resolveCloudCredentials;
96
- private cleanup;
97
- }
98
-
99
- declare class LocalTailscaleService extends Service implements ITunnelService {
100
- static serviceType: string;
101
- readonly capabilityDescription = "Provides secure tunnel functionality via the locally-installed `tailscale` CLI (serve / funnel).";
102
- private tunnelUrl;
103
- private tunnelPort;
104
- private startedAt;
105
- private isShuttingDown;
106
- private useFunnel;
107
- static start(runtime: IAgentRuntime): Promise<Service>;
108
- start(): Promise<void>;
109
- stop(): Promise<void>;
110
- startTunnel(port?: number, options?: {
111
- accountId?: string;
112
- }): Promise<string | undefined>;
113
- stopTunnel(_options?: {
114
- accountId?: string;
115
- }): Promise<void>;
116
- getUrl(): string | null;
117
- isActive(): boolean;
118
- getStatus(): TunnelStatus;
119
- private fetchSelfDnsName;
120
- private cleanup;
121
- }
122
-
123
- type TunnelBackendCtor = typeof LocalTailscaleService | typeof CloudTailscaleService;
124
- interface BackendDecision {
125
- backend: TunnelBackendCtor;
126
- mode: TailscaleBackendMode;
127
- reason: string;
128
- }
129
- declare function readBackendMode(runtime: IAgentRuntime): TailscaleBackendMode;
130
- declare function selectTunnelBackend(runtime: IAgentRuntime): BackendDecision;
131
-
132
- /**
133
- * Plugin doesn't list any services upfront. The selector runs in `init()` and
134
- * registers exactly one Tailscale backend (local or cloud) under the canonical
135
- * `serviceType="tunnel"` slot from `@elizaos/plugin-tunnel`. Coordination with
136
- * other tunnel providers (ngrok, plugin-tunnel's local CLI, plugin-elizacloud's
137
- * cloud tunnel) is first-active-wins via `tunnelSlotIsFree(runtime)`.
138
- *
139
- * Consumers should stay backend-agnostic via `getTunnelService(runtime)` from
140
- * `@elizaos/plugin-tunnel`.
141
- *
142
- * The canonical TUNNEL action from `@elizaos/plugin-tunnel` handles start,
143
- * stop, and status. This plugin only contributes a provider/backend.
144
- */
145
- declare const tailscalePlugin: Plugin;
146
-
147
- export { type BackendDecision, CloudTailscaleService, DEFAULT_TAILSCALE_ACCOUNT_ID, LocalTailscaleService, type TailscaleAccountConfig, type TailscaleBackendMode, createTailscaleConnectorAccountProvider, tailscalePlugin as default, normalizeTailscaleAccountId, readBackendMode, readTailscaleAccounts, resolveTailscaleAccount, resolveTailscaleAccountId, selectTunnelBackend, tailscalePlugin };