@foldspace_npm/harness 0.1.15 → 0.1.17
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/CLAUDE.md +94 -37
- package/README.md +3 -3
- package/bin/attach.mjs +50 -19
- package/bin/badge.mjs +50 -0
- package/bin/inject.mjs +5 -2
- package/bin/observe.mjs +790 -0
- package/package.json +1 -1
- package/recipes/INDEX.md +1 -0
- package/recipes/bottom-bar/README.md +43 -0
- package/recipes/bottom-bar/agent/bottomBar.ts +94 -0
- package/recipes/bottom-bar/fixtures/configuration.sent.json +17 -0
- package/recipes/bottom-bar/recipe.json +9 -0
- package/src/badge-core.mjs +22 -0
- package/src/cdp-client.mjs +233 -0
- package/src/cli-help.mjs +2 -1
- package/src/cli-registry.mjs +85 -6
- package/src/observe-core.mjs +627 -0
- package/src/session-events.mjs +42 -0
- package/templates/agent-starter/README.md +2 -2
package/CLAUDE.md
CHANGED
|
@@ -44,16 +44,23 @@ current CLI contract — risk, prerequisites, effects, next step.
|
|
|
44
44
|
| **L2 · look up entities** | a search from the user's own words, showing rows |
|
|
45
45
|
|
|
46
46
|
1. **Get the agent live first (L0) - with no actions.** Straight after
|
|
47
|
-
orienting: `npm run build`, `npm run inject`,
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
orienting: `npm run build`, `npm run inject`, and one ask - *"A Chrome
|
|
48
|
+
window opened at <Product>'s login. Please log in there - I'll notice when
|
|
49
|
+
you're in."* Then `npx foldspace observe wait-login`: it returns the moment
|
|
50
|
+
they are in, so nobody has to type "I've logged in". Then
|
|
51
|
+
`npx foldspace attach --daemon`. An empty registry is valid. L0 is
|
|
52
|
+
`registration_ok` with the agent visible on their page: take
|
|
53
|
+
`npx foldspace observe screenshot`, send them the image, and tell them to
|
|
54
|
+
look at it and say hello to it. **From here on the agent stays on their
|
|
55
|
+
page** - you never detach to look at the app.
|
|
56
|
+
**Do not explore the app first**: no
|
|
51
57
|
reading its bundle, no watching its traffic, no browsing its screens.
|
|
52
58
|
Nothing about L0 needs any of it, and minutes of silent investigation
|
|
53
59
|
before the human has seen anything is the wrong first impression.
|
|
54
60
|
2. **Then offer experiences - from what you already have.** With the agent on
|
|
55
61
|
their screen, offer three or four read-only experiences as a choice,
|
|
56
|
-
recommended first. Sources: the
|
|
62
|
+
recommended first. Sources: the product's own menu (`npx foldspace observe
|
|
63
|
+
menu` takes a second and moves nothing), the conversation starters seeded at sign-up,
|
|
57
64
|
`discover_actions`, and `docs/app-profile.md`. `discover_actions` gives
|
|
58
65
|
candidates, not an inventory. Do not investigate the app to build this
|
|
59
66
|
list. Order them cheapest first: the user's own data with no parameter
|
|
@@ -64,11 +71,15 @@ current CLI contract — risk, prerequisites, effects, next step.
|
|
|
64
71
|
so and offer the next one. On an empty account the useful first action is
|
|
65
72
|
one that creates data - say so instead.
|
|
66
73
|
|
|
67
|
-
**Say when the test window is yours, and when it is theirs
|
|
68
|
-
human share one Chrome window. Before you start looking
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
74
|
+
**Say when the test window is yours, and when it is theirs - on the page and
|
|
75
|
+
in chat.** You and the human share one Chrome window. Before you start looking
|
|
76
|
+
at the app or testing in it, run `npx foldspace badge working` (a bar across
|
|
77
|
+
the top of the window says Foldspace is building here, leave it alone) and say
|
|
78
|
+
so in chat: *"I'm going to work in that Chrome window for a few minutes -
|
|
79
|
+
please leave it alone until I tell you it's ready."* When it is their turn,
|
|
80
|
+
`npx foldspace badge ready --text "Try asking: <the question this experience
|
|
81
|
+
answers>"` and the same line in chat. `foldspace badge label` puts the small
|
|
82
|
+
corner tag back; `off` removes it, for a screenshot they will share.
|
|
72
83
|
|
|
73
84
|
Navigation, uploads, extraction and anything that writes are a later session.
|
|
74
85
|
|
|
@@ -92,15 +103,35 @@ and asking are not the only options — **observing is, and it comes first**:
|
|
|
92
103
|
|
|
93
104
|
1. **Already known** — `docs/app-profile.md`. A verified endpoint needs no
|
|
94
105
|
browsing.
|
|
95
|
-
2. **You
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
106
|
+
2. **You look - once they have chosen an experience - with `foldspace
|
|
107
|
+
observe`.** It reads the test window they signed in to, it is read-only by
|
|
108
|
+
construction, and it works **while the agent stays attached**: do not
|
|
109
|
+
`attach --stop` to look. Write no browser-driving script of your own, and do
|
|
110
|
+
not reach for a separate DevTools server - one launched on its own opens a
|
|
111
|
+
different, signed-out Chrome and can never see the window they logged in to.
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
npx foldspace observe menu # the product's own screens, by label and path
|
|
115
|
+
npx foldspace observe screen --click "<a label from menu>" --match "<three to five words for the data>"
|
|
116
|
+
npx foldspace observe read "<the GET path screen returned>" # GET only: status, field names and types, row count
|
|
117
|
+
npx foldspace observe auth # only if a read came back 401 or 403
|
|
118
|
+
npx foldspace observe styles # their font and colours, for the card
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
`screen` reaches a screen the way a person does and returns at most eight
|
|
122
|
+
requests, ranked by your words - field names, value types and row counts,
|
|
123
|
+
**never values**. `read` confirms the one you picked. `auth` reports *where*
|
|
124
|
+
each header value comes from, never the value: do not go hunting through
|
|
125
|
+
storage, cookies or bundles yourself. One experience needs one screen; two
|
|
126
|
+
`screen` calls is normal, five means the experience is wrong for this
|
|
127
|
+
product - offer the next one.
|
|
128
|
+
|
|
129
|
+
**`observe` refuses what is not provably looking**, and a refusal is an
|
|
130
|
+
answer (**Safety**): it follows links, tabs and menu openers only - a plain
|
|
131
|
+
button can do anything, so use `--goto <path>` from `menu` instead; it will
|
|
132
|
+
not open a path that does something (`/logout`, `/…/delete`); and `read`
|
|
133
|
+
only goes to the app's own origin, a host configured for this project, or
|
|
134
|
+
an origin `screen` saw the page call.
|
|
104
135
|
3. **Ask the human to perform the workflow** — only for what you cannot reach:
|
|
105
136
|
an MFA step, a paywall, a screen that needs a record the account does not
|
|
106
137
|
have, or anything that would write.
|
|
@@ -180,11 +211,17 @@ with how you established each.
|
|
|
180
211
|
| Navigation route | **yes** | `isActive: true` |
|
|
181
212
|
|
|
182
213
|
Publishing a **new** object is inert; publishing an **edit to a live one** is
|
|
183
|
-
the deploy above. Navigation has
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
214
|
+
the deploy above. **Navigation has two switches, and both must be on.** Each
|
|
215
|
+
route has its own `isActive`, set when you create it (`create_navigation_route`
|
|
216
|
+
/ `bulk_create_navigation_routes`, `isActive: true`). The agent has one
|
|
217
|
+
agent-level switch, **off by default on a new subscription**: `publish_navigation`
|
|
218
|
+
turns it on, `unpublish_navigation` off, and neither touches the routes' own
|
|
219
|
+
`isActive`. `list_navigation_routes` shows both. There is no MCP tool to flip a
|
|
220
|
+
single route's `isActive` after creation (it was disabled until Agent Studio
|
|
221
|
+
can reverse it) - create it right, or `update_navigation_route`, which replaces
|
|
222
|
+
the whole route. Route descriptions are a retrieval surface: write what the
|
|
223
|
+
page is *for*, in the user's words, and pair every parameterised route with the
|
|
224
|
+
lookup action that produces its parameter — or ship neither.
|
|
188
225
|
|
|
189
226
|
**Ask before publishing.** It is required only so the copilot can call the
|
|
190
227
|
action, and it is a live product change when the agent has real users.
|
|
@@ -198,9 +235,8 @@ npx foldspace attach --daemon
|
|
|
198
235
|
```
|
|
199
236
|
|
|
200
237
|
`inject` launches an isolated Chrome profile and records its debug port; it
|
|
201
|
-
loads nothing. In a first session `attach` comes straight after sign-in (L0)
|
|
202
|
-
|
|
203
|
-
same Chrome, then attach again. `attach` prepares the page and loads the local
|
|
238
|
+
loads nothing. In a first session `attach` comes straight after sign-in (L0),
|
|
239
|
+
and `foldspace observe` reads the same window while it runs. `attach` prepares the page and loads the local
|
|
204
240
|
`dist/index.js` over CDP. Coding agents use `--daemon` so the tool returns after
|
|
205
241
|
`[lifecycle] inspect_registration:…`. An empty local registry is valid; `npm run
|
|
206
242
|
build` is still required so `dist/index.js` exists.
|
|
@@ -212,14 +248,32 @@ SDK without the configured agent.
|
|
|
212
248
|
|
|
213
249
|
`inspect_registration:registration_ok` is the signal. On `registration_mismatch`
|
|
214
250
|
read `npx foldspace help attach --json` and map `missingActionNames`,
|
|
215
|
-
`unexpectedActionNames`, `diagnosticError`.
|
|
216
|
-
|
|
251
|
+
`unexpectedActionNames`, `diagnosticError`. A running attach keeps the actions
|
|
252
|
+
it loaded: after a rebuild, `attach --stop` and attach again to load the new
|
|
253
|
+
ones. That is the only reason to stop it.
|
|
217
254
|
|
|
218
255
|
Prove a named action through the visible agent: after the human talks to the
|
|
219
256
|
copilot, read the daemon log for `[actions]` SDK callback and local
|
|
220
257
|
execute/render lines. Those record names, statuses, durations and parameter keys
|
|
221
258
|
only — never results or error bodies.
|
|
222
259
|
|
|
260
|
+
## Done, and what you say at the end
|
|
261
|
+
|
|
262
|
+
A first session is done when the customer has seen one experience answer from
|
|
263
|
+
their own data, in the test window, and been invited to try it. Not when the
|
|
264
|
+
gates pass, and not when there is nothing left to improve.
|
|
265
|
+
|
|
266
|
+
If they ask *"how do I put this in front of my users?"*, the honest answer is:
|
|
267
|
+
|
|
268
|
+
> What you've seen runs in a test window on your machine. Putting it in front
|
|
269
|
+
> of your users is a separate step, and the Foldspace team does it with you.
|
|
270
|
+
> Reach them through the website - <https://foldspace.ai/demo> - or at
|
|
271
|
+
> <support@foldspace.ai>. Everything built today carries over: the actions are
|
|
272
|
+
> already in your Foldspace account, and the code is in this folder.
|
|
273
|
+
|
|
274
|
+
Never run `foldspace deploy`, never install cloud tooling, never ask them for
|
|
275
|
+
cloud credentials: `deploy` is Foldspace-internal and no customer can run it.
|
|
276
|
+
|
|
223
277
|
## Verification gates
|
|
224
278
|
|
|
225
279
|
Do not report success without all six:
|
|
@@ -261,14 +315,17 @@ Do not report success without all six:
|
|
|
261
315
|
MCP (`search_docs`, `fetch_page`) before asserting how the platform behaves;
|
|
262
316
|
cite the page, or say plainly that the docs do not cover it and write down
|
|
263
317
|
what you learned.
|
|
264
|
-
4. **
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
318
|
+
4. **Test through the agent, then let them.** In a first session, talk to the
|
|
319
|
+
agent yourself to prove an experience - type the real question into its
|
|
320
|
+
panel in the test window and read the reply - and only then invite the
|
|
321
|
+
customer to try it. Those conversations appear in their Foldspace dashboard,
|
|
322
|
+
and that is wanted: seeing their own conversations recorded is part of what
|
|
323
|
+
they came to see. Never pass `--test-mode` in a first session.
|
|
324
|
+
5. **Test mode is for an agent that already has real users.** Then `attach
|
|
325
|
+
--test-mode` keeps your conversations out of their data. It is not a safety
|
|
326
|
+
net: publishing still changes the live agent. Working on an agent in
|
|
327
|
+
production - test mode, pinned action versions, what may be published - is
|
|
328
|
+
a separate playbook, not this one.
|
|
272
329
|
6. **The human is not necessarily a developer.** Never hand over a terminal
|
|
273
330
|
line, a stack trace, or a choice between flags. Name experiences by outcome
|
|
274
331
|
— *"show my plan and credits"*, not `get_account_status`. Their two jobs:
|
package/README.md
CHANGED
|
@@ -190,8 +190,8 @@ Use `foldspace help attach` for mode requirements, effects, and safety options.
|
|
|
190
190
|
Coding agents should run `foldspace attach --daemon` so the invoking tool
|
|
191
191
|
returns after `[lifecycle] inspect_registration:…`. Foreground attach remains
|
|
192
192
|
the default for humans watching the terminal. `attach --status` and
|
|
193
|
-
`attach --stop` inspect and tear down a daemon.
|
|
194
|
-
|
|
193
|
+
`attach --stop` inspect and tear down a daemon. Look at the customer's app with
|
|
194
|
+
`foldspace observe`: it reads the same window, read-only, while attach runs.
|
|
195
195
|
|
|
196
196
|
### Verify actions through the agent
|
|
197
197
|
|
|
@@ -252,7 +252,7 @@ and **neither had all five**:
|
|
|
252
252
|
| SDK load guard + retry | On 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 |
|
|
253
253
|
| Worker release on auto-attach | Auto-attach pauses every worker until the attaching client releases it — the harness froze the app's own workers for the whole session |
|
|
254
254
|
| `agentIds` enumeration | `agent({apiName})` returns the OVERLAY handle; arming it on an embedded copilot leaves real conversations untagged |
|
|
255
|
-
| `--
|
|
255
|
+
| `--test-mode` (off by default) | Test mode was armed unconditionally, hiding exactly what initial setup needs to see; now it is opt-in |
|
|
256
256
|
| Honest badge | It asserted `TEST MODE` whether or not test mode was on |
|
|
257
257
|
|
|
258
258
|
Two further changes came from packaging it: the bundle is **injected** rather
|
package/bin/attach.mjs
CHANGED
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
shouldFulfillActionRequest,
|
|
44
44
|
} from "../src/attach-preflight.mjs";
|
|
45
45
|
import { serializeDiagnostic } from "../src/diagnostics.mjs";
|
|
46
|
+
import { recordEvent } from "../src/session-events.mjs";
|
|
46
47
|
import {
|
|
47
48
|
ERROR_CODES,
|
|
48
49
|
createLifecycleResult,
|
|
@@ -134,11 +135,13 @@ try {
|
|
|
134
135
|
}
|
|
135
136
|
const bootstrap = attachMode === ATTACH_MODES.BOOTSTRAP;
|
|
136
137
|
const replace = attachMode === ATTACH_MODES.REPLACE;
|
|
137
|
-
// Test mode keeps conversations out of the customer's default list.
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
|
|
138
|
+
// Test mode keeps conversations out of the customer's default list. It is OFF
|
|
139
|
+
// by default: in a first session the customer's own conversations appearing in
|
|
140
|
+
// their dashboard is the point - they see what Foldspace records - and an
|
|
141
|
+
// orange TEST MODE label would imply a safety (nothing published) that does
|
|
142
|
+
// not exist. --test-mode opts in, for an agent that already has real users.
|
|
143
|
+
// --no-test-mode is still accepted so older instructions keep working.
|
|
144
|
+
const noTestMode = !process.argv.includes("--test-mode");
|
|
142
145
|
// The badge used to hardcode "TEST MODE" whether or not test mode was on — a
|
|
143
146
|
// string that asserts you are safe while you are not. It now states what is
|
|
144
147
|
// actually true. --no-badge drops it entirely, for recording a demo.
|
|
@@ -170,8 +173,8 @@ const agentKey = cfgTarget.overrideKey || `EU-${productId}-1-1`;
|
|
|
170
173
|
const sdkUrl = cfgTarget.sdkUrl || cfg.sdkUrl;
|
|
171
174
|
const actionTarget = { productId, agentApiName };
|
|
172
175
|
const badgeText = noTestMode
|
|
173
|
-
? `FOLDSPACE
|
|
174
|
-
: `FOLDSPACE
|
|
176
|
+
? `FOLDSPACE AGENT BUILDER \u00b7 ${agentApiName} \u00b7 ${attachMode} \u00b7 LIVE`
|
|
177
|
+
: `FOLDSPACE AGENT BUILDER \u00b7 ${agentApiName} \u00b7 ${attachMode} \u00b7 TEST MODE`;
|
|
175
178
|
|
|
176
179
|
// Refuse to run against an uninitialised template. Without this, attach injects
|
|
177
180
|
// the literal placeholder as an agent api name and the page fails with an opaque
|
|
@@ -217,19 +220,45 @@ const SDK_PATTERNS = [
|
|
|
217
220
|
"*://*/*eucera.js*",
|
|
218
221
|
];
|
|
219
222
|
|
|
223
|
+
// The badge has three states, set from outside with \`foldspace badge\`:
|
|
224
|
+
// label - the small corner tag (default): who is attached, which mode
|
|
225
|
+
// working - a bar across the top: the agent is building in this window,
|
|
226
|
+
// please leave it alone
|
|
227
|
+
// ready - a bar: it is the human's turn, with the question to try
|
|
228
|
+
// The state lives in sessionStorage so it survives navigation inside the
|
|
229
|
+
// test window; \`foldspace badge\` writes it and the script redraws.
|
|
220
230
|
const badgeSrc = `(() => {
|
|
221
231
|
if (window.top !== window.self) return;
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
b.style.cssText = "position:fixed;top:0;left:0;z-index:2147483647;background:#3247F2;color:#fff;font:700 10px/1 ui-monospace,Menlo,monospace;letter-spacing:.12em;padding:5px 10px;border-bottom-right-radius:4px;pointer-events:none;box-shadow:0 1px 6px rgba(0,0,0,.35)";
|
|
228
|
-
document.body.appendChild(b);
|
|
232
|
+
const LABEL = ${JSON.stringify(badgeText)};
|
|
233
|
+
const KEY = "__foldspace_badge__";
|
|
234
|
+
const read = () => {
|
|
235
|
+
try { return JSON.parse(sessionStorage.getItem(KEY) || "null") || { state: "label" }; }
|
|
236
|
+
catch (e) { return { state: "label" }; }
|
|
229
237
|
};
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
238
|
+
const draw = () => {
|
|
239
|
+
if (!document.body) return;
|
|
240
|
+
const s = read();
|
|
241
|
+
if (s.state === "off") { document.getElementById("foldspace-dev-badge")?.remove(); return; }
|
|
242
|
+
let b = document.getElementById("foldspace-dev-badge");
|
|
243
|
+
if (!b) { b = document.createElement("div"); b.id = "foldspace-dev-badge"; document.body.appendChild(b); }
|
|
244
|
+
const want = s.state + "|" + (s.text || "");
|
|
245
|
+
if (b.dataset.fsState === want) return;
|
|
246
|
+
b.dataset.fsState = want;
|
|
247
|
+
const base = "position:fixed;top:0;left:0;z-index:2147483647;color:#fff;font:700 10px/1 ui-monospace,Menlo,monospace;letter-spacing:.12em;pointer-events:none;box-shadow:0 1px 6px rgba(0,0,0,.35);";
|
|
248
|
+
if (s.state === "working") {
|
|
249
|
+
b.style.cssText = base + "right:0;background:#3247F2;padding:9px 14px;font-size:12px;letter-spacing:.04em;text-align:center;";
|
|
250
|
+
b.textContent = s.text || "Foldspace is building your agent on this page. Please leave this window alone until Claude says it is ready.";
|
|
251
|
+
} else if (s.state === "ready") {
|
|
252
|
+
b.style.cssText = base + "right:0;background:#0F9D58;padding:9px 14px;font-size:12px;letter-spacing:.04em;text-align:center;";
|
|
253
|
+
b.textContent = s.text || "Ready - your turn. Open your agent and try it.";
|
|
254
|
+
} else {
|
|
255
|
+
b.style.cssText = base + "background:#3247F2;padding:5px 10px;border-bottom-right-radius:4px;";
|
|
256
|
+
b.textContent = LABEL;
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
draw();
|
|
260
|
+
document.addEventListener("DOMContentLoaded", draw);
|
|
261
|
+
setInterval(draw, 1000);
|
|
233
262
|
})();`;
|
|
234
263
|
|
|
235
264
|
// Flag every local session as test traffic so it is excluded from analytics and
|
|
@@ -699,6 +728,8 @@ async function prepare(ws, sessionId, url) {
|
|
|
699
728
|
|
|
700
729
|
{
|
|
701
730
|
const verification = await verifyPreparedPage(ws, sessionId);
|
|
731
|
+
// For scoring a session: the moment the customer could first see their agent.
|
|
732
|
+
if (verification.ok) recordEvent(root, "agent_visible", { mode: attachMode });
|
|
702
733
|
logLifecycle(
|
|
703
734
|
createLifecycleResult({
|
|
704
735
|
operation: "inspect_registration",
|
|
@@ -948,11 +979,11 @@ const ver = await connect(owned.version);
|
|
|
948
979
|
console.log(`Attached to ${ver.Browser} on :${port}`);
|
|
949
980
|
console.log(`Mode: ${attachMode}`);
|
|
950
981
|
console.log(`Agent: ${agentApiName} (product ${productId}, ${agentMode})`);
|
|
951
|
-
console.log(`Test: ${noTestMode ? "
|
|
982
|
+
console.log(`Test: ${noTestMode ? "off — conversations appear in the dashboard (pass --test-mode to hide them)" : "on"}`);
|
|
952
983
|
console.log(`Hosts: ${hostPatterns.join(", ")}`);
|
|
953
984
|
console.log(`Serving: ${path.relative(root, bundlePath)}`);
|
|
954
985
|
console.log(
|
|
955
|
-
`CDP:
|
|
986
|
+
`CDP: attached. \`foldspace observe\` reads this same window while attach runs; stop attach only to load rebuilt actions.\n`,
|
|
956
987
|
);
|
|
957
988
|
|
|
958
989
|
function printSessionSummary() {
|
package/bin/badge.mjs
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* foldspace badge <working|ready|label|off> [--text "<what to show>"]
|
|
4
|
+
*
|
|
5
|
+
* Sets the banner the attach badge draws across the top of the test window.
|
|
6
|
+
* The agent and the human share that one Chrome window, so the banner says
|
|
7
|
+
* whose turn it is:
|
|
8
|
+
* working - "Foldspace is building your agent on this page. Please leave
|
|
9
|
+
* this window alone until Claude says it is ready."
|
|
10
|
+
* ready - "Ready - your turn. Open your agent and try it." (pass --text
|
|
11
|
+
* with the question to try)
|
|
12
|
+
* label - back to the small corner tag
|
|
13
|
+
* off - remove it
|
|
14
|
+
* The state is kept in the page's sessionStorage, so it survives navigation;
|
|
15
|
+
* attach's badge script redraws within a second. Nothing else on the page is
|
|
16
|
+
* touched.
|
|
17
|
+
*/
|
|
18
|
+
import { CdpPage, parseArgs, printJson } from "../src/cdp-client.mjs";
|
|
19
|
+
import { resolveProjectDir } from "../src/upgrade.mjs";
|
|
20
|
+
import { badgePayload } from "../src/badge-core.mjs";
|
|
21
|
+
|
|
22
|
+
const projectDir = resolveProjectDir();
|
|
23
|
+
const { flags, rest } = parseArgs(process.argv.slice(2), ["text"]);
|
|
24
|
+
|
|
25
|
+
let payload;
|
|
26
|
+
try {
|
|
27
|
+
const unknown = Object.keys(flags).filter((name) => name !== "text");
|
|
28
|
+
if (unknown.length) throw new Error(`unknown option '--${unknown[0]}'`);
|
|
29
|
+
if (rest.length > 1) throw new Error(`unexpected argument '${rest[1]}'`);
|
|
30
|
+
payload = badgePayload(rest[0], flags.text);
|
|
31
|
+
} catch (error) {
|
|
32
|
+
console.error(`badge: ${error instanceof Error ? error.message : String(error)}`);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let page;
|
|
37
|
+
try {
|
|
38
|
+
page = await CdpPage.open({ projectDir });
|
|
39
|
+
const shown = await page.evaluate(
|
|
40
|
+
`sessionStorage.setItem("__foldspace_badge__", ${JSON.stringify(JSON.stringify(payload))});
|
|
41
|
+
const b = document.getElementById("foldspace-dev-badge");
|
|
42
|
+
return { url: location.href.split(/[?#]/)[0], hadBadge: Boolean(b) };`,
|
|
43
|
+
);
|
|
44
|
+
printJson({ ok: true, state: payload.state, ...(payload.text ? { text: payload.text } : {}), ...shown });
|
|
45
|
+
} catch (error) {
|
|
46
|
+
printJson({ ok: false, error: error instanceof Error ? error.message : String(error) });
|
|
47
|
+
process.exit(1);
|
|
48
|
+
} finally {
|
|
49
|
+
page?.close();
|
|
50
|
+
}
|
package/bin/inject.mjs
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* npm run inject -- --url https://app.other.com --product ABC123 --agent other-agent
|
|
12
12
|
* npm run inject -- --print # resolve target only, don't launch
|
|
13
13
|
*/
|
|
14
|
+
import { recordEvent } from "../src/session-events.mjs";
|
|
14
15
|
import fs from "fs";
|
|
15
16
|
import path from "path";
|
|
16
17
|
import os from "os";
|
|
@@ -143,7 +144,7 @@ fs.writeFileSync(
|
|
|
143
144
|
path.join(themeDir, "manifest.json"),
|
|
144
145
|
JSON.stringify({
|
|
145
146
|
manifest_version: 3,
|
|
146
|
-
name: "Foldspace
|
|
147
|
+
name: "Foldspace Agent Builder Theme",
|
|
147
148
|
version: "1.0",
|
|
148
149
|
description: "Paints the dev browser Foldspace blue.",
|
|
149
150
|
theme: {
|
|
@@ -196,7 +197,7 @@ function nameProfile() {
|
|
|
196
197
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
197
198
|
fs.writeFileSync(file, JSON.stringify(data));
|
|
198
199
|
};
|
|
199
|
-
const label = `Foldspace
|
|
200
|
+
const label = `Foldspace Agent Builder \u2014 ${target.name}`;
|
|
200
201
|
writeMerged(path.join(profileDir, "Local State"), (d) => {
|
|
201
202
|
d.profile = d.profile || {};
|
|
202
203
|
d.profile.info_cache = d.profile.info_cache || {};
|
|
@@ -279,6 +280,8 @@ fs.writeFileSync(
|
|
|
279
280
|
),
|
|
280
281
|
);
|
|
281
282
|
|
|
283
|
+
recordEvent(root, "chrome_opened", { url: startUrl });
|
|
284
|
+
|
|
282
285
|
// --- theme -------------------------------------------------------------
|
|
283
286
|
//
|
|
284
287
|
// Chrome 151 ignores --load-extension, so load the optional cosmetic theme
|