Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ jobs:
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v7
with:
node-version: 24
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile --filter @lockintime/headless
- run: pnpm --filter @lockintime/headless test
Expand Down Expand Up @@ -144,10 +144,23 @@ jobs:
timeout-minutes: 45
steps:
- uses: actions/checkout@v7
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v7
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile --filter @lockintime/headless
- name: Build app
run: ./apps/headless/build.sh
- name: Protocol and security tests
run: ./apps/headless/test.sh
- name: Build TypeScript SDK
run: pnpm --filter @lockintime/headless build
- name: Swift CLI to TypeScript SDK integration
env:
HEADLESS_TEST_CLI: ${{ github.workspace }}/apps/headless/Headless.app/Contents/Resources/bin/headless
HEADLESS_TEST_HOST: ${{ github.workspace }}/apps/headless/Headless.app/Contents/MacOS/Headless
run: node packages/headless-npm/test/macos-swift-integration.mjs

macos-e2e:
name: macOS E2E (WKWebView)
Expand Down
7 changes: 5 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ jobs:
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v7
with:
node-version: 24
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile --filter @lockintime/headless
- run: pnpm --filter @lockintime/headless test
Expand Down Expand Up @@ -416,10 +416,13 @@ jobs:
id-token: write
steps:
- uses: actions/checkout@v7
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v7
with:
node-version: 24
node-version: 22
registry-url: https://registry.npmjs.org
cache: pnpm
- run: pnpm install --frozen-lockfile --filter @lockintime/headless
- name: Publish verified launcher
working-directory: packages/headless-npm
env:
Expand Down
22 changes: 22 additions & 0 deletions packages/headless-npm/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
MIT License

Copyright (c) 2026 LockInTime
Copyright (c) 2026 Antiwork, Inc. (original chromeless foundation)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
76 changes: 75 additions & 1 deletion packages/headless-npm/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# `@lockintime/headless`

Verified npm launcher for the [Headless agent browser](https://github.com/LockInTime/headless).
Typed Node.js SDK and verified npm launcher for the
[Headless agent browser](https://github.com/LockInTime/headless). Node.js 22 or
newer is required. The SDK has no runtime dependencies and talks directly to
Headless over its private per-user Unix socket. It does not start a network
service or use MCP as an internal transport.

## CLI launcher

```sh
npx @lockintime/headless help
Expand All @@ -15,3 +21,71 @@ plus Linux x86_64 and arm64. Windows users should use the published GHCR image.

Set `HEADLESS_NPM_CACHE` to an absolute directory to move the verified cache.
The release download origin is fixed and cannot be overridden.

Because this package distributes both the SDK and verified product launcher,
its version follows Headless product tags. The supported wire and schema
versions remain independent and are pinned in the generated SDK contract.

## Connect to a shared host

Replace repeated CLI calls with typed methods. Closing this client closes only
its active socket requests. It never stops a shared Headless host.

```ts
import { connect } from "@lockintime/headless";

// CLI: headless status
await using headless = await connect();

// CLI: headless visit https://example.com
const page = await headless.visit({ url: "https://example.com" });
if (page.untrustedContent) {
console.log(page.value.title);
}
```

Every page-derived result is returned as `Untrusted<T>`. Callers must preserve
that trust marker when sending page content to an agent or another system.

## Supervised host

Use `launch()` when this process must own a new host. It invokes the installed
CLI with `headless start --supervised`, keeps the ownership pipe open, verifies
that the startup response and socket report the same host PID, and reaps only
that launcher during disposal. Omit `presentation` to preserve the platform
default, or explicitly select `background` or `foreground` on macOS. Launch
fails rather than claiming an already-running shared host.

```ts
import { launch } from "@lockintime/headless";

await using host = await launch({
allow: ["example.com"],
installationTimeoutMs: 300_000,
startupTimeoutMs: 10_000,
});

const session = await host.client.openSession("research", { isolated: true });
await using scoped = session;
const snapshot = await scoped.inspect({ context: "actions" });
```

Session helpers expose only session-scoped commands. Host lifecycle and session
creation remain on `HeadlessClient`.

## Cancellation and authentication

Methods accept `{ signal, timeoutMs }` as their final argument. Cancellation or
timeout before any request byte is written is retry-safe. After a write,
`OperationOutcomeUnknown` means the SDK cannot know whether the browser action
completed. Never retry it automatically; inspect browser state first.

Saved-login methods accept only a challenge ID and account alias. There is no
password parameter in the authentication API:

```ts
await scoped.authLogin({
challenge: "11111111-1111-4111-8111-111111111111",
account: "work",
});
```
18 changes: 18 additions & 0 deletions packages/headless-npm/lib/installer.d.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export interface InstalledRelease {
readonly directory: string;
readonly release: {
readonly executable: string;
};
}

export interface InstallOptions {
readonly cacheRoot?: string;
readonly signal?: AbortSignal;
}

export function defaultCacheRoot(
platform?: NodeJS.Platform,
environment?: NodeJS.ProcessEnv,
): string;

export function ensureInstalled(options?: InstallOptions): Promise<InstalledRelease>;
Loading
Loading