@foldspace_npm/harness

中文文档
0.1.42发布于 2026/11/09TypeScriptES Module近 30 天下载 2284

Build and verify portable Foldspace action artifacts against a live app.

原始使用文档
文档目录18 节
  1. The two architectures
  2. What is genuinely shared
  3. What differs — only the transport
  4. Why one package rather than two
  5. Use
  6. Discover the CLI
  7. Create a project
  8. Keep the harness current
  9. Keep up with the saved code
  10. Withdrawing a release (maintainers)
  11. Lint agent code before it ships
  12. Runtime helpers
  13. Choose an attach mode
  14. Verify actions through the agent
  15. Check the published code
  16. Make a short video to share
  17. Add the harness to an existing project
  18. Why this package exists at all

@foldspace_npm/harness

Build the portable dist/index.js action artifact and verify it against a live app.

This package is the part that is the same in both architectures. Everything else about how a builder runs differs between them; this does not.


The two architectures

A — local (today)B — remote (designed, unbuilt)
Where the build runsClaude Code on the human's computerClaude Agent SDK in GCP
Whose browseran isolated Chrome the harness launchesthe human's own Chrome
How code reaches the pageharness CDP preparationbuilder extension loads the cloud artifact
PrerequisitesClaude Code, a client repo, Node, an isolated Chrome profilethe extension installed
Session and cookiesin a profile the harness createsthe human's own, untouched
Code and historya git repo on the human's computera workspace in the hosted environment

"Export to repo" moves a build from B to A — and it is only a copy, not a rewrite, while agent/ is identical in both. Keeping it identical is the constraint this package exists to protect.

What is genuinely shared

  • agent/actions/* and agent/api/* — a handler is fetch plus runTask, nothing more. Actions built in one track run unmodified in the other.
  • agent/utils.ts — configures and re-exports @foldspace_npm/harness/runtime
  • foldspace build — esbuild → dist/index.js. In a code project, it first makes agent/runtime/ match this harness's runtime (never rolling back a copy of a newer runtime; the version is in src/runtime/VERSION) and names every file it wrote
  • foldspace deploy (Foldspace-internal) - uploads dist/index.js to agent/actions/<env>/<productId>/<agentApiName>
  • fixtures and tests
  • the verb interface below

What differs — only the transport

VerbA (CDP)B (extension)
loadBundleaddScriptToEvaluateOnNewDocument<script src> at the deployed CDN path
evaluateRuntime.evaluatecontent-script message
screenshotPage.captureScreenshottabs.captureVisibleTab
relaxCSPPage.setBypassCSPdeclarativeNetRequest header rules
navigatePage.navigatetabs.update

Detail and status: src/transports/README.md.

Why one package rather than two

Splitting harness-local and harness-remote forks build and deploy — the exact failure this package was created to end. And the verb interface only stays honest while both implementations sit behind it; separate them and "the same action runs in both modes" quietly stops being true.

A hosted environment uses the same action source and build contract, but local inject and attach remain CDP-only commands. The harness does not generate a customer application extension.


Use

Discover the CLI

Human-readable help is grouped by workflow:

foldspace help
foldspace help attach

Coding agents can inspect the same versioned command and capability contract:

foldspace help --json
foldspace help attach --json

The installed harness binary is an alias for foldspace.

The JSON describes current options, prerequisites, side effects, risks, capabilities, and next steps. It does not claim that future hosted-environment capability enforcement is already implemented.

Create a project

Create a configured actions project from the template bundled with the installed harness version. Missing values are prompted when stdin is a terminal:

npx --yes @foldspace_npm/harness init

Non-interactive / CI form:

npx --yes @foldspace_npm/harness init foldspace-agent \
  --product-id <product-id> \
  --agent-key my-agent \
  --app-domain app.example.com \
  --name "My Agent"

When running directly from a harness checkout during development:

node bin/cli.mjs init ../foldspace-agent \
  --product-id <product-id> \
  --agent-key my-agent \
  --app-domain app.example.com

The Agent Key is the value shown in Agent Studio, such as my-agent. It is not the sidecar directory name. --agent-api-name remains a deprecated alias for --agent-key. --domain remains a deprecated alias for --app-domain. --name is optional and defaults to the target directory name (foldspace-agent unless you pass a directory). The product ID must be the bare ID, not the SDK key (the one that starts with EU-, in every region). The app domain may be a hostname or an HTTP(S) URL without a port or path.

--region us|eu (default us) is the Foldspace region the account lives in. It is written to foldspace.dev.json as region, and picks the SDK the project loads (sdkUrl): each region has its own build, pointing at that region's servers. A project with no region is US. The values per region live in src/region.mjs.

For safety, init sets up a new folder, or fills one that holds nothing but what the system or an editor put there (.DS_Store, .localized, Icon\r, ._*, Thumbs.db, desktop.ini, .mcp.json, and the folders .git/, .vscode/, .cursor/, .idea/, and .claude/ without settings.json). It leaves those as they are and refuses anything else, including an existing Foldspace project. It never fills the home directory or its Desktop, Documents or Downloads, the disk's root, or the folder Claude Code Desktop makes for a session started with no folder (under scratch-workspaces), or Codex's folder for chats with no project (~/Documents/Codex) and each chat's folder in it, even when they are empty. It never overwrites a file and does not initialize Git. On a TTY it can offer npm install --ignore-scripts after scaffolding. After creation:

cd foldspace-agent
npm install --ignore-scripts   # if you skipped the install prompt
# before the first build, CLAUDE.md step 5: a code project's agent/ replaces the scaffold's
npm run build
npm run inject   # the human signs in in that window
npx foldspace observe wait-login
npx foldspace attach --daemon

The generated npm scripts intentionally remain the normal project interface; foldspace init is the one-time project creation command.

Generated CLAUDE.md imports platform instructions from @foldspace_npm/harness instead of copying them. Existing projects can switch to that import with foldspace upgrade --refresh-instructions.

Keep the harness current

Tenant projects pin an exact @foldspace_npm/harness version. Publishing a newer package does not move them: each session checks and bumps. Several builders can work on one agent, so within one line (0.1.x to a newer 0.1.y, update.autoUpgrade) the coding agent upgrades without asking and says so in one line; across a line (0.1 to 0.2) it asks the human first.

npx --yes @foldspace_npm/harness@latest upgrade --check
foldspace upgrade --yes   # within the line: no question; across a line: after the human agrees

--check (and a non-interactive upgrade with no --yes) never installs. --yes writes the new exact version and runs npm install --ignore-scripts. Rebuild afterward; that publishes nothing.

foldspace build and foldspace attach run the same check before they start (attach --status and --stop do not). A project that is behind gets one line on stderr and carries on. A project on a withdrawn version (below) refuses to run and prints the reason. To build on it anyway, once the human has declined the upgrade:

FOLDSPACE_SKIP_UPDATE_CHECK=1 npm run build

That turns the check off entirely; it does not fix the withdrawn version.

Keep up with the saved code

With a code project, several builders can save to one agent. foldspace sync keeps a folder's agent/ up to date: it never reaches Foldspace itself - the coding agent reads the saved code with the Foldspace tools and passes the heads it read.

npx foldspace sync check --develop <sha> [--line <sha>]          # what to pull, and where to write it
npx foldspace sync apply                                         # merge it into agent/
npx foldspace sync saved --head <commit_sha> --paths <a,b>       # after a save

--line is the head of foldspace/<Agent Key>, the line of work the code project's instructions save to, once it exists. apply keeps a copy of agent/ in .foldspace-dev/agent-kept/<time>/ and merges file by file against what the folder last pulled (kept in .foldspace-dev/saved-code/): a file only one side changed takes that side; a file both changed takes the saved version and is listed under conflicts. Saving and publishing are unchanged: they follow the code project's instructions.

Withdrawing a release (maintainers)

When a published version is broken badly enough that nobody should build on it, withdraw it with npm deprecate. The registry is the list of withdrawn versions, so projects already installed on it see the withdrawal without installing anything new. Only owners of @foldspace_npm/harness on npm can run it.

  1. Publish the fix first. If the withdrawn version is also latest, the message tells users to upgrade to a version that is itself withdrawn.

  2. Withdraw the broken version, with a message that says what is wrong and which version to move to. That message is exactly what users, and the coding agent working for them, see when build or attach refuses to run:

    npm deprecate @foldspace_npm/[email protected] "attach loads the wrong bundle; upgrade to 0.1.15"
    

    A range works too: npm deprecate "@foldspace_npm/harness@<0.1.15" "…".

  3. Check it took:

    npm view @foldspace_npm/[email protected] deprecated
    

To undo a withdrawal, deprecate with an empty message:

npm deprecate @foldspace_npm/[email protected] ""

It is not instant for build and attach. They use the registry's answer cached for 12 hours in .foldspace-dev/harness-update.json. foldspace help --json (which the manual reads first) and foldspace upgrade ask the registry each time and refresh that cache, so a withdrawal reaches a project at its next session start, and within 12 hours otherwise. It only reaches projects on a harness version that has this check.

Lint agent code before it ships

foldspace build runs foldspace lint first. Errors skip bundling; warnings print and still produce dist/index.js.

foldspace lint
foldspace lint --json

v1 flags:

  • error no-static-agent-prompt - execute, a card's callback or its readyState handing the agent instructions (directive, “tell the user”, …)
  • error no-runtask-prompt — runTask({ data: { prompt } }) and the same keys; a customer API field named prompt is allowed
  • error card-contract - awaitUserInput: true on a view card (its renderComponent or render uses no callback, cancel or readyState): the chat panel ignores it, but in headless mode the agent waits for a callback that never comes
  • error no-test-mode-in-bundle - armAllInstances used anywhere in agent/ (called, or handed on to be called later), or setTestMode(...) with anything but a literal false. A code project's own copy of the runtime in agent/runtime/ is not refused for the setTestMode inside its armAllInstances
  • error no-fixed-customer-address - on a product with a subdomain per customer (PER_CUSTOMER_SUBDOMAIN in agent/constants.ts), one customer's address in API_BASE or anywhere in agent/; while that is undecided, an address whose first part looks like a customer's name (acme. - not app., eu1., reports.)
  • error token-stays-home - the user's sign-in token (getAuthToken, a session-named storage key, document.cookie, followed through variables and helper functions) returned from execute or a card, handed to Foldspace, logged, stored again, or sent to a host that is not the product's backend
  • error no-secrets - API keys, JWTs, bearer tokens, cookie values, secret-named strings, .har captures, storage dumps and .env files anywhere in the project (not node_modules/, dist/, .foldspace-dev/, agent/runtime/)
  • warning known-hosts-only - fetch, sendBeacon, WebSocket, EventSource, XMLHttpRequest, publicFetch or import() to an address written out whose host is neither Foldspace's (*.foldspace.ai), the product's (foldspace.dev.json hosts, API_BASE, the app's own domain) nor written in agent/constants.ts
  • warning card-contract - any other awaitUserInput on a view card, awaitUserInput on an input card (not needed), a card that declares callback, cancel or readyState and uses none of them (the SDK treats it as a view card), or a renderComponent the SDK can't read (the object passed on or aliased, ...rest, arguments): the agent waits until it calls readyState, callback or cancel, so a view card that calls none of them never gets a reply
  • warning no-emoji — emoji in handler or agent/views string literals
  • warning registry-integrity — duplicate keys, { execute } not registered, registry value missing execute

Put action instructions in Agent Studio or with the Foldspace tools. foldspace lint cannot see that copy.

Runtime helpers

Tenant actions import from agent/utils.ts, which configures and re-exports @foldspace_npm/harness/runtime. That code is bundled into dist/index.js and runs in the customer's page — it is not a CLI command.

API_BASE and AUTH_SOURCE stamp empty. Fill them from a captured XHR, not a guess. For auth that is not Bearer, follow recipes/swap-the-login-method/README.md: for a custom header, copy its agent/utils.ts over agent/utils.ts; for cookies with no token, its variants/utils.cookies.ts.

Choose an attach mode

  • Swap (default): the page already uses the configured product and agent.
  • Bootstrap (--bootstrap): the page has no Foldspace SDK.
  • Replace (--replace): the page embeds a different product or agent, or has an SDK without the configured agent.

Use foldspace help attach for mode requirements, effects, and safety options. Coding agents run foldspace attach --daemon so the invoking tool returns after [lifecycle] inspect_registration:…. Foreground attach remains the default for someone watching the terminal. attach --status and attach --stop inspect and tear down a daemon. Look at the customer's app with foldspace observe: it reads the test window, read-only, while attach runs.

Verify actions through the agent

foldspace attach does not invoke action handlers directly. It loads the local artifact, verifies that the captured local registry matches the configured SDK agent - including an empty registry - and then observes action calls while you exercise the normal agent experience. Named actions are not required to attach.

npx foldspace ask "<question>" --retry 2 types the question into the agent's panel and reports the reply, the card text and a screenshot, and - while attach runs - whether execute and render ran.

Action output shows the SDK's action calls and local execute/render phases:

[actions] sdk:action show_account_360 executed
[actions] local-handler:execute show_account_360 succeeded 412ms
[actions] local-handler:render show_account_360 succeeded 18ms

Only action names, parameter keys, statuses, and durations are recorded; handler parameter values, results, and error messages are not. On detach, repeated page failures and bounded action observations are summarized. The observer and diagnostics are injected by the harness, so dist/index.js is not rewritten.

Check the published code

After the deploy tool in the Foldspace tools - not foldspace deploy, which is for Foldspace staff only - publishes the code project to dev (or, with the human's yes, to production), check it in the same test window. This needs a site that loads this agent by itself: after attach --bootstrap or --replace there is nothing to check yet.

npx foldspace attach --stop          # while attach runs the page serves your local build
npx foldspace published dev          # this page's agent now loads the published dev code
npx foldspace ask "<the question>" --retry 2
npx foldspace attach --daemon        # back to your local build before you build anything else

published refuses while attach runs. It also refuses unless the page's own Foldspace agent is this project's agent - the same product and Agent Key as foldspace.dev.json, checked the way attach checks it. It changes only this page's agent, through the SDK's setConfiguration({ enableDebugLogs: true, remoteActionsSettings: { enabled: true, environment: "DEV" | "PROD" } }); a reload puts the page back on what the agent loads normally. When code loads, the SDK keeps any handler the agent already has over a new one of the same name, so once the published code has loaded, its handlers are set over the page's for every action it defines; the page keeps its own handlers for the rest. An agent pinned to one version of its code, or loading code from its own address, is refused: the window would not show what was published last. Without attach, ask still reports the reply, the card text and a screenshot; handler.execute and handler.render read "not tracked here (published code)".

Make a short video to share

npx foldspace record shows the agent's key use cases, from video/scenes.json: an opening card meets the product's agent, then each question is asked in the test window, typed in a new conversation, with a caption once it is answered - the Foldspace features it shows as pills, the question, and one large line on what the user gets, its payoff highlighted. About 40 s, in Foldspace's colours with the official logo: video/<product>-<date>.mp4 and a poster. It records with Chrome's own screencast and encodes in a hidden Google Chrome - no ffmpeg, no screen-recording permission, nothing uploaded. The video shows real data from the signed-in account; sharing it is the human's call, and video/.gitignore keeps it out of git. The share-a-video skill, written into each project's .claude/skills/, is the how-to. It runs for a few minutes; stopped half-way, it puts the window back as it was.

Add the harness to an existing project

npm i -D @foldspace_npm/harness
{ "scripts": {
    "dev":    "foldspace build --watch",
    "build":  "foldspace build",
    "inject": "foldspace inject",
    "attach": "foldspace attach",
    "attach:daemon": "foldspace attach --daemon"
} }

Every command resolves the consuming repo — process.cwd(), or FOLDSPACE_PROJECT_DIR so a hosted builder can point it at a workspace it controls. Config comes from the consumer's foldspace.dev.json.

foldspace deploy is Foldspace-internal: it writes to Foldspace's own storage, which no customer can write to. A coding agent never runs or offers it. With a code project, a build session publishes the agent's code with the Foldspace tools instead: repo_write saves it, and the deploy tool publishes it - to dev after saying so, to production only on the human's yes - as the manual's "Done, and what you say at the end" says. Without a code project, the Foldspace team does it with the human.


Why this package exists at all

scripts/ used to be copied into every client repo, and copies fork. By 2026-08-25 client-template held one fix and a client repo held four others, and neither had all five:

FixWhy it matters
SDK load guard + retryOn an app with no document.body at document-start the append throws; the old guard tested for the stub, so nothing retried and the agent silently never loaded
Worker release on auto-attachAuto-attach pauses every worker until the attaching client releases it — the harness froze the app's own workers for the whole session
agentIds enumerationagent({apiName}) returns the OVERLAY handle; arming it on an embedded agent leaves real conversations untagged
--test-mode (off by default)Test mode was armed unconditionally, hiding exactly what initial setup needs to see; now it is opt-in
Honest badgeIt asserted TEST MODE whether or not test mode was on

Two further changes came from packaging it: the bundle is injected rather than fetched from a dev server — which removed express and concurrently, 95 packages, and is the precondition for driving a browser that cannot reach the build machine — and inject now records the port that attach reads, since they previously disagreed by construction.

版本与发布记录

@foldspace_npm/harness 共发布 43 个版本,首次发布于 2026/08/31;过去 12 个月发布了 43 个版本。

版本发布日期距今
0.1.42latest2026/11/096 天前
0.1.412026/11/097 天前
0.1.402026/11/087 天前
0.1.392026/11/0313 天前
0.1.382026/11/0114 天前
0.1.372026/10/2818 天前

关于 @foldspace_npm/harness 的常见问题

@foldspace_npm/harness 是什么?

Build and verify portable Foldspace action artifacts against a live app.

如何安装 @foldspace_npm/harness?

在项目目录运行 npm install @foldspace_npm/harness,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 harness、foldspace,可以用 npx harness 直接运行。

@foldspace_npm/harness 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@foldspace_npm/harness 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@foldspace_npm/harness 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@foldspace_npm/harness 可以商用吗?

@foldspace_npm/harness 使用 未声明 许可。package.json 没有声明许可证。没有许可证的代码默认保留全部权利,商用前请联系作者或查看仓库中的 LICENSE 文件。以上为概括,不构成法律意见,请以许可原文为准。

@foldspace_npm/harness 的下载量有多少?

根据 npm 官方下载统计,@foldspace_npm/harness 在 2026/10/15 – 2026/11/13 这 30 天内被下载 2,284 次,日均约 76 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@foldspace_npm/harness 还在维护吗?

最新版本 0.1.42 发布于 2026/11/09(6 天前);至今共发布 43 个版本,其中过去 12 个月发布了 43 个;首次发布于 2026/08/31。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。