@phnx-labs/agents-cli 1.20.21 → 1.20.23

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.
Files changed (40) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/commands/cloud.js +142 -13
  3. package/dist/commands/exec.js +13 -1
  4. package/dist/commands/menubar.d.ts +10 -0
  5. package/dist/commands/menubar.js +83 -0
  6. package/dist/commands/routines.js +34 -1
  7. package/dist/commands/secrets.d.ts +1 -1
  8. package/dist/commands/secrets.js +95 -38
  9. package/dist/index.js +292 -225
  10. package/dist/lib/agents.js +8 -0
  11. package/dist/lib/cloud/antigravity.d.ts +70 -0
  12. package/dist/lib/cloud/antigravity.js +196 -0
  13. package/dist/lib/cloud/codex.d.ts +1 -0
  14. package/dist/lib/cloud/codex.js +8 -2
  15. package/dist/lib/cloud/factory.d.ts +79 -18
  16. package/dist/lib/cloud/factory.js +324 -26
  17. package/dist/lib/cloud/registry.d.ts +18 -2
  18. package/dist/lib/cloud/registry.js +28 -4
  19. package/dist/lib/cloud/types.d.ts +73 -2
  20. package/dist/lib/cloud/types.js +17 -0
  21. package/dist/lib/exec.d.ts +2 -0
  22. package/dist/lib/exec.js +5 -0
  23. package/dist/lib/menubar/MenubarHelper.app/Contents/Info.plist +20 -0
  24. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  25. package/dist/lib/menubar/MenubarHelper.app/Contents/_CodeSignature/CodeResources +115 -0
  26. package/dist/lib/menubar/install-menubar.d.ts +57 -0
  27. package/dist/lib/menubar/install-menubar.js +291 -0
  28. package/dist/lib/secrets/agent.d.ts +9 -1
  29. package/dist/lib/secrets/agent.js +91 -10
  30. package/dist/lib/secrets/bundles.d.ts +19 -12
  31. package/dist/lib/secrets/bundles.js +22 -14
  32. package/dist/lib/self-update.d.ts +34 -0
  33. package/dist/lib/self-update.js +63 -2
  34. package/dist/lib/startup/command-registry.d.ts +99 -0
  35. package/dist/lib/startup/command-registry.js +136 -0
  36. package/dist/lib/types.d.ts +8 -0
  37. package/dist/lib/version.d.ts +11 -0
  38. package/dist/lib/version.js +20 -0
  39. package/package.json +5 -3
  40. package/scripts/postinstall.js +35 -0
@@ -207,6 +207,9 @@ export const AGENTS = {
207
207
  format: 'markdown',
208
208
  variableSyntax: '$ARGUMENTS',
209
209
  supportsHooks: true,
210
+ // Claude Code has no headless Anthropic-hosted dispatch CLI (only
211
+ // --remote-control, which bridges a *local* session). Its cloud is Rush.
212
+ cloudProvider: 'rush',
210
213
  capabilities: { hooks: true, mcp: true, allowlist: true, skills: true, commands: true, plugins: true, subagents: true, rules: { file: 'CLAUDE.md' }, workflows: true, modes: ['plan', 'edit', 'auto', 'skip'], rulesImports: true },
211
214
  },
212
215
  // codex hooks: gated to >= 0.116.0 (introduced [features] codex_hooks flag).
@@ -226,6 +229,7 @@ export const AGENTS = {
226
229
  format: 'markdown',
227
230
  variableSyntax: '$ARGUMENTS',
228
231
  supportsHooks: true,
232
+ cloudProvider: 'codex',
229
233
  capabilities: { hooks: { since: '0.116.0' }, mcp: true, allowlist: false, skills: true, commands: { until: '0.117.0' }, plugins: { since: '0.128.0' }, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, modes: ['plan', 'edit', 'skip'] },
230
234
  },
231
235
  gemini: {
@@ -412,6 +416,7 @@ export const AGENTS = {
412
416
  format: 'markdown',
413
417
  variableSyntax: '{{args}}',
414
418
  supportsHooks: true,
419
+ cloudProvider: 'antigravity',
415
420
  capabilities: { hooks: true, mcp: true, allowlist: true, skills: true, commands: true, plugins: true, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, modes: ['edit', 'skip'], rulesImports: false },
416
421
  },
417
422
  // xAI Grok Build CLI (`grok`) — early beta, SuperGrok Heavy. Auth via OAuth on
@@ -509,6 +514,9 @@ export const AGENTS = {
509
514
  format: 'markdown',
510
515
  variableSyntax: '$ARGUMENTS',
511
516
  supportsHooks: false,
517
+ // Factory Droid Computers (cloud VMs) reached via `droid computer ssh` +
518
+ // remote headless `droid exec`.
519
+ cloudProvider: 'factory',
512
520
  capabilities: {
513
521
  hooks: false,
514
522
  mcp: true,
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Antigravity cloud provider — Google's Antigravity agent via the Gemini
3
+ * **Managed Agents Interactions API**.
4
+ *
5
+ * The `agy` CLI is local-only (`--print` / `--sandbox`); Antigravity's cloud
6
+ * surface is an HTTP endpoint that runs the Antigravity harness in a remote
7
+ * ephemeral Linux sandbox:
8
+ *
9
+ * POST https://generativelanguage.googleapis.com/v1beta/interactions
10
+ * x-goog-api-key: <GEMINI_API_KEY>
11
+ * { "agent": "antigravity-preview-05-2026", "input": "<prompt>", "environment": "remote" }
12
+ *
13
+ * The call is synchronous by default (the response carries `status` +
14
+ * `output_text`), so `dispatch()` awaits it, returns a terminal CloudTask, and
15
+ * `stream()` replays the buffered text + done events — same shape as the Factory
16
+ * provider. It is a raw sandbox: no GitHub repo → PR (that's Rush's job).
17
+ *
18
+ * Auth: the Gemini API key comes from an `agents secrets` bundle named in
19
+ * `cloud.providers.antigravity.secretsBundle` (never from agents.yaml), falling
20
+ * back to GEMINI_API_KEY / GOOGLE_API_KEY in the environment.
21
+ */
22
+ import type { CloudProvider, CloudTask, CloudTaskStatus, CloudEvent, DispatchOptions, ProviderCapabilities } from './types.js';
23
+ /** Shape of the Interactions API response we consume (defensive: all optional). */
24
+ interface InteractionResponse {
25
+ id?: string;
26
+ interaction_id?: string;
27
+ environment_id?: string;
28
+ status?: string;
29
+ output_text?: string;
30
+ error?: {
31
+ message?: string;
32
+ } | string;
33
+ }
34
+ /** Map an Interactions API status string to the canonical CloudTaskStatus. */
35
+ export declare function mapStatus(s: string | undefined): CloudTaskStatus;
36
+ /** Build the Interactions API request body for a fresh dispatch. */
37
+ export declare function buildInteractionBody(prompt: string, model: string): Record<string, unknown>;
38
+ /** Parse an Interactions API response into a CloudTask (minus prompt/timestamps). */
39
+ export declare function parseInteraction(resp: InteractionResponse): {
40
+ id: string;
41
+ status: CloudTaskStatus;
42
+ summary?: string;
43
+ environmentId?: string;
44
+ };
45
+ export declare class AntigravityCloudProvider implements CloudProvider {
46
+ id: "antigravity";
47
+ name: string;
48
+ private secretsBundle?;
49
+ private model;
50
+ private runs;
51
+ constructor(config?: {
52
+ secretsBundle?: string;
53
+ model?: string;
54
+ });
55
+ /**
56
+ * True when a key *source* is configured. Cheap: never resolves the bundle
57
+ * (which could prompt for biometry) — that happens lazily at dispatch.
58
+ */
59
+ private hasKeySource;
60
+ /** Resolve the Gemini API key from the configured bundle or the environment. */
61
+ private resolveApiKey;
62
+ capabilities(): ProviderCapabilities;
63
+ dispatch(options: DispatchOptions): Promise<CloudTask>;
64
+ status(taskId: string): Promise<CloudTask>;
65
+ list(): Promise<CloudTask[]>;
66
+ stream(taskId: string): AsyncIterable<CloudEvent>;
67
+ cancel(_taskId: string): Promise<void>;
68
+ message(_taskId: string, _content: string): Promise<void>;
69
+ }
70
+ export {};
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Antigravity cloud provider — Google's Antigravity agent via the Gemini
3
+ * **Managed Agents Interactions API**.
4
+ *
5
+ * The `agy` CLI is local-only (`--print` / `--sandbox`); Antigravity's cloud
6
+ * surface is an HTTP endpoint that runs the Antigravity harness in a remote
7
+ * ephemeral Linux sandbox:
8
+ *
9
+ * POST https://generativelanguage.googleapis.com/v1beta/interactions
10
+ * x-goog-api-key: <GEMINI_API_KEY>
11
+ * { "agent": "antigravity-preview-05-2026", "input": "<prompt>", "environment": "remote" }
12
+ *
13
+ * The call is synchronous by default (the response carries `status` +
14
+ * `output_text`), so `dispatch()` awaits it, returns a terminal CloudTask, and
15
+ * `stream()` replays the buffered text + done events — same shape as the Factory
16
+ * provider. It is a raw sandbox: no GitHub repo → PR (that's Rush's job).
17
+ *
18
+ * Auth: the Gemini API key comes from an `agents secrets` bundle named in
19
+ * `cloud.providers.antigravity.secretsBundle` (never from agents.yaml), falling
20
+ * back to GEMINI_API_KEY / GOOGLE_API_KEY in the environment.
21
+ */
22
+ import { resolveDispatchRepos } from './types.js';
23
+ import { readAndResolveBundleEnv } from '../secrets/bundles.js';
24
+ const INTERACTIONS_URL = 'https://generativelanguage.googleapis.com/v1beta/interactions';
25
+ const DEFAULT_MODEL = 'antigravity-preview-05-2026';
26
+ const KEY_NAMES = ['GEMINI_API_KEY', 'GOOGLE_API_KEY'];
27
+ /** Map an Interactions API status string to the canonical CloudTaskStatus. */
28
+ export function mapStatus(s) {
29
+ const lower = (s ?? '').toLowerCase();
30
+ if (lower.includes('queue') || lower.includes('pending'))
31
+ return 'queued';
32
+ if (lower.includes('run') || lower.includes('progress'))
33
+ return 'running';
34
+ if (lower.includes('complete') || lower.includes('success'))
35
+ return 'completed';
36
+ if (lower.includes('fail') || lower.includes('error'))
37
+ return 'failed';
38
+ if (lower.includes('cancel'))
39
+ return 'cancelled';
40
+ return 'completed';
41
+ }
42
+ /** Build the Interactions API request body for a fresh dispatch. */
43
+ export function buildInteractionBody(prompt, model) {
44
+ return {
45
+ agent: model,
46
+ input: prompt,
47
+ environment: 'remote',
48
+ };
49
+ }
50
+ /** Parse an Interactions API response into a CloudTask (minus prompt/timestamps). */
51
+ export function parseInteraction(resp) {
52
+ const id = resp.id ?? resp.interaction_id ?? `antigravity-${Date.now()}`;
53
+ return {
54
+ id,
55
+ status: mapStatus(resp.status),
56
+ summary: resp.output_text,
57
+ environmentId: resp.environment_id,
58
+ };
59
+ }
60
+ export class AntigravityCloudProvider {
61
+ id = 'antigravity';
62
+ name = 'Antigravity (Gemini)';
63
+ secretsBundle;
64
+ model;
65
+ runs = new Map();
66
+ constructor(config) {
67
+ this.secretsBundle = config?.secretsBundle;
68
+ this.model = config?.model ?? DEFAULT_MODEL;
69
+ }
70
+ /**
71
+ * True when a key *source* is configured. Cheap: never resolves the bundle
72
+ * (which could prompt for biometry) — that happens lazily at dispatch.
73
+ */
74
+ hasKeySource() {
75
+ if (this.secretsBundle)
76
+ return true;
77
+ return KEY_NAMES.some((k) => Boolean(process.env[k]));
78
+ }
79
+ /** Resolve the Gemini API key from the configured bundle or the environment. */
80
+ resolveApiKey() {
81
+ if (this.secretsBundle) {
82
+ try {
83
+ const { env } = readAndResolveBundleEnv(this.secretsBundle, { caller: 'cloud:antigravity' });
84
+ for (const k of KEY_NAMES) {
85
+ if (env[k])
86
+ return env[k];
87
+ }
88
+ throw new Error(`Secrets bundle '${this.secretsBundle}' has no ${KEY_NAMES.join(' or ')}. Add one: agents secrets add ${this.secretsBundle} GEMINI_API_KEY`);
89
+ }
90
+ catch (err) {
91
+ throw new Error(`Could not read Gemini API key from bundle '${this.secretsBundle}': ${err.message}`);
92
+ }
93
+ }
94
+ for (const k of KEY_NAMES) {
95
+ const v = process.env[k];
96
+ if (v)
97
+ return v;
98
+ }
99
+ throw new Error(`Antigravity cloud needs a Gemini API key. Set cloud.providers.antigravity.secretsBundle in ~/.agents/agents.yaml, or export ${KEY_NAMES.join(' / ')}.`);
100
+ }
101
+ capabilities() {
102
+ const available = this.hasKeySource();
103
+ return {
104
+ available,
105
+ dispatch: available,
106
+ status: available,
107
+ list: available,
108
+ stream: available,
109
+ cancel: false,
110
+ message: false,
111
+ multiRepo: false,
112
+ skills: false,
113
+ images: false,
114
+ };
115
+ }
116
+ async dispatch(options) {
117
+ // The Interactions sandbox has no GitHub repo → PR flow. Reject repos
118
+ // loudly rather than silently ignoring them (point the user at Rush).
119
+ const repos = resolveDispatchRepos(options);
120
+ if (repos.length > 0) {
121
+ throw new Error(`Antigravity cloud is a raw sandbox with no GitHub repo → PR flow. Got repo(s): ${repos.join(', ')}. ` +
122
+ `Use --provider rush for repo-backed dispatch.`);
123
+ }
124
+ const apiKey = this.resolveApiKey();
125
+ const model = options.model ?? this.model;
126
+ const body = buildInteractionBody(options.prompt, model);
127
+ let resp;
128
+ try {
129
+ resp = await fetch(INTERACTIONS_URL, {
130
+ method: 'POST',
131
+ headers: { 'content-type': 'application/json', 'x-goog-api-key': apiKey },
132
+ body: JSON.stringify(body),
133
+ });
134
+ }
135
+ catch (err) {
136
+ throw new Error(`Antigravity dispatch failed (network): ${err.message}`);
137
+ }
138
+ const text = await resp.text();
139
+ if (!resp.ok) {
140
+ throw new Error(`Antigravity dispatch failed (${resp.status}): ${text.slice(0, 500)}`);
141
+ }
142
+ let parsed;
143
+ try {
144
+ parsed = JSON.parse(text);
145
+ }
146
+ catch {
147
+ throw new Error(`Antigravity returned non-JSON response: ${text.slice(0, 300)}`);
148
+ }
149
+ const { id, status, summary } = parseInteraction(parsed);
150
+ const now = new Date().toISOString();
151
+ const task = {
152
+ id,
153
+ provider: 'antigravity',
154
+ status,
155
+ agent: 'antigravity',
156
+ prompt: options.prompt,
157
+ summary,
158
+ createdAt: now,
159
+ updatedAt: now,
160
+ };
161
+ const events = [];
162
+ if (summary)
163
+ events.push({ type: 'text', content: summary, timestamp: now });
164
+ events.push({ type: 'done', status, summary, timestamp: now });
165
+ this.runs.set(id, { events, task });
166
+ return task;
167
+ }
168
+ async status(taskId) {
169
+ const run = this.runs.get(taskId);
170
+ if (run)
171
+ return run.task;
172
+ throw new Error(`No live status for Antigravity task ${taskId} (synchronous interaction; see local cache).`);
173
+ }
174
+ async list() {
175
+ return [...this.runs.values()].map((r) => r.task);
176
+ }
177
+ async *stream(taskId) {
178
+ const run = this.runs.get(taskId);
179
+ if (!run) {
180
+ yield {
181
+ type: 'error',
182
+ message: `Antigravity interaction ${taskId} is not buffered in this process. The interaction is synchronous — see 'agents cloud status ${taskId}' for the result.`,
183
+ timestamp: new Date().toISOString(),
184
+ };
185
+ return;
186
+ }
187
+ for (const event of run.events)
188
+ yield event;
189
+ }
190
+ async cancel(_taskId) {
191
+ throw new Error('Cancel is not supported for Antigravity — interactions run synchronously to completion.');
192
+ }
193
+ async message(_taskId, _content) {
194
+ throw new Error('Follow-up messages are not yet supported for Antigravity cloud tasks.');
195
+ }
196
+ }
@@ -9,6 +9,7 @@ import type { CloudProvider, CloudTask, CloudTaskStatus, CloudEvent, DispatchOpt
9
9
  export declare class CodexCloudProvider implements CloudProvider {
10
10
  id: "codex";
11
11
  name: string;
12
+ targetKind: "env";
12
13
  private defaultEnv?;
13
14
  constructor(config?: {
14
15
  env?: string;
@@ -8,7 +8,7 @@
8
8
  import { spawn, execFileSync } from 'child_process';
9
9
  import * as fs from 'fs';
10
10
  import * as path from 'path';
11
- import { resolveDispatchRepos } from './types.js';
11
+ import { resolveDispatchRepos, MissingTargetError } from './types.js';
12
12
  import { getShimsDir } from '../state.js';
13
13
  const SHIMS_DIR = getShimsDir();
14
14
  /** Map a Codex Cloud status string to the canonical CloudTaskStatus enum. */
@@ -85,6 +85,10 @@ function parseTaskFromText(text) {
85
85
  export class CodexCloudProvider {
86
86
  id = 'codex';
87
87
  name = 'Codex Cloud';
88
+ targetKind = 'env';
89
+ // No listTargets: OpenAI ships no non-interactive "list environments"
90
+ // command. `codex cloud exec` requires --env and the help points at the
91
+ // interactive `codex cloud` TUI to browse. So discovery is guidance-only.
88
92
  defaultEnv;
89
93
  constructor(config) {
90
94
  this.defaultEnv = config?.env;
@@ -107,7 +111,9 @@ export class CodexCloudProvider {
107
111
  async dispatch(options) {
108
112
  const env = options.providerOptions?.env ?? this.defaultEnv;
109
113
  if (!env) {
110
- throw new Error('Codex Cloud requires --env <id>. Set a default in ~/.agents/agents.yaml under cloud.providers.codex.env.');
114
+ throw new MissingTargetError('env', 'Codex Cloud requires --env <id>.', 'Codex environments are created in the Codex web UI and bundle a repo + setup. ' +
115
+ 'Browse yours with `codex cloud` (interactive), then re-run with --env <id> ' +
116
+ 'or set a default in ~/.agents/agents.yaml under cloud.providers.codex.env.');
111
117
  }
112
118
  // Codex envs bundle their own repo list — the repos a task can touch are
113
119
  // fixed at env-creation time, not per-dispatch. Passing 2+ repos here is
@@ -1,31 +1,92 @@
1
1
  /**
2
- * Factory/Droid cloud provider -- stub for Phase 2.
2
+ * Factory (Droid) cloud provider.
3
3
  *
4
- * Will dispatch tasks to a `droid daemon` running on a remote machine.
5
- * All methods throw until the droid daemon API is documented and stable.
4
+ * Dispatches to a Factory **Droid Computer** a persistent cloud VM (managed
5
+ * by Factory, or bring-your-own via `droid computer register`). There is no
6
+ * `droid cloud run`; remote execution = reach the computer over the Droid relay
7
+ * (`droid computer ssh <name>`) and run a headless `droid exec` there.
8
+ *
9
+ * `droid exec` is synchronous (it runs to completion and exits, unlike Codex
10
+ * Cloud which is async server-side). So `dispatch()` runs the remote exec to
11
+ * completion with `--output-format stream-json`, buffers the NDJSON events, and
12
+ * `stream()` replays them. The task id is droid's own `session_id` (captured
13
+ * from the run output), so it lines up with `droid exec -s <id>` for future
14
+ * resume support.
15
+ *
16
+ * Transport note: the exact relay SSH composition (user + ProxyCommand) is
17
+ * built in `buildSshArgs()` and must be confirmed against a live provisioned
18
+ * Droid Computer (Factory auth required). `capabilities().available` gates on
19
+ * the droid binary + a configured computer so the provider fails with a clear
20
+ * message rather than misfiring when unconfigured.
6
21
  */
7
- import type { CloudProvider, CloudTask, CloudTaskStatus, CloudEvent, DispatchOptions, ProviderCapabilities } from './types.js';
22
+ import type { CloudProvider, CloudTask, CloudTaskStatus, CloudEvent, CloudTarget, DispatchOptions, ProviderCapabilities, DroidAutonomy } from './types.js';
23
+ /** Locate the droid binary, checking agents-cli shims first then PATH. */
24
+ export declare function findDroidBinary(): string | null;
25
+ /** Normalize an autonomy value, falling back to the safe cloud default (`high`). */
26
+ export declare function resolveAutonomy(value: unknown, fallback?: DroidAutonomy): DroidAutonomy;
8
27
  /**
9
- * Factory/Droid cloud provider stub for Phase 2.
10
- *
11
- * Integration path: `droid daemon` running on a remote machine (workstation, cloud VM, k8s pod).
12
- * Dispatch via HTTP to the daemon, stream output, cancel via HTTP DELETE.
28
+ * Parse `droid computer list` text into targets. Defensive: the exact column
29
+ * layout isn't documented, so we take the first whitespace token of each data
30
+ * row as the computer name and keep the remainder as a label, skipping headers,
31
+ * separators, and status messages. The interactive picker degrades to free-text
32
+ * entry if this yields nothing, so an unexpected layout never blocks a dispatch.
33
+ */
34
+ export declare function parseComputerList(text: string): CloudTarget[];
35
+ /**
36
+ * Build the remote `droid exec` argv. Headless, stream-json output, given
37
+ * autonomy. `sessionId` (when resuming) maps to `-s`.
38
+ */
39
+ export declare function buildExecArgs(prompt: string, opts: {
40
+ autonomy: DroidAutonomy;
41
+ sessionId?: string;
42
+ }): string[];
43
+ /**
44
+ * Build the `ssh` argv that runs a remote command on a Droid Computer through
45
+ * the Droid relay. The relay is used as an OpenSSH ProxyCommand
46
+ * (`droid computer ssh <name> --proxy`), so the connection rides Factory's
47
+ * brokered tunnel rather than a directly reachable host.
13
48
  *
14
- * Not yet implemented because:
15
- * 1. Droid v0.104 has no cloud dispatch command (droid exec is local only)
16
- * 2. droid daemon API isn't documented yet
17
- * 3. droid computer register/ssh is the remote execution primitive but needs exploration
49
+ * `remoteArgv` is the already-built remote command (e.g. droid exec argv); it is
50
+ * shell-quoted into a single remote command string.
51
+ */
52
+ export declare function buildSshArgs(computer: string, remoteBin: string, remoteArgv: string[], opts: {
53
+ droidBin: string;
54
+ user: string;
55
+ port?: string;
56
+ }): string[];
57
+ /** Map a droid stream-json `result.subtype` / `is_error` to a CloudTaskStatus. */
58
+ export declare function mapResultStatus(line: {
59
+ is_error?: boolean;
60
+ subtype?: string;
61
+ }): CloudTaskStatus;
62
+ /**
63
+ * Map one parsed droid stream-json event to a CloudEvent. The stream-json
64
+ * schema is only partially documented, so this is defensive: known shapes map
65
+ * to typed events, everything else surfaces as `unknown` rather than being
66
+ * dropped (mirrors the rest of the cloud event pipeline).
18
67
  */
68
+ export declare function mapDroidEvent(obj: Record<string, unknown>): CloudEvent;
19
69
  export declare class FactoryCloudProvider implements CloudProvider {
20
70
  id: "factory";
21
71
  name: string;
72
+ targetKind: "computer";
73
+ private defaultComputer?;
74
+ private defaultAutonomy;
75
+ /** session_id → buffered run, populated by dispatch, drained by stream. */
76
+ private runs;
77
+ constructor(config?: {
78
+ computer?: string;
79
+ autonomy?: DroidAutonomy;
80
+ });
22
81
  capabilities(): ProviderCapabilities;
23
- dispatch(_options: DispatchOptions): Promise<CloudTask>;
24
- status(_taskId: string): Promise<CloudTask>;
25
- list(_filter?: {
26
- status?: CloudTaskStatus;
27
- }): Promise<CloudTask[]>;
28
- stream(_taskId: string): AsyncIterable<CloudEvent>;
82
+ /** Enumerate Droid Computers via `droid computer list`. Throws if not signed in. */
83
+ listTargets(): Promise<CloudTarget[]>;
84
+ dispatch(options: DispatchOptions): Promise<CloudTask>;
85
+ /** Run the remote droid exec to completion, collecting events + final result. */
86
+ private runRemote;
87
+ status(taskId: string): Promise<CloudTask>;
88
+ list(): Promise<CloudTask[]>;
89
+ stream(taskId: string): AsyncIterable<CloudEvent>;
29
90
  cancel(_taskId: string): Promise<void>;
30
91
  message(_taskId: string, _content: string): Promise<void>;
31
92
  }