webhands 0.0.0 → 0.1.6
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 +661 -0
- package/README.md +112 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +14 -0
- package/dist/bin.js.map +1 -0
- package/dist/cli.d.ts +79 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +639 -0
- package/dist/cli.js.map +1 -0
- package/dist/errors.d.ts +45 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +69 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/session-provider.d.ts +52 -0
- package/dist/session-provider.d.ts.map +1 -0
- package/dist/session-provider.js +28 -0
- package/dist/session-provider.js.map +1 -0
- package/dist/version.d.ts +11 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +12 -0
- package/dist/version.js.map +1 -0
- package/package.json +58 -2
- package/src/bin.ts +14 -0
- package/src/cli.ts +797 -0
- package/src/errors.ts +114 -0
- package/src/index.ts +36 -0
- package/src/session-provider.ts +70 -0
- package/src/version.ts +12 -0
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { MissingBrowserBinaryError, MissingProfileError, AttachNotChromiumError, AttachNoContextError, NoLiveServerError, SessionAlreadyActiveError, type ControllerError, type ControllerErrorCode } from '@webhands/core';
|
|
2
|
+
/**
|
|
3
|
+
* Map a TYPED `core` error condition into the user-facing message + the EXACT
|
|
4
|
+
* command to fix it (PRD story 17).
|
|
5
|
+
*
|
|
6
|
+
* `core` OWNS the typed conditions (a `ControllerError` with a stable `code`,
|
|
7
|
+
* raised by the transports — see `packages/core/src/errors.ts`); the CLI OWNS
|
|
8
|
+
* the user-facing message text. The CLI never re-DETECTS a missing binary or a
|
|
9
|
+
* missing profile (no second `stat`, no message-string match): it branches on
|
|
10
|
+
* the machine-readable `code` and composes the fix command from the structured
|
|
11
|
+
* context the error already carries (`browser`, `profile`, `profileDir`, ...).
|
|
12
|
+
*
|
|
13
|
+
* The result is fed to incur's `c.error({code, message, ...})` so the failure
|
|
14
|
+
* surfaces in the structured output envelope with the SAME `code` an agent can
|
|
15
|
+
* branch on, and a `message` whose final line is a copy-pasteable fix command.
|
|
16
|
+
*/
|
|
17
|
+
export interface MappedError {
|
|
18
|
+
/** The machine-readable `core` error code, surfaced unchanged to the agent. */
|
|
19
|
+
readonly code: ControllerErrorCode;
|
|
20
|
+
/** The full user-facing message, ending in the exact fix command. */
|
|
21
|
+
readonly message: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Compose the EXACT fix command for a typed `core` error. Returned alongside
|
|
25
|
+
* the message so a test can assert the precise command, and so the wording
|
|
26
|
+
* lives in ONE place. `binary` is the CLI binary name (`incur` passes it to the
|
|
27
|
+
* handler as `c.name`), so the suggested command always matches how the user
|
|
28
|
+
* invoked the tool.
|
|
29
|
+
*/
|
|
30
|
+
export declare function fixCommandFor(error: ControllerError, binary: string): string;
|
|
31
|
+
/**
|
|
32
|
+
* If `cause` is a typed `core` error, return the {@link MappedError} (the
|
|
33
|
+
* user-facing message with the exact fix command appended); otherwise return
|
|
34
|
+
* `undefined` so the caller falls back to the generic error path.
|
|
35
|
+
*
|
|
36
|
+
* The message is the typed error's own message (which already states the
|
|
37
|
+
* condition in domain terms) followed by a blank line and a `To fix, run:`
|
|
38
|
+
* block naming the exact command. We keep `core`'s message rather than
|
|
39
|
+
* re-author it, so the condition text has one source of truth; the CLI's job is
|
|
40
|
+
* only to ADD the actionable fix command.
|
|
41
|
+
*/
|
|
42
|
+
export declare function mapControllerError(cause: unknown, binary: string): MappedError | undefined;
|
|
43
|
+
export { MissingBrowserBinaryError, MissingProfileError, AttachNotChromiumError, AttachNoContextError, NoLiveServerError, SessionAlreadyActiveError, };
|
|
44
|
+
export type { ControllerError, ControllerErrorCode };
|
|
45
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,yBAAyB,EACzB,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,EACpB,iBAAiB,EACjB,yBAAyB,EACzB,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,MAAM,gBAAgB,CAAC;AAExB;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,WAAW;IAC3B,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IACnC,qEAAqE;IACrE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAmC5E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CACjC,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,MAAM,GACZ,WAAW,GAAG,SAAS,CASzB;AAID,OAAO,EACN,yBAAyB,EACzB,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,EACpB,iBAAiB,EACjB,yBAAyB,GACzB,CAAC;AACF,YAAY,EAAC,eAAe,EAAE,mBAAmB,EAAC,CAAC"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { isControllerError, MissingBrowserBinaryError, MissingProfileError, AttachNotChromiumError, AttachNoContextError, NoLiveServerError, SessionAlreadyActiveError, } from '@webhands/core';
|
|
2
|
+
/**
|
|
3
|
+
* Compose the EXACT fix command for a typed `core` error. Returned alongside
|
|
4
|
+
* the message so a test can assert the precise command, and so the wording
|
|
5
|
+
* lives in ONE place. `binary` is the CLI binary name (`incur` passes it to the
|
|
6
|
+
* handler as `c.name`), so the suggested command always matches how the user
|
|
7
|
+
* invoked the tool.
|
|
8
|
+
*/
|
|
9
|
+
export function fixCommandFor(error, binary) {
|
|
10
|
+
switch (error.code) {
|
|
11
|
+
case 'missing-browser-binary':
|
|
12
|
+
// Playwright ships its own browser binaries; `playwright install
|
|
13
|
+
// <browser>` is the documented way to download the missing one. We name
|
|
14
|
+
// the specific browser from the typed error rather than a generic hint.
|
|
15
|
+
return `npx playwright install ${error.browser}`;
|
|
16
|
+
case 'missing-profile':
|
|
17
|
+
// A profile is created by the headed `setup-profile` flow (the ONE place
|
|
18
|
+
// a profile dir is created — see core's MissingProfileError). Name the
|
|
19
|
+
// profile so the command is ready to run as-is.
|
|
20
|
+
return `${binary} setup-profile --profile ${error.profile}`;
|
|
21
|
+
case 'attach-not-chromium':
|
|
22
|
+
// attach is Chromium-only; the fix is to start Chromium/Chrome with a
|
|
23
|
+
// remote-debugging port and attach to THAT endpoint.
|
|
24
|
+
return `${binary} attach --endpoint http://127.0.0.1:9222 (start Chromium/Chrome with --remote-debugging-port=9222 first)`;
|
|
25
|
+
case 'attach-no-context':
|
|
26
|
+
// The reached browser has no window/tab to reuse; the fix is to open one.
|
|
27
|
+
return `${binary} attach --endpoint ${error.endpoint} (open a window/tab in that browser first)`;
|
|
28
|
+
case 'no-live-server':
|
|
29
|
+
// No long-lived session server is running (ADR-0005): a verb is a thin
|
|
30
|
+
// client and has nothing to drive. The fix is to bring one up FIRST with
|
|
31
|
+
// `serve`; we never auto-spawn a browser in v1.
|
|
32
|
+
return `${binary} serve`;
|
|
33
|
+
case 'session-already-active':
|
|
34
|
+
// A session is already live; v1 holds exactly one. The fix is to tear it
|
|
35
|
+
// down before starting another.
|
|
36
|
+
return `${binary} stop`;
|
|
37
|
+
default: {
|
|
38
|
+
// Exhaustiveness guard: a new ControllerErrorCode must add a fix command
|
|
39
|
+
// here rather than silently fall through to a generic message.
|
|
40
|
+
const _never = error.code;
|
|
41
|
+
return `Run \`${binary} --help\` (unhandled error code: ${String(_never)}).`;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* If `cause` is a typed `core` error, return the {@link MappedError} (the
|
|
47
|
+
* user-facing message with the exact fix command appended); otherwise return
|
|
48
|
+
* `undefined` so the caller falls back to the generic error path.
|
|
49
|
+
*
|
|
50
|
+
* The message is the typed error's own message (which already states the
|
|
51
|
+
* condition in domain terms) followed by a blank line and a `To fix, run:`
|
|
52
|
+
* block naming the exact command. We keep `core`'s message rather than
|
|
53
|
+
* re-author it, so the condition text has one source of truth; the CLI's job is
|
|
54
|
+
* only to ADD the actionable fix command.
|
|
55
|
+
*/
|
|
56
|
+
export function mapControllerError(cause, binary) {
|
|
57
|
+
if (!isControllerError(cause)) {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
const fix = fixCommandFor(cause, binary);
|
|
61
|
+
return {
|
|
62
|
+
code: cause.code,
|
|
63
|
+
message: `${cause.message}\n\nTo fix, run:\n ${fix}`,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
// Re-export the concrete classes so a test importing from the cli package can
|
|
67
|
+
// construct/assert against them without reaching into core directly.
|
|
68
|
+
export { MissingBrowserBinaryError, MissingProfileError, AttachNotChromiumError, AttachNoContextError, NoLiveServerError, SessionAlreadyActiveError, };
|
|
69
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,iBAAiB,EACjB,yBAAyB,EACzB,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,EACpB,iBAAiB,EACjB,yBAAyB,GAGzB,MAAM,gBAAgB,CAAC;AAwBxB;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,KAAsB,EAAE,MAAc;IACnE,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,wBAAwB;YAC5B,iEAAiE;YACjE,wEAAwE;YACxE,wEAAwE;YACxE,OAAO,0BAA2B,KAAmC,CAAC,OAAO,EAAE,CAAC;QACjF,KAAK,iBAAiB;YACrB,yEAAyE;YACzE,uEAAuE;YACvE,gDAAgD;YAChD,OAAO,GAAG,MAAM,4BAA6B,KAA6B,CAAC,OAAO,EAAE,CAAC;QACtF,KAAK,qBAAqB;YACzB,sEAAsE;YACtE,qDAAqD;YACrD,OAAO,GAAG,MAAM,0GAA0G,CAAC;QAC5H,KAAK,mBAAmB;YACvB,0EAA0E;YAC1E,OAAO,GAAG,MAAM,sBAAuB,KAA8B,CAAC,QAAQ,4CAA4C,CAAC;QAC5H,KAAK,gBAAgB;YACpB,uEAAuE;YACvE,yEAAyE;YACzE,gDAAgD;YAChD,OAAO,GAAG,MAAM,QAAQ,CAAC;QAC1B,KAAK,wBAAwB;YAC5B,yEAAyE;YACzE,gCAAgC;YAChC,OAAO,GAAG,MAAM,OAAO,CAAC;QACzB,OAAO,CAAC,CAAC,CAAC;YACT,yEAAyE;YACzE,+DAA+D;YAC/D,MAAM,MAAM,GAAU,KAAK,CAAC,IAAI,CAAC;YACjC,OAAO,SAAS,MAAM,oCAAoC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;QAC9E,CAAC;IACF,CAAC;AACF,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,kBAAkB,CACjC,KAAc,EACd,MAAc;IAEd,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/B,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,MAAM,GAAG,GAAG,aAAa,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACzC,OAAO;QACN,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,OAAO,EAAE,GAAG,KAAK,CAAC,OAAO,uBAAuB,GAAG,EAAE;KACrD,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,qEAAqE;AACrE,OAAO,EACN,yBAAyB,EACzB,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,EACpB,iBAAiB,EACjB,yBAAyB,GACzB,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Driver } from '@webhands/core';
|
|
2
|
+
/**
|
|
3
|
+
* The `incur`-based CLI wrapper around `core` (the `webhands`
|
|
4
|
+
* binary). It binds ONE `incur` command per verb (`goto`, `snapshot`, `click`,
|
|
5
|
+
* `type`, `eval`, `wait`, `cookies`) plus `setup-profile`/`launch`/`attach`,
|
|
6
|
+
* each with a zod `args`/`options`/`output` schema, returns the structured
|
|
7
|
+
* TOON/JSON envelope with `cta` next-verb hints, and maps `core`'s typed
|
|
8
|
+
* missing-binary / missing-profile errors to an actionable fix command.
|
|
9
|
+
*
|
|
10
|
+
* Because it is built on `incur`, the same binary is ALSO an MCP server
|
|
11
|
+
* (`--mcp` / `mcp add`) and emits a skills / `--llms` manifest with no bespoke
|
|
12
|
+
* MCP code. The executable entry (`bin.ts`) calls `.serve()`; this module
|
|
13
|
+
* exports the builder + its types so a test (or a host) can drive the CLI
|
|
14
|
+
* programmatically (`createCli().serve(argv, {stdout, exit})` / `cli.fetch`).
|
|
15
|
+
*/
|
|
16
|
+
export { createCli, CLI_NAME, DEFAULT_PROFILE, type CliDeps, type ServeSession, } from './cli.js';
|
|
17
|
+
export { createDefaultSessionProvider, type SessionProvider, type DefaultSessionProviderOptions, } from './session-provider.js';
|
|
18
|
+
export { mapControllerError, fixCommandFor, type MappedError } from './errors.js';
|
|
19
|
+
export type { Driver };
|
|
20
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,gBAAgB,CAAC;AAE3C;;;;;;;;;;;;;GAaG;AAEH,OAAO,EACN,SAAS,EACT,QAAQ,EACR,eAAe,EACf,KAAK,OAAO,EACZ,KAAK,YAAY,GACjB,MAAM,UAAU,CAAC;AAElB,OAAO,EACN,4BAA4B,EAC5B,KAAK,eAAe,EACpB,KAAK,6BAA6B,GAClC,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAC,kBAAkB,EAAE,aAAa,EAAE,KAAK,WAAW,EAAC,MAAM,aAAa,CAAC;AAIhF,YAAY,EAAC,MAAM,EAAC,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `incur`-based CLI wrapper around `core` (the `webhands`
|
|
3
|
+
* binary). It binds ONE `incur` command per verb (`goto`, `snapshot`, `click`,
|
|
4
|
+
* `type`, `eval`, `wait`, `cookies`) plus `setup-profile`/`launch`/`attach`,
|
|
5
|
+
* each with a zod `args`/`options`/`output` schema, returns the structured
|
|
6
|
+
* TOON/JSON envelope with `cta` next-verb hints, and maps `core`'s typed
|
|
7
|
+
* missing-binary / missing-profile errors to an actionable fix command.
|
|
8
|
+
*
|
|
9
|
+
* Because it is built on `incur`, the same binary is ALSO an MCP server
|
|
10
|
+
* (`--mcp` / `mcp add`) and emits a skills / `--llms` manifest with no bespoke
|
|
11
|
+
* MCP code. The executable entry (`bin.ts`) calls `.serve()`; this module
|
|
12
|
+
* exports the builder + its types so a test (or a host) can drive the CLI
|
|
13
|
+
* programmatically (`createCli().serve(argv, {stdout, exit})` / `cli.fetch`).
|
|
14
|
+
*/
|
|
15
|
+
export { createCli, CLI_NAME, DEFAULT_PROFILE, } from './cli.js';
|
|
16
|
+
export { createDefaultSessionProvider, } from './session-provider.js';
|
|
17
|
+
export { mapControllerError, fixCommandFor } from './errors.js';
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EACN,SAAS,EACT,QAAQ,EACR,eAAe,GAGf,MAAM,UAAU,CAAC;AAElB,OAAO,EACN,4BAA4B,GAG5B,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAC,kBAAkB,EAAE,aAAa,EAAmB,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type OpenTarget, type Session } from '@webhands/core';
|
|
2
|
+
/**
|
|
3
|
+
* How a CLI verb command obtains a live {@link Session} to run against.
|
|
4
|
+
*
|
|
5
|
+
* This is the ONE seam the verb commands use to reach a browser. It is
|
|
6
|
+
* deliberately a single function (open a session for an {@link OpenTarget})
|
|
7
|
+
* rather than the transports directly, for two reasons:
|
|
8
|
+
*
|
|
9
|
+
* 1. **Testability of the WIRING.** CLI-level tests assert incur wiring
|
|
10
|
+
* (schemas, output envelope, cta, manifest, error text), NOT verb behaviour
|
|
11
|
+
* (that is covered at the `core` seam). A test injects a provider backed by
|
|
12
|
+
* the `core` `StubTransport`, so the command surface can be exercised with no
|
|
13
|
+
* real browser, and a test can inject a provider that THROWS the typed
|
|
14
|
+
* `core` errors to assert the actionable fix-command messages.
|
|
15
|
+
*
|
|
16
|
+
* 2. **The cross-invocation persistence swap point.** Per ADR-0005 a single
|
|
17
|
+
* browser is kept alive between separate CLI invocations behind a long-lived
|
|
18
|
+
* `serve` process; verb commands are THIN CLIENTS of that server. The default
|
|
19
|
+
* provider (below) is now exactly that thin client: it discovers the running
|
|
20
|
+
* server via the endpoint file and returns a {@link connectRemoteSession}
|
|
21
|
+
* proxy that drives the server's already-live page; when NO server is live it
|
|
22
|
+
* raises a typed {@link NoLiveServerError} so the CLI prints "run `serve`
|
|
23
|
+
* first" and exits non-zero, never auto-spawning a browser (ADR-0005:
|
|
24
|
+
* lifecycle is EXPLICIT in v1).
|
|
25
|
+
*/
|
|
26
|
+
export type SessionProvider = (target: OpenTarget) => Promise<Session>;
|
|
27
|
+
/** Overrides for where the default provider discovers the running server (tests pass a temp root). */
|
|
28
|
+
export interface DefaultSessionProviderOptions {
|
|
29
|
+
/** Explicit controller home root. Omit to use `~/.webhands`. */
|
|
30
|
+
readonly root?: string;
|
|
31
|
+
/** Environment to read the home override from. Defaults to `process.env`. */
|
|
32
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The v1 default {@link SessionProvider}: a THIN CLIENT of the long-lived
|
|
36
|
+
* `serve` process (ADR-0005).
|
|
37
|
+
*
|
|
38
|
+
* It reads the endpoint file the running server advertised under the config dir
|
|
39
|
+
* and returns a {@link connectRemoteSession} proxy that forwards each verb to
|
|
40
|
+
* the server's single live page. There is no per-invocation browser launch
|
|
41
|
+
* here: a verb invocation drives the SAME live page the server holds, which is
|
|
42
|
+
* what makes session state persist across separate CLI processes.
|
|
43
|
+
*
|
|
44
|
+
* The {@link OpenTarget} is intentionally IGNORED for discovery: which browser
|
|
45
|
+
* to launch (`launch`/`attach`, the profile) was decided once, by the `serve`
|
|
46
|
+
* command, when the single session was brought up. A verb does not get to pick
|
|
47
|
+
* a different browser; it just drives the live one. If no server is live the
|
|
48
|
+
* provider raises {@link NoLiveServerError} (mapped by the CLI to "run `serve`
|
|
49
|
+
* first"); it never silently opens a browser.
|
|
50
|
+
*/
|
|
51
|
+
export declare function createDefaultSessionProvider(options?: DefaultSessionProviderOptions): SessionProvider;
|
|
52
|
+
//# sourceMappingURL=session-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-provider.d.ts","sourceRoot":"","sources":["../src/session-provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAIN,KAAK,UAAU,EACf,KAAK,OAAO,EACZ,MAAM,gBAAgB,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAEvE,sGAAsG;AACtG,MAAM,WAAW,6BAA6B;IAC7C,gEAAgE;IAChE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,6EAA6E;IAC7E,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACjC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,4BAA4B,CAC3C,OAAO,GAAE,6BAAkC,GACzC,eAAe,CAQjB"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { connectRemoteSession, NoLiveServerError, readSessionEndpoint, } from '@webhands/core';
|
|
2
|
+
/**
|
|
3
|
+
* The v1 default {@link SessionProvider}: a THIN CLIENT of the long-lived
|
|
4
|
+
* `serve` process (ADR-0005).
|
|
5
|
+
*
|
|
6
|
+
* It reads the endpoint file the running server advertised under the config dir
|
|
7
|
+
* and returns a {@link connectRemoteSession} proxy that forwards each verb to
|
|
8
|
+
* the server's single live page. There is no per-invocation browser launch
|
|
9
|
+
* here: a verb invocation drives the SAME live page the server holds, which is
|
|
10
|
+
* what makes session state persist across separate CLI processes.
|
|
11
|
+
*
|
|
12
|
+
* The {@link OpenTarget} is intentionally IGNORED for discovery: which browser
|
|
13
|
+
* to launch (`launch`/`attach`, the profile) was decided once, by the `serve`
|
|
14
|
+
* command, when the single session was brought up. A verb does not get to pick
|
|
15
|
+
* a different browser; it just drives the live one. If no server is live the
|
|
16
|
+
* provider raises {@link NoLiveServerError} (mapped by the CLI to "run `serve`
|
|
17
|
+
* first"); it never silently opens a browser.
|
|
18
|
+
*/
|
|
19
|
+
export function createDefaultSessionProvider(options = {}) {
|
|
20
|
+
return async (_target) => {
|
|
21
|
+
const endpoint = await readSessionEndpoint(options);
|
|
22
|
+
if (endpoint === undefined) {
|
|
23
|
+
throw new NoLiveServerError();
|
|
24
|
+
}
|
|
25
|
+
return connectRemoteSession(endpoint.url);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=session-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-provider.js","sourceRoot":"","sources":["../src/session-provider.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,oBAAoB,EACpB,iBAAiB,EACjB,mBAAmB,GAGnB,MAAM,gBAAgB,CAAC;AAoCxB;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,4BAA4B,CAC3C,UAAyC,EAAE;IAE3C,OAAO,KAAK,EAAE,OAAmB,EAAoB,EAAE;QACtD,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CAAC,OAAO,CAAC,CAAC;QACpD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC5B,MAAM,IAAI,iBAAiB,EAAE,CAAC;QAC/B,CAAC;QACD,OAAO,oBAAoB,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC3C,CAAC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The CLI version, read from this package's `package.json` at build time via a
|
|
3
|
+
* JSON import attribute (`with { type: 'json' }`). Bundled into the emit, so
|
|
4
|
+
* there is no runtime filesystem read; publish-safe because `dist/version.js`
|
|
5
|
+
* resolves `../package.json` to the package root, which npm always ships.
|
|
6
|
+
*
|
|
7
|
+
* Passed into `Cli.create(..., { version })` so `--version`, the help header,
|
|
8
|
+
* and the MCP server version all report the real package version.
|
|
9
|
+
*/
|
|
10
|
+
export declare const VERSION: string;
|
|
11
|
+
//# sourceMappingURL=version.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,eAAO,MAAM,OAAO,EAAE,MAAoB,CAAC"}
|
package/dist/version.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import pkg from '../package.json' with { type: 'json' };
|
|
2
|
+
/**
|
|
3
|
+
* The CLI version, read from this package's `package.json` at build time via a
|
|
4
|
+
* JSON import attribute (`with { type: 'json' }`). Bundled into the emit, so
|
|
5
|
+
* there is no runtime filesystem read; publish-safe because `dist/version.js`
|
|
6
|
+
* resolves `../package.json` to the package root, which npm always ships.
|
|
7
|
+
*
|
|
8
|
+
* Passed into `Cli.create(..., { version })` so `--version`, the help header,
|
|
9
|
+
* and the MCP server version all report the real package version.
|
|
10
|
+
*/
|
|
11
|
+
export const VERSION = pkg.version;
|
|
12
|
+
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,iBAAiB,CAAC,OAAM,IAAI,EAAE,MAAM,EAAC,CAAC;AAEtD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,OAAO,GAAW,GAAG,CAAC,OAAO,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,4 +1,60 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "webhands",
|
|
3
|
-
"version": "0.
|
|
4
|
-
|
|
3
|
+
"version": "0.1.6",
|
|
4
|
+
"description": "A CLI (and MCP server) that drives a real, persistent browser via Playwright, letting an agent or human control any website from a genuinely logged-in browser session.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"browser",
|
|
7
|
+
"playwright",
|
|
8
|
+
"automation",
|
|
9
|
+
"cli",
|
|
10
|
+
"mcp",
|
|
11
|
+
"agent"
|
|
12
|
+
],
|
|
13
|
+
"license": "AGPL-3.0-or-later",
|
|
14
|
+
"author": "wighawag",
|
|
15
|
+
"homepage": "https://github.com/wighawag/webhands#readme",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/wighawag/webhands.git",
|
|
19
|
+
"directory": "packages/cli"
|
|
20
|
+
},
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/wighawag/webhands/issues"
|
|
23
|
+
},
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
},
|
|
27
|
+
"type": "module",
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"default": "./dist/index.js"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"bin": {
|
|
35
|
+
"webhands": "./dist/bin.js"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist",
|
|
39
|
+
"src",
|
|
40
|
+
"README.md",
|
|
41
|
+
"LICENSE"
|
|
42
|
+
],
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"incur": "^0.4.10",
|
|
45
|
+
"@webhands/core": "0.2.0"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/node": "^25.2.0",
|
|
49
|
+
"as-soon": "^0.1.5",
|
|
50
|
+
"tsx": "^4.21.0",
|
|
51
|
+
"typescript": "^5.3.3",
|
|
52
|
+
"vitest": "^4.0.18"
|
|
53
|
+
},
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build": "tsc",
|
|
56
|
+
"dev": "as-soon -w src pnpm build",
|
|
57
|
+
"test": "vitest run",
|
|
58
|
+
"test:watch": "vitest"
|
|
59
|
+
}
|
|
60
|
+
}
|
package/src/bin.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {createCli} from './cli.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The executable entry for the `webhands` binary. It builds the
|
|
6
|
+
* `incur` CLI (with its default, real-browser session provider) and serves it:
|
|
7
|
+
* `serve()` parses argv, runs the matched command, writes the structured output
|
|
8
|
+
* envelope, and handles `--mcp` / `--llms` / `mcp add` / `skills add` for free.
|
|
9
|
+
*
|
|
10
|
+
* Kept separate from the builder (`cli.ts`) so tests drive the builder with an
|
|
11
|
+
* injected provider via `serve(argv, {stdout, exit})` without spawning a real
|
|
12
|
+
* browser, and only THIS file performs the real `.serve()`.
|
|
13
|
+
*/
|
|
14
|
+
void createCli().serve();
|