@ory/argus 0.11.0 → 0.12.0
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/README.md +1 -1
- package/dist/agent-auth.d.ts +23 -0
- package/dist/agent-auth.js +42 -1
- package/dist/auth.js +76 -3
- package/dist/cli-invocation.d.ts +34 -0
- package/dist/cli-invocation.js +60 -0
- package/dist/cli.d.ts +15 -5
- package/dist/cli.js +161 -53
- package/dist/client.d.ts +27 -0
- package/dist/client.js +85 -5
- package/dist/config.d.ts +61 -0
- package/dist/config.js +81 -5
- package/dist/dev.d.ts +8 -0
- package/dist/dev.js +12 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.js +17 -3
- package/dist/interactive-setup.d.ts +123 -0
- package/dist/interactive-setup.js +1203 -0
- package/dist/local/cli.js +12 -11
- package/dist/local/index.d.ts +1 -1
- package/dist/local/index.js +3 -1
- package/dist/local/manager.js +1 -1
- package/dist/permissions-cli.js +55 -19
- package/dist/registry/config.js +7 -0
- package/dist/setup.d.ts +43 -3
- package/dist/setup.js +116 -23
- package/dist/status-cli.d.ts +2 -1
- package/dist/status-cli.js +34 -13
- package/dist/subject.d.ts +8 -5
- package/dist/subject.js +9 -6
- package/dist/tool-catalog.d.ts +2 -2
- package/dist/tool-catalog.js +14 -6
- package/dist/ui.d.ts +144 -0
- package/dist/ui.js +270 -0
- package/dist/uninstall.d.ts +47 -0
- package/dist/uninstall.js +90 -0
- package/dist/user-login.d.ts +5 -3
- package/dist/user-login.js +13 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Ory Argus: Agent and Developer Experience
|
|
2
2
|
|
|
3
|
-
The core API behind every Ory Agent Plugin and Extension. Argus wraps [Ory Identities](https://www.ory.com/ory-ecosystem), [Ory Permissions](https://ory.com/permissions), MCP authorization, and distributed tracing into a single client. Each
|
|
3
|
+
The core API behind every Ory Agent Plugin and Extension. Argus wraps [Ory Identities](https://www.ory.com/ory-ecosystem), [Ory Permissions](https://ory.com/permissions), MCP authorization, and distributed tracing into a single client. Each harness package (`@ory/claude-code`, `@ory/codex`, `@ory/gemini-cli`, and the rest — see the [root README](../../README.md) for the full list) is a thin adapter that maps one harness's hook contract onto Argus.
|
|
4
4
|
|
|
5
5
|
Argus is also published on its own so you can build new harness plugins or extensions, embed Ory into a custom agent runtime, or instrument any SDK that exposes event lifecycle hooks for session start, tool execution, and tool completion.
|
|
6
6
|
|
package/dist/agent-auth.d.ts
CHANGED
|
@@ -88,6 +88,29 @@ export declare function loadAgentDynamicCredentials(): OryAgentDynamicCredential
|
|
|
88
88
|
export declare function saveAgentDynamicCredentials(creds: OryAgentDynamicCredentials): void;
|
|
89
89
|
/** Remove persisted DCR credentials (e.g. after revocation or unregister). */
|
|
90
90
|
export declare function clearAgentDynamicCredentials(): void;
|
|
91
|
+
/** Outcome of a best-effort RFC 7592 client revocation. */
|
|
92
|
+
export interface RevokeAgentClientResult {
|
|
93
|
+
/**
|
|
94
|
+
* `deleted` — the server-side client was removed (or was already gone);
|
|
95
|
+
* `skipped` — no management credentials were persisted, so we couldn't
|
|
96
|
+
* try; `failed` — the DELETE was attempted but the server rejected it.
|
|
97
|
+
*/
|
|
98
|
+
status: "deleted" | "skipped" | "failed";
|
|
99
|
+
/** HTTP status when the server responded. */
|
|
100
|
+
httpStatus?: number;
|
|
101
|
+
/** Human-readable detail for warnings (present on `skipped` / `failed`). */
|
|
102
|
+
message?: string;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Best-effort RFC 7592 `DELETE` of a dynamically-registered OAuth2 client.
|
|
106
|
+
*
|
|
107
|
+
* Requires the registration management URI and access token to have been
|
|
108
|
+
* persisted at registration time; otherwise returns `{ status: "skipped" }`.
|
|
109
|
+
* Never throws — a missing client (404) counts as `deleted`, and any other
|
|
110
|
+
* network/HTTP failure is captured in the result so callers can warn and
|
|
111
|
+
* still clear local state.
|
|
112
|
+
*/
|
|
113
|
+
export declare function revokeAgentDynamicClient(creds: OryAgentDynamicCredentials): Promise<RevokeAgentClientResult>;
|
|
91
114
|
export interface RegisterAgentClientArgs {
|
|
92
115
|
projectUrl: string;
|
|
93
116
|
/** Bearer used as the initial access token (RFC 7591 §3). */
|
package/dist/agent-auth.js
CHANGED
|
@@ -36,6 +36,7 @@ exports.clearSubAgentDynamicCredentials = clearSubAgentDynamicCredentials;
|
|
|
36
36
|
exports.loadAgentDynamicCredentials = loadAgentDynamicCredentials;
|
|
37
37
|
exports.saveAgentDynamicCredentials = saveAgentDynamicCredentials;
|
|
38
38
|
exports.clearAgentDynamicCredentials = clearAgentDynamicCredentials;
|
|
39
|
+
exports.revokeAgentDynamicClient = revokeAgentDynamicClient;
|
|
39
40
|
exports.registerAgentClient = registerAgentClient;
|
|
40
41
|
exports.resolveAgentCredentials = resolveAgentCredentials;
|
|
41
42
|
exports.fetchClientCredentialsToken = fetchClientCredentialsToken;
|
|
@@ -116,6 +117,40 @@ function clearAgentDynamicCredentials() {
|
|
|
116
117
|
return next;
|
|
117
118
|
});
|
|
118
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* Best-effort RFC 7592 `DELETE` of a dynamically-registered OAuth2 client.
|
|
122
|
+
*
|
|
123
|
+
* Requires the registration management URI and access token to have been
|
|
124
|
+
* persisted at registration time; otherwise returns `{ status: "skipped" }`.
|
|
125
|
+
* Never throws — a missing client (404) counts as `deleted`, and any other
|
|
126
|
+
* network/HTTP failure is captured in the result so callers can warn and
|
|
127
|
+
* still clear local state.
|
|
128
|
+
*/
|
|
129
|
+
async function revokeAgentDynamicClient(creds) {
|
|
130
|
+
if (!creds.registrationClientUri || !creds.registrationAccessToken) {
|
|
131
|
+
return {
|
|
132
|
+
status: "skipped",
|
|
133
|
+
message: "no registration_access_token persisted",
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
const oidc = new client_1.OidcApi(new client_1.Configuration({
|
|
137
|
+
basePath: creds.projectUrl,
|
|
138
|
+
accessToken: creds.registrationAccessToken,
|
|
139
|
+
}));
|
|
140
|
+
try {
|
|
141
|
+
await oidc.deleteOidcDynamicClient({ id: creds.clientId });
|
|
142
|
+
return { status: "deleted" };
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
const httpStatus = err
|
|
146
|
+
?.response?.status;
|
|
147
|
+
// Already gone server-side — treat as success.
|
|
148
|
+
if (httpStatus === 404)
|
|
149
|
+
return { status: "deleted", httpStatus };
|
|
150
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
151
|
+
return { status: "failed", httpStatus, message };
|
|
152
|
+
}
|
|
153
|
+
}
|
|
119
154
|
/**
|
|
120
155
|
* Register a new OAuth2 client via RFC 7591. Authorization is carried
|
|
121
156
|
* either by the user's interactive token (the natural bootstrap) or by
|
|
@@ -144,7 +179,13 @@ async function registerAgentClient(args) {
|
|
|
144
179
|
scope: "openid offline_access",
|
|
145
180
|
token_endpoint_auth_method: "client_secret_post",
|
|
146
181
|
},
|
|
147
|
-
}
|
|
182
|
+
},
|
|
183
|
+
// The generated SDK declares no security scheme for the public DCR create
|
|
184
|
+
// operation (`/oauth2/register`), so it never attaches the bearer from the
|
|
185
|
+
// Configuration's `accessToken`. Ory projects that gate DCR behind an
|
|
186
|
+
// initial access token then reject the unauthenticated request and the
|
|
187
|
+
// agent never gets credentials. Send the bearer explicitly.
|
|
188
|
+
args.auth ? { headers: { Authorization: `Bearer ${args.auth}` } } : undefined);
|
|
148
189
|
}
|
|
149
190
|
catch (err) {
|
|
150
191
|
const status = err?.response?.status;
|
package/dist/auth.js
CHANGED
|
@@ -103,6 +103,10 @@ async function pkceLogin(options) {
|
|
|
103
103
|
server.close();
|
|
104
104
|
if (result.kind !== "ok")
|
|
105
105
|
return result;
|
|
106
|
+
// Mirror the browser success page on the terminal so it's unambiguous the
|
|
107
|
+
// flow finished and the browser tab can be closed — the user's attention is
|
|
108
|
+
// often still on the browser at this point.
|
|
109
|
+
process.stderr.write("Signed in. You can close the browser tab — continuing here…\n\n");
|
|
106
110
|
try {
|
|
107
111
|
const tokens = await exchangeCodeForTokens({
|
|
108
112
|
projectUrl: options.projectUrl,
|
|
@@ -203,18 +207,18 @@ function waitForCallback(server, opts) {
|
|
|
203
207
|
}
|
|
204
208
|
const error = url.searchParams.get("error");
|
|
205
209
|
if (error) {
|
|
206
|
-
|
|
210
|
+
respondHtml(res, 400, resultPage("cancelled"));
|
|
207
211
|
settle({ kind: "declined", reason: "user_denied" });
|
|
208
212
|
return;
|
|
209
213
|
}
|
|
210
214
|
const state = url.searchParams.get("state");
|
|
211
215
|
const code = url.searchParams.get("code");
|
|
212
216
|
if (!code || state !== opts.expectedState) {
|
|
213
|
-
|
|
217
|
+
respondHtml(res, 400, resultPage("mismatch"));
|
|
214
218
|
settle({ kind: "declined", reason: "state_mismatch" });
|
|
215
219
|
return;
|
|
216
220
|
}
|
|
217
|
-
|
|
221
|
+
respondHtml(res, 200, resultPage("success"));
|
|
218
222
|
settle({ kind: "ok", code });
|
|
219
223
|
};
|
|
220
224
|
server.on("request", onRequest);
|
|
@@ -230,6 +234,75 @@ function respond(res, status, body) {
|
|
|
230
234
|
res.writeHead(status, { "content-type": "text/plain; charset=utf-8" });
|
|
231
235
|
res.end(body + "\n");
|
|
232
236
|
}
|
|
237
|
+
function respondHtml(res, status, html) {
|
|
238
|
+
res.writeHead(status, { "content-type": "text/html; charset=utf-8" });
|
|
239
|
+
res.end(html);
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* The page the browser lands on after the OAuth2 redirect. The whole point is
|
|
243
|
+
* to make the next action unambiguous: on success it tells the user, in plain
|
|
244
|
+
* words, that they can close the tab and return to the terminal (the #1
|
|
245
|
+
* source of "is it done? do I close this?" confusion). Self-contained HTML —
|
|
246
|
+
* inline styles only, Ory-branded (Indigo #4F46E5), no external requests — so
|
|
247
|
+
* it renders identically offline and on the loopback origin.
|
|
248
|
+
*/
|
|
249
|
+
function resultPage(kind) {
|
|
250
|
+
const view = {
|
|
251
|
+
success: {
|
|
252
|
+
accent: "#4F46E5", // Ory Indigo
|
|
253
|
+
icon: "✓", // ✓
|
|
254
|
+
title: "Signed in to Ory",
|
|
255
|
+
body: "You can <strong>close this tab</strong> and return to your terminal — setup continues automatically.",
|
|
256
|
+
},
|
|
257
|
+
cancelled: {
|
|
258
|
+
accent: "#F43F5E", // Ory Rose
|
|
259
|
+
icon: "✕", // ✕
|
|
260
|
+
title: "Sign-in cancelled",
|
|
261
|
+
body: "No changes were made. <strong>Close this tab</strong> and return to your terminal to try again.",
|
|
262
|
+
},
|
|
263
|
+
mismatch: {
|
|
264
|
+
accent: "#F43F5E",
|
|
265
|
+
icon: "!",
|
|
266
|
+
title: "Sign-in could not be verified",
|
|
267
|
+
body: "The login response failed a security check. <strong>Close this tab</strong> and re-run the login from your terminal.",
|
|
268
|
+
},
|
|
269
|
+
}[kind];
|
|
270
|
+
return `<!doctype html>
|
|
271
|
+
<html lang="en">
|
|
272
|
+
<head>
|
|
273
|
+
<meta charset="utf-8">
|
|
274
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
275
|
+
<title>${view.title}</title>
|
|
276
|
+
<style>
|
|
277
|
+
:root { color-scheme: light dark; }
|
|
278
|
+
html, body { height: 100%; margin: 0; }
|
|
279
|
+
body {
|
|
280
|
+
display: flex; align-items: center; justify-content: center;
|
|
281
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
282
|
+
background: #0F172A; color: #E2E8F0;
|
|
283
|
+
}
|
|
284
|
+
.card { max-width: 26rem; padding: 2.5rem 2rem; text-align: center; }
|
|
285
|
+
.badge {
|
|
286
|
+
width: 4rem; height: 4rem; border-radius: 9999px; margin: 0 auto 1.25rem;
|
|
287
|
+
display: flex; align-items: center; justify-content: center;
|
|
288
|
+
font-size: 2rem; line-height: 1; font-weight: 700;
|
|
289
|
+
background: ${view.accent}22; color: ${view.accent};
|
|
290
|
+
}
|
|
291
|
+
h1 { font-size: 1.35rem; margin: 0 0 .5rem; color: #F8FAFC; }
|
|
292
|
+
p { margin: 0; line-height: 1.55; color: #94A3B8; }
|
|
293
|
+
strong { color: #E2E8F0; font-weight: 600; }
|
|
294
|
+
</style>
|
|
295
|
+
</head>
|
|
296
|
+
<body>
|
|
297
|
+
<div class="card">
|
|
298
|
+
<div class="badge">${view.icon}</div>
|
|
299
|
+
<h1>${view.title}</h1>
|
|
300
|
+
<p>${view.body}</p>
|
|
301
|
+
</div>
|
|
302
|
+
</body>
|
|
303
|
+
</html>
|
|
304
|
+
`;
|
|
305
|
+
}
|
|
233
306
|
// ─── Browser launch ───────────────────────────────────────────────────
|
|
234
307
|
async function launchBrowser(url, override) {
|
|
235
308
|
if (override) {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper for rendering copy-pasteable `npx` invocations in CLI banners,
|
|
3
|
+
* help text, and prompts.
|
|
4
|
+
*
|
|
5
|
+
* Every harness plugin ships a CLI bin named `ory-<short>` (e.g.
|
|
6
|
+
* `ory-claude`, `ory-gemini`). Those bins only resolve on `PATH` when the
|
|
7
|
+
* package is already installed. In the common install flow the user runs
|
|
8
|
+
* `npx @ory/<harness> install` — a one-shot npx invocation that leaves
|
|
9
|
+
* nothing on `PATH` — so a follow-up banner that says `npx ory-claude
|
|
10
|
+
* permissions bootstrap` fails: npm can't find a package literally named
|
|
11
|
+
* `ory-claude` and errors out.
|
|
12
|
+
*
|
|
13
|
+
* The always-resolvable form is `npx -y -p @ory/<harness> ory-<short>
|
|
14
|
+
* <args>`, which is exactly what the READMEs document. {@link oryNpx}
|
|
15
|
+
* renders that prefix for a given bin name so every banner is
|
|
16
|
+
* copy-paste-safe from a clean shell.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Render a copy-pasteable `npx` invocation prefix for a harness bin.
|
|
20
|
+
*
|
|
21
|
+
* Returns `npx -y -p <package> <binName>` when the bin's package is
|
|
22
|
+
* known, so the command resolves from a clean shell. Each package ships
|
|
23
|
+
* sibling `-setup` and `-hook` bins alongside its main `ory-<short>` bin;
|
|
24
|
+
* those normalize to the same package. Falls back to the bare `npx
|
|
25
|
+
* <binName>` for unknown bins (preserving prior behavior) — that only
|
|
26
|
+
* resolves when the bin is already on `PATH`, so keep
|
|
27
|
+
* {@link BIN_TO_PACKAGE} in sync with the shipped bins.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* oryNpx("ory-claude") // "npx -y -p @ory/claude-code ory-claude"
|
|
31
|
+
* oryNpx("ory-claude-setup") // "npx -y -p @ory/claude-code ory-claude-setup"
|
|
32
|
+
* `${oryNpx("ory-claude")} permissions status`
|
|
33
|
+
*/
|
|
34
|
+
export declare function oryNpx(binName: string): string;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Helper for rendering copy-pasteable `npx` invocations in CLI banners,
|
|
4
|
+
* help text, and prompts.
|
|
5
|
+
*
|
|
6
|
+
* Every harness plugin ships a CLI bin named `ory-<short>` (e.g.
|
|
7
|
+
* `ory-claude`, `ory-gemini`). Those bins only resolve on `PATH` when the
|
|
8
|
+
* package is already installed. In the common install flow the user runs
|
|
9
|
+
* `npx @ory/<harness> install` — a one-shot npx invocation that leaves
|
|
10
|
+
* nothing on `PATH` — so a follow-up banner that says `npx ory-claude
|
|
11
|
+
* permissions bootstrap` fails: npm can't find a package literally named
|
|
12
|
+
* `ory-claude` and errors out.
|
|
13
|
+
*
|
|
14
|
+
* The always-resolvable form is `npx -y -p @ory/<harness> ory-<short>
|
|
15
|
+
* <args>`, which is exactly what the READMEs document. {@link oryNpx}
|
|
16
|
+
* renders that prefix for a given bin name so every banner is
|
|
17
|
+
* copy-paste-safe from a clean shell.
|
|
18
|
+
*/
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.oryNpx = oryNpx;
|
|
21
|
+
/**
|
|
22
|
+
* Maps each harness CLI bin name to its npm package. Keyed by the bin
|
|
23
|
+
* name callers pass into the shared CLI helpers (always `ory-<short>`).
|
|
24
|
+
* When adding a new harness plugin, add its bin here so its banners emit
|
|
25
|
+
* a resolvable command.
|
|
26
|
+
*/
|
|
27
|
+
const BIN_TO_PACKAGE = {
|
|
28
|
+
"ory-claude": "@ory/claude-code",
|
|
29
|
+
"ory-codex": "@ory/codex",
|
|
30
|
+
"ory-gemini": "@ory/gemini-cli",
|
|
31
|
+
"ory-openclaw": "@ory/openclaw",
|
|
32
|
+
"ory-opencode": "@ory/opencode",
|
|
33
|
+
"ory-continue": "@ory/continue",
|
|
34
|
+
"ory-goose": "@ory/goose",
|
|
35
|
+
"ory-cline": "@ory/cline",
|
|
36
|
+
"ory-amp": "@ory/amp",
|
|
37
|
+
"ory-pi": "@ory/pi",
|
|
38
|
+
"ory-antigravity": "@ory/antigravity",
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Render a copy-pasteable `npx` invocation prefix for a harness bin.
|
|
42
|
+
*
|
|
43
|
+
* Returns `npx -y -p <package> <binName>` when the bin's package is
|
|
44
|
+
* known, so the command resolves from a clean shell. Each package ships
|
|
45
|
+
* sibling `-setup` and `-hook` bins alongside its main `ory-<short>` bin;
|
|
46
|
+
* those normalize to the same package. Falls back to the bare `npx
|
|
47
|
+
* <binName>` for unknown bins (preserving prior behavior) — that only
|
|
48
|
+
* resolves when the bin is already on `PATH`, so keep
|
|
49
|
+
* {@link BIN_TO_PACKAGE} in sync with the shipped bins.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* oryNpx("ory-claude") // "npx -y -p @ory/claude-code ory-claude"
|
|
53
|
+
* oryNpx("ory-claude-setup") // "npx -y -p @ory/claude-code ory-claude-setup"
|
|
54
|
+
* `${oryNpx("ory-claude")} permissions status`
|
|
55
|
+
*/
|
|
56
|
+
function oryNpx(binName) {
|
|
57
|
+
const base = binName.replace(/-(setup|hook)$/, "");
|
|
58
|
+
const pkg = BIN_TO_PACKAGE[base];
|
|
59
|
+
return pkg ? `npx -y -p ${pkg} ${binName}` : `npx ${binName}`;
|
|
60
|
+
}
|
package/dist/cli.d.ts
CHANGED
|
@@ -22,22 +22,32 @@ export declare function printOryConfig(): void;
|
|
|
22
22
|
*/
|
|
23
23
|
export declare function printEnvironment(): void;
|
|
24
24
|
/**
|
|
25
|
-
* Print the last 5 debug log entries
|
|
25
|
+
* Print the last 5 debug log entries. When `harness` is given the path is
|
|
26
|
+
* resolved the same way the plugin does at runtime — the default per-harness
|
|
27
|
+
* data-dir location when `ORY_AGENT_LOG_FILE` is unset — so `status` finds the
|
|
28
|
+
* log without the reader hand-setting env vars. The debug log only exists when
|
|
29
|
+
* a session ran with `ORY_AGENT_DEBUG=true`; when it doesn't, this prints a
|
|
30
|
+
* one-line pointer on how to turn it on rather than staying silent.
|
|
26
31
|
*/
|
|
27
|
-
export declare function printLogTail(): void;
|
|
32
|
+
export declare function printLogTail(harness?: string): void;
|
|
28
33
|
/**
|
|
29
34
|
* Print the "Configure your Ory credentials" help text.
|
|
30
35
|
*/
|
|
31
36
|
export declare function printEnvHelp(binName: string): void;
|
|
32
37
|
/**
|
|
33
|
-
* Print the last 5 trace
|
|
38
|
+
* Print the last 5 trace spans. When `harness` is given the path is resolved
|
|
39
|
+
* the same way the plugin does at runtime — the default per-harness data-dir
|
|
40
|
+
* location when `ORY_AGENT_TRACE_FILE` is unset — so `status` shows recent
|
|
41
|
+
* activity out of the box (traces are written on every recorded span). When no
|
|
42
|
+
* spans have been recorded yet, prints a one-line pointer at the trace file so
|
|
43
|
+
* the reader knows where to look (and how to watch it live).
|
|
34
44
|
*/
|
|
35
|
-
export declare function printTraceTail(): void;
|
|
45
|
+
export declare function printTraceTail(harness?: string): void;
|
|
36
46
|
/**
|
|
37
47
|
* Run the live trace watcher. Tails the NDJSON trace file and prints
|
|
38
48
|
* formatted spans to stdout as they arrive. Blocks until interrupted.
|
|
39
49
|
*/
|
|
40
|
-
export declare function runWatchCommand(args: string[]): void;
|
|
50
|
+
export declare function runWatchCommand(harness: string, args: string[]): void;
|
|
41
51
|
/**
|
|
42
52
|
* Returns true if `/dev/tty` can be opened for reading. Use this before
|
|
43
53
|
* attempting to prompt the user — the harness has already taken stdin so
|