@phnx-labs/agents-cli 1.20.21 → 1.20.22

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.
@@ -1,30 +1,176 @@
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
  */
22
+ import { spawn, execFileSync } from 'child_process';
23
+ import * as fs from 'fs';
24
+ import * as path from 'path';
25
+ import { getShimsDir } from '../state.js';
26
+ const SHIMS_DIR = getShimsDir();
27
+ const DEFAULT_AUTONOMY = 'high';
28
+ const VALID_AUTONOMY = new Set(['low', 'medium', 'high']);
29
+ /** Locate the droid binary, checking agents-cli shims first then PATH. */
30
+ export function findDroidBinary() {
31
+ const shim = path.join(SHIMS_DIR, 'droid');
32
+ if (fs.existsSync(shim))
33
+ return shim;
34
+ try {
35
+ return execFileSync('which', ['droid'], { stdio: 'pipe' }).toString().trim() || null;
36
+ }
37
+ catch {
38
+ return null;
39
+ }
40
+ }
41
+ /** Normalize an autonomy value, falling back to the safe cloud default (`high`). */
42
+ export function resolveAutonomy(value, fallback = DEFAULT_AUTONOMY) {
43
+ return typeof value === 'string' && VALID_AUTONOMY.has(value)
44
+ ? value
45
+ : fallback;
46
+ }
7
47
  /**
8
- * Factory/Droid cloud provider stub for Phase 2.
9
- *
10
- * Integration path: `droid daemon` running on a remote machine (workstation, cloud VM, k8s pod).
11
- * Dispatch via HTTP to the daemon, stream output, cancel via HTTP DELETE.
48
+ * Build the remote `droid exec` argv. Headless, stream-json output, given
49
+ * autonomy. `sessionId` (when resuming) maps to `-s`.
50
+ */
51
+ export function buildExecArgs(prompt, opts) {
52
+ const args = ['exec', '--auto', opts.autonomy, '--output-format', 'stream-json'];
53
+ if (opts.sessionId)
54
+ args.push('-s', opts.sessionId);
55
+ args.push(prompt);
56
+ return args;
57
+ }
58
+ /**
59
+ * Build the `ssh` argv that runs a remote command on a Droid Computer through
60
+ * the Droid relay. The relay is used as an OpenSSH ProxyCommand
61
+ * (`droid computer ssh <name> --proxy`), so the connection rides Factory's
62
+ * brokered tunnel rather than a directly reachable host.
12
63
  *
13
- * Not yet implemented because:
14
- * 1. Droid v0.104 has no cloud dispatch command (droid exec is local only)
15
- * 2. droid daemon API isn't documented yet
16
- * 3. droid computer register/ssh is the remote execution primitive but needs exploration
64
+ * `remoteArgv` is the already-built remote command (e.g. droid exec argv); it is
65
+ * shell-quoted into a single remote command string.
66
+ */
67
+ export function buildSshArgs(computer, remoteBin, remoteArgv, opts) {
68
+ const remoteCmd = [remoteBin, ...remoteArgv].map(shellQuote).join(' ');
69
+ const proxy = `ProxyCommand=${opts.droidBin} computer ssh ${computer} --proxy --port %p`;
70
+ return [
71
+ '-o', proxy,
72
+ '-o', 'StrictHostKeyChecking=accept-new',
73
+ '-p', opts.port ?? '22',
74
+ `${opts.user}@${computer}`,
75
+ remoteCmd,
76
+ ];
77
+ }
78
+ /** POSIX single-quote a shell argument. */
79
+ function shellQuote(arg) {
80
+ return `'${arg.replace(/'/g, `'\\''`)}'`;
81
+ }
82
+ /** Map a droid stream-json `result.subtype` / `is_error` to a CloudTaskStatus. */
83
+ export function mapResultStatus(line) {
84
+ if (line.is_error)
85
+ return 'failed';
86
+ if (line.subtype && /cancel/i.test(line.subtype))
87
+ return 'cancelled';
88
+ return 'completed';
89
+ }
90
+ /**
91
+ * Map one parsed droid stream-json event to a CloudEvent. The stream-json
92
+ * schema is only partially documented, so this is defensive: known shapes map
93
+ * to typed events, everything else surfaces as `unknown` rather than being
94
+ * dropped (mirrors the rest of the cloud event pipeline).
17
95
  */
96
+ export function mapDroidEvent(obj) {
97
+ const ts = new Date().toISOString();
98
+ const type = String(obj.type ?? '');
99
+ switch (type) {
100
+ case 'result': {
101
+ return {
102
+ type: 'done',
103
+ status: mapResultStatus(obj),
104
+ summary: typeof obj.result === 'string' ? obj.result : undefined,
105
+ timestamp: ts,
106
+ };
107
+ }
108
+ case 'assistant':
109
+ case 'message':
110
+ case 'text': {
111
+ const content = extractText(obj);
112
+ if (content)
113
+ return { type: 'text', content, timestamp: ts };
114
+ break;
115
+ }
116
+ case 'thinking':
117
+ case 'reasoning': {
118
+ const content = extractText(obj);
119
+ if (content)
120
+ return { type: 'thinking', content, timestamp: ts };
121
+ break;
122
+ }
123
+ case 'tool_call':
124
+ case 'tool_use': {
125
+ return { type: 'tool_use', tool: String(obj.name ?? obj.tool ?? 'tool'), input: obj.input ?? obj.arguments ?? {}, timestamp: ts };
126
+ }
127
+ case 'tool_result': {
128
+ return { type: 'tool_result', tool: String(obj.name ?? obj.tool ?? 'tool'), output: obj.output ?? obj.result ?? '', timestamp: ts };
129
+ }
130
+ case 'error': {
131
+ return { type: 'error', message: String(obj.message ?? obj.error ?? 'unknown error'), timestamp: ts };
132
+ }
133
+ }
134
+ return { type: 'unknown', name: type || 'unknown', data: JSON.stringify(obj), timestamp: ts };
135
+ }
136
+ /** Pull a text string out of the varied droid message shapes. */
137
+ function extractText(obj) {
138
+ if (typeof obj.text === 'string')
139
+ return obj.text;
140
+ if (typeof obj.content === 'string')
141
+ return obj.content;
142
+ if (Array.isArray(obj.content)) {
143
+ return obj.content
144
+ .map((c) => (c && typeof c === 'object' && typeof c.text === 'string' ? c.text : ''))
145
+ .join('');
146
+ }
147
+ const msg = obj.message;
148
+ if (msg && typeof msg === 'object')
149
+ return extractText(msg);
150
+ return '';
151
+ }
18
152
  export class FactoryCloudProvider {
19
153
  id = 'factory';
20
154
  name = 'Factory (Droid)';
155
+ defaultComputer;
156
+ defaultAutonomy;
157
+ /** session_id → buffered run, populated by dispatch, drained by stream. */
158
+ runs = new Map();
159
+ constructor(config) {
160
+ this.defaultComputer = config?.computer;
161
+ this.defaultAutonomy = resolveAutonomy(config?.autonomy);
162
+ }
21
163
  capabilities() {
164
+ const droid = findDroidBinary() !== null;
165
+ const computer = Boolean(this.defaultComputer);
22
166
  return {
23
- available: false,
24
- dispatch: false,
25
- status: false,
26
- list: false,
27
- stream: false,
167
+ // Reachable only when the droid binary exists AND a computer is set.
168
+ // (A per-dispatch --computer can still override the missing default.)
169
+ available: droid && computer,
170
+ dispatch: droid,
171
+ status: droid,
172
+ list: droid,
173
+ stream: droid,
28
174
  cancel: false,
29
175
  message: false,
30
176
  multiRepo: false,
@@ -32,22 +178,119 @@ export class FactoryCloudProvider {
32
178
  images: false,
33
179
  };
34
180
  }
35
- async dispatch(_options) {
36
- throw new Error('Factory cloud provider is not yet available. Coming in a future release.');
181
+ async dispatch(options) {
182
+ const droidBin = findDroidBinary();
183
+ if (!droidBin) {
184
+ throw new Error('droid CLI not found. Install it: curl -fsSL https://app.factory.ai/cli | sh');
185
+ }
186
+ const computer = options.providerOptions?.computer ?? this.defaultComputer;
187
+ if (!computer) {
188
+ throw new Error('Factory cloud requires a Droid Computer. Pass --computer <name>, or set ' +
189
+ 'cloud.providers.factory.computer in ~/.agents/agents.yaml. Create one in ' +
190
+ 'Factory (Settings → Droid Computers), or register a machine with `droid computer register`.');
191
+ }
192
+ const autonomy = resolveAutonomy(options.providerOptions?.autonomy ?? options.providerOptions?.mode, this.defaultAutonomy);
193
+ const user = options.providerOptions?.user ?? 'droid';
194
+ const execArgs = buildExecArgs(options.prompt, { autonomy });
195
+ const sshArgs = buildSshArgs(computer, 'droid', execArgs, { droidBin, user });
196
+ const { events, status, summary, sessionId } = await this.runRemote(sshArgs);
197
+ const now = new Date().toISOString();
198
+ const id = sessionId ?? `droid-${Date.now()}`;
199
+ const task = {
200
+ id,
201
+ provider: 'factory',
202
+ status,
203
+ agent: 'droid',
204
+ prompt: options.prompt,
205
+ summary,
206
+ createdAt: now,
207
+ updatedAt: now,
208
+ };
209
+ this.runs.set(id, { events, task });
210
+ return task;
211
+ }
212
+ /** Run the remote droid exec to completion, collecting events + final result. */
213
+ runRemote(sshArgs) {
214
+ return new Promise((resolve, reject) => {
215
+ const proc = spawn('ssh', sshArgs, { stdio: ['ignore', 'pipe', 'pipe'] });
216
+ const events = [];
217
+ let status = 'failed';
218
+ let summary;
219
+ let sessionId;
220
+ let stdoutBuf = '';
221
+ let stderr = '';
222
+ const handleLine = (line) => {
223
+ const trimmed = line.trim();
224
+ if (!trimmed)
225
+ return;
226
+ let obj;
227
+ try {
228
+ obj = JSON.parse(trimmed);
229
+ }
230
+ catch {
231
+ // Non-JSON line (banner, ssh notice) — surface it, don't drop it.
232
+ events.push({ type: 'unknown', name: 'stdout', data: trimmed, timestamp: new Date().toISOString() });
233
+ return;
234
+ }
235
+ if (typeof obj.session_id === 'string')
236
+ sessionId = obj.session_id;
237
+ const event = mapDroidEvent(obj);
238
+ if (event.type === 'done') {
239
+ status = event.status ?? 'completed';
240
+ summary = event.summary ?? summary;
241
+ }
242
+ events.push(event);
243
+ };
244
+ proc.stdout.on('data', (d) => {
245
+ stdoutBuf += d.toString();
246
+ const lines = stdoutBuf.split('\n');
247
+ stdoutBuf = lines.pop() ?? '';
248
+ for (const line of lines)
249
+ handleLine(line);
250
+ });
251
+ proc.stderr.on('data', (d) => { stderr += d.toString(); });
252
+ proc.on('error', (err) => reject(err));
253
+ proc.on('close', (code) => {
254
+ if (stdoutBuf)
255
+ handleLine(stdoutBuf);
256
+ if (code !== 0 && events.every((e) => e.type !== 'done')) {
257
+ // Surface the auth error verbatim — it's the common first-run failure.
258
+ const detail = stderr.trim() || `ssh exited ${code}`;
259
+ reject(new Error(`Factory dispatch failed: ${detail}`));
260
+ return;
261
+ }
262
+ resolve({ events, status, summary, sessionId });
263
+ });
264
+ });
37
265
  }
38
- async status(_taskId) {
39
- throw new Error('Factory cloud provider is not yet available.');
266
+ async status(taskId) {
267
+ const run = this.runs.get(taskId);
268
+ if (run)
269
+ return run.task;
270
+ // No remote task registry — the command layer falls back to the local store.
271
+ throw new Error(`No live status for Factory task ${taskId} (synchronous run; see local cache).`);
40
272
  }
41
- async list(_filter) {
42
- return [];
273
+ async list() {
274
+ // Factory has no remote task list; `agents cloud list` reads the local store.
275
+ return [...this.runs.values()].map((r) => r.task);
43
276
  }
44
- async *stream(_taskId) {
45
- throw new Error('Factory cloud provider is not yet available.');
277
+ async *stream(taskId) {
278
+ const run = this.runs.get(taskId);
279
+ if (!run) {
280
+ yield {
281
+ type: 'error',
282
+ message: `Factory run ${taskId} is not buffered in this process. droid exec is synchronous — its output is not retained after the run completes. See 'agents cloud status ${taskId}' for the summary.`,
283
+ timestamp: new Date().toISOString(),
284
+ };
285
+ return;
286
+ }
287
+ for (const event of run.events)
288
+ yield event;
46
289
  }
47
290
  async cancel(_taskId) {
48
- throw new Error('Factory cloud provider is not yet available.');
291
+ throw new Error('Cancel is not supported for Factory (Droid) droid exec runs synchronously to completion.');
49
292
  }
50
293
  async message(_taskId, _content) {
51
- throw new Error('Factory cloud provider is not yet available.');
294
+ throw new Error('Follow-up messages are not yet supported for Factory (Droid) cloud tasks.');
52
295
  }
53
296
  }
@@ -5,11 +5,27 @@
5
5
  * implementations, and exposes lookup helpers used by the `agents cloud` commands.
6
6
  */
7
7
  import type { CloudProvider, CloudProviderId } from './types.js';
8
+ /**
9
+ * The cloud provider an agent dispatches to by default. Reads the canonical
10
+ * `cloudProvider` field on the agent registry entry — one source of truth, no
11
+ * side map. Returns undefined for agents with no native cloud.
12
+ */
13
+ export declare function nativeProviderForAgent(agentId: string): CloudProviderId | undefined;
8
14
  /** Look up a provider by ID, throwing if the ID is unknown. */
9
15
  export declare function getProvider(id: CloudProviderId): CloudProvider;
10
16
  /** Return the user's configured default provider, falling back to 'rush'. */
11
17
  export declare function getDefaultProviderId(): CloudProviderId;
12
18
  /** Return every registered provider (used by `agents cloud providers`). */
13
19
  export declare function getAllProviders(): CloudProvider[];
14
- /** Resolve the active provider from an explicit flag or the configured default. */
15
- export declare function resolveProvider(explicit?: string): CloudProvider;
20
+ /**
21
+ * Resolve the active provider for a dispatch.
22
+ *
23
+ * Precedence: explicit `--provider` > the agent's native cloud
24
+ * (`cloudProvider`) > configured `cloud.default_provider` > `rush`. This is
25
+ * what makes `agents cloud run --agent droid` land on Factory and
26
+ * `--agent codex` land on Codex Cloud without the user naming a provider.
27
+ *
28
+ * Callers that already hold a concrete provider id (e.g. resolving a stored
29
+ * task's provider) pass it as `explicit` and the agent arg is ignored.
30
+ */
31
+ export declare function resolveProvider(explicit?: string, agentId?: string): CloudProvider;
@@ -10,7 +10,9 @@ import * as yaml from 'yaml';
10
10
  import { RushCloudProvider } from './rush.js';
11
11
  import { CodexCloudProvider } from './codex.js';
12
12
  import { FactoryCloudProvider } from './factory.js';
13
+ import { AntigravityCloudProvider } from './antigravity.js';
13
14
  import { getUserAgentsDir } from '../state.js';
15
+ import { AGENTS } from '../agents.js';
14
16
  const META_FILE = path.join(getUserAgentsDir(), 'agents.yaml');
15
17
  let _config = null;
16
18
  /** Parse the `cloud` section from agents.yaml, caching the result for the process lifetime. */
@@ -39,7 +41,17 @@ function initProviders() {
39
41
  const config = loadCloudConfig();
40
42
  providers.set('rush', new RushCloudProvider());
41
43
  providers.set('codex', new CodexCloudProvider(config.providers?.codex));
42
- providers.set('factory', new FactoryCloudProvider());
44
+ providers.set('factory', new FactoryCloudProvider(config.providers?.factory));
45
+ providers.set('antigravity', new AntigravityCloudProvider(config.providers?.antigravity));
46
+ }
47
+ /**
48
+ * The cloud provider an agent dispatches to by default. Reads the canonical
49
+ * `cloudProvider` field on the agent registry entry — one source of truth, no
50
+ * side map. Returns undefined for agents with no native cloud.
51
+ */
52
+ export function nativeProviderForAgent(agentId) {
53
+ const agent = AGENTS[agentId];
54
+ return agent?.cloudProvider;
43
55
  }
44
56
  /** Look up a provider by ID, throwing if the ID is unknown. */
45
57
  export function getProvider(id) {
@@ -60,8 +72,20 @@ export function getAllProviders() {
60
72
  initProviders();
61
73
  return [...providers.values()];
62
74
  }
63
- /** Resolve the active provider from an explicit flag or the configured default. */
64
- export function resolveProvider(explicit) {
65
- const id = (explicit ?? getDefaultProviderId());
75
+ /**
76
+ * Resolve the active provider for a dispatch.
77
+ *
78
+ * Precedence: explicit `--provider` > the agent's native cloud
79
+ * (`cloudProvider`) > configured `cloud.default_provider` > `rush`. This is
80
+ * what makes `agents cloud run --agent droid` land on Factory and
81
+ * `--agent codex` land on Codex Cloud without the user naming a provider.
82
+ *
83
+ * Callers that already hold a concrete provider id (e.g. resolving a stored
84
+ * task's provider) pass it as `explicit` and the agent arg is ignored.
85
+ */
86
+ export function resolveProvider(explicit, agentId) {
87
+ const id = (explicit
88
+ ?? (agentId ? nativeProviderForAgent(agentId) : undefined)
89
+ ?? getDefaultProviderId());
66
90
  return getProvider(id);
67
91
  }
@@ -5,8 +5,19 @@
5
5
  * Factory) implement, plus the shared task and event types that flow through
6
6
  * the dispatch pipeline.
7
7
  */
8
- /** Identifier for a supported cloud dispatch backend. */
9
- export type CloudProviderId = 'rush' | 'codex' | 'factory';
8
+ /**
9
+ * Identifier for a supported cloud dispatch backend.
10
+ *
11
+ * Each id is one agent's *own* cloud:
12
+ * - `rush` — Rush Cloud (runs Claude against a GitHub repo → PR)
13
+ * - `codex` — OpenAI Codex Cloud (`codex cloud exec`)
14
+ * - `factory` — Factory Droid Computers (`droid computer ssh` + remote `droid exec`)
15
+ * - `antigravity` — Google Gemini Managed Agents (Interactions API)
16
+ *
17
+ * Agents route to their native cloud automatically (see `cloudProvider` in the
18
+ * agent registry); `--provider` overrides.
19
+ */
20
+ export type CloudProviderId = 'rush' | 'codex' | 'factory' | 'antigravity';
10
21
  /**
11
22
  * Lifecycle state of a cloud-dispatched task.
12
23
  *
@@ -188,14 +199,33 @@ export interface CloudProvider {
188
199
  /** Send a follow-up message to a finished/idle/needs_review task. */
189
200
  message(taskId: string, content: string): Promise<void>;
190
201
  }
202
+ /** Autonomy level passed to `droid exec --auto` for Factory cloud dispatches. */
203
+ export type DroidAutonomy = 'low' | 'medium' | 'high';
191
204
  /** Per-provider configuration stored in the `cloud.providers` section of agents.yaml. */
192
205
  export interface CloudProviderConfig {
193
206
  rush?: Record<string, string>;
194
207
  codex?: {
195
208
  env?: string;
196
209
  };
210
+ /**
211
+ * Factory (Droid) cloud. `computer` is the pre-provisioned Droid Computer
212
+ * name (managed in Factory's UI, or BYOM via `droid computer register`) —
213
+ * the Factory analogue of Codex's pre-built `env`. `autonomy` is the default
214
+ * `droid exec --auto` level for cloud runs (defaults to `high`).
215
+ */
197
216
  factory?: {
198
217
  computer?: string;
218
+ autonomy?: DroidAutonomy;
219
+ };
220
+ /**
221
+ * Antigravity (Gemini Managed Agents) cloud. The Gemini API key is read from
222
+ * an `agents secrets` bundle named here (never stored in agents.yaml); if
223
+ * unset, the provider falls back to GEMINI_API_KEY / GOOGLE_API_KEY in the
224
+ * environment. `model` overrides the default managed-agent id.
225
+ */
226
+ antigravity?: {
227
+ secretsBundle?: string;
228
+ model?: string;
199
229
  };
200
230
  }
201
231
  /** Top-level `cloud` section of agents.yaml. */
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Install + lifecycle for the macOS menu-bar helper (`MenubarHelper.app`).
3
+ *
4
+ * Mirrors `src/lib/secrets/install-helper.ts` (stable Application Support path,
5
+ * survives npm re-sign) and the secrets-agent launchd pattern in
6
+ * `src/lib/secrets/agent.ts` (RunAtLoad + KeepAlive user service).
7
+ *
8
+ * The helper is a no-Dock `.accessory` status-bar app. It reads live agent
9
+ * state directly from disk and shells `agents` only for actions, so the plist
10
+ * bakes in the node interpreter + entry point + bin path so the GUI process can
11
+ * find the CLI without a login PATH.
12
+ *
13
+ * Opt-out is sticky: `agents menubar disable` drops a sentinel that the upgrade
14
+ * migration (`installMenubarLaunchAgent` in migrate.ts) honors, so a disabled
15
+ * menu bar never silently comes back on the next release.
16
+ */
17
+ /** True if the user explicitly disabled the menu bar (don't auto-enable on upgrade). */
18
+ export declare function menubarDisabledByUser(): boolean;
19
+ /** True if the launchd plist for the menu-bar service is installed. */
20
+ export declare function menubarServiceInstalled(): boolean;
21
+ /**
22
+ * Copy the bundled `.app` to the stable user path (idempotent unless forced).
23
+ * Returns the installed executable path, or null if no source bundle ships
24
+ * with this install (e.g. Linux package, or a build without the helper).
25
+ */
26
+ export declare function ensureMenubarAppInstalled(opts?: {
27
+ forceReinstall?: boolean;
28
+ }): string | null;
29
+ /**
30
+ * Install + start the menu-bar helper as a launchd user service (idempotent).
31
+ * Clears the sticky opt-out, installs the .app, writes the plist, and
32
+ * bootstraps it into the GUI domain. Returns false on non-darwin or when no
33
+ * helper bundle ships with this install.
34
+ */
35
+ export declare function enableMenubarService(opts?: {
36
+ clearOptOut?: boolean;
37
+ }): boolean;
38
+ /**
39
+ * Stop + remove the menu-bar service and write the sticky opt-out so the
40
+ * upgrade migration won't re-enable it.
41
+ */
42
+ export declare function disableMenubarService(): void;
43
+ /**
44
+ * Upgrade-time auto-enable. Runs from runMigration() once per sentinel bump.
45
+ * No-ops if: not darwin, the user opted out, no helper bundle ships, or the
46
+ * service is already installed. Best-effort — never throws into migration.
47
+ */
48
+ export declare function installMenubarLaunchAgentOnUpgrade(): void;
49
+ export interface MenubarStatus {
50
+ platform: string;
51
+ source: string | null;
52
+ installedApp: string | null;
53
+ serviceInstalled: boolean;
54
+ running: boolean;
55
+ disabledByUser: boolean;
56
+ }
57
+ export declare function getMenubarStatus(): MenubarStatus;