@falari/harness 0.0.0-stage → 0.8.9

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/CHANGELOG.md ADDED
@@ -0,0 +1,444 @@
1
+ # Changelog
2
+
3
+ All notable changes to the Falari Harness bundle. The bundle is installed into a
4
+ DeepSeek Harness profile; version numbers track the delivery milestones in
5
+ [docs/plan.md](docs/plan.md).
6
+
7
+ ## 0.8.9 — 2026-11-10
8
+
9
+ Any storage term can be typed, and the fee rules match the chain exactly.
10
+
11
+ - Upload → 存储时长 gains **自定义…**: a number plus days / months / years
12
+ (days = 24 h, months = 30 days, years = 365 days), converted to whole seconds
13
+ and sent as the duration. The field echoes the billing in words
14
+ (`≈ 30 days billed · expires 12/10/2026`), rejects anything under one day, and
15
+ blocks the upload button while the term is incomplete.
16
+ - Host accepts any whole second count from 0 (permanent) to the chain's
17
+ `max_intent_duration_seconds` (100 years), and refuses anything outside it.
18
+ The quote response now carries `billableDays` and `durationLimits`.
19
+ - **Fee rule fixed in the SDK**: `quoteStorageFee` now mirrors
20
+ `Params.RequiredFee` for long finite terms. Before, a 40-year quote was priced
21
+ by the day and came out *more expensive than permanent*; the chain switches to
22
+ the permanent price at 36 years (30-year window + 20% reserve), and the SDK now
23
+ does the same. Whole-day billing, rounded up, minimum one day, unchanged.
24
+ - SDK `@falari/agent` 0.2.6; new `duration-fee-cap.test.mjs` (4 checks).
25
+
26
+ ## 0.8.8 — 2026-11-10
27
+
28
+ The panel header wears the original render, not the cut-out.
29
+
30
+ - `FalariLogoImage` draws the supplied artwork as-is (dark backdrop included),
31
+ cropped around the cube so it fills the 46 px tile without the empty margin of
32
+ the 512 px source. It is inlined once as a data URI, so the client bundle stays
33
+ a single file (~28 KB base64).
34
+ - The sidebar keeps the vector mark: at 20 px a raster loses the `>_` glyph,
35
+ while the vector stays crisp at any DPI.
36
+ - `icon.png` (plugin list) remains the transparent cut-out from 0.8.5; the header
37
+ and the icon are intentionally different treatments of the same artwork.
38
+
39
+ ## 0.8.7 — 2026-11-10
40
+
41
+ The idle-lock window is a real setting, not a fixed number.
42
+
43
+ - Default stays **60 minutes**; the field now offers presets (15 / 30 / 60 / 120 /
44
+ 240 / never) **plus a free minute input**, so any value from 0 to 10080 (7 days)
45
+ can be typed. 0 means "never lock until DSH exits".
46
+ - The typed value is echoed in words next to the input (`90 → 1 h 30 min`,
47
+ `2880 → 2 d`), so a bare number is never ambiguous.
48
+ - The host validates the same range (`lockIdleMinutes` 0–10080, integer) and
49
+ rejects anything outside it.
50
+ - The security card states the active window: `已解锁 … · 空闲 60 分钟后自动锁定`.
51
+ - Tests: host +3 (90 saved, 10081 rejected, 0 = never), client +4 (duration
52
+ wording in both languages).
53
+
54
+ ## 0.8.6 — 2026-11-10
55
+
56
+ The PIN is asked for far less often, and never for a change that did not touch
57
+ the key.
58
+
59
+ - **A plugin row reload no longer locks.** Saving settings, toggling the bundle
60
+ or reinstalling constructs a new service in the same process; the decrypted
61
+ key now lives in a process-wide vault keyed by the sealed record's ciphertext,
62
+ so the new instance adopts it. A different key, a key re-sealed under a new
63
+ PIN, `Clear`, `Lock now` and process exit all still drop it.
64
+ - Default idle lock raised from 15 to **60 minutes**, still configurable
65
+ (`lockIdleMinutes`, 0 = never). The security card now states the real number:
66
+ `idle 60 min → auto-lock`.
67
+ - New `scripts/unit-vault.mjs` (6 checks): fresh service is locked, unlock works,
68
+ a reload keeps it, a different key does not inherit it, `Lock now` forgets it.
69
+
70
+ ## 0.8.5 — 2026-11-10
71
+
72
+ New plugin icon: the rendered cube, cut out to transparency.
73
+
74
+ - `icon.png` (256×256 RGBA, 48 KB) replaces `icon.svg`; `exports["./icon"]` and
75
+ `files` point at it, matching the convention other Harness plugins use.
76
+ - The source artwork was a 512×512 image on a dark backdrop. The backdrop is
77
+ removed by taking the convex hull of the lit faces (which keeps the cube's own
78
+ black edges and the `>_` glyph), filling it at 4× for a smooth edge, and
79
+ cropping to the alpha bounds with 6% padding, so the mark is centred at any
80
+ size.
81
+ - Verified over white and dark backgrounds and at 16/24/32/44/64/128 px.
82
+
83
+ ## 0.8.4 — 2026-11-10
84
+
85
+ One writer per queue ledger, across processes.
86
+
87
+ - The SDK takes `<upload-queue>.lock` around every queue pass: a JSON record with
88
+ `pid`, `host`, a `token` and a heartbeat every 15 s. A second DSH window (or a
89
+ second machine sharing the profile) defers instead of uploading the same task,
90
+ which is what produced `code 32: account sequence mismatch` when two drivers
91
+ signed batch commits at the same sequence.
92
+ - A lock whose heartbeat is older than 90 s, or whose pid is gone on this host,
93
+ is stale and is reclaimed: a crashed driver must not freeze the queue forever.
94
+ - Release only deletes its own token, so a stale takeover cannot be undone by the
95
+ slow original owner.
96
+ - `runUploadQueue()` reports `pending: true` while another process owns the
97
+ ledger, so the timer retries later without spinning.
98
+ - SDK `@falari/agent` 0.2.5, new test `upload-queue-lock.test.mjs` (live holder
99
+ defers, stale holder is reclaimed, lock released after a pass).
100
+
101
+ ## 0.8.3 — 2026-11-10
102
+
103
+ Two writers on one queue were racing the account sequence, and the idle lock was
104
+ able to stop a paid upload.
105
+
106
+ - **One queue writer per process.** A plugin row reload constructs a new service
107
+ while the previous one may still have an upload tick in flight; both then sign
108
+ batch commits against the same account sequence and the chain rejects the loser
109
+ with `code 32`. The service now retires the previous instance before taking
110
+ over, and the SDK refuses to start a second queue loop while a tick is still
111
+ running (`uploadQueueRunning`).
112
+ - **Sequence retries widened**: 6 attempts with 200–1200 ms backoff instead of 3
113
+ attempts at 150–300 ms, enough for a burst of competing transactions to drain.
114
+ - **The idle lock no longer interrupts an upload.** `hasActiveUploads()` reads
115
+ the live queue, and while anything is `queued`/`uploading` the auto-lock
116
+ restarts its clock instead of locking. `POST /key/lock` refuses with
117
+ `upload_in_progress` (423) unless `force: true`; the panel asks before forcing.
118
+ - The security card says when auto-lock is paused for an upload.
119
+
120
+ ## 0.8.2 — 2026-11-10
121
+
122
+ - The main account balance renders to four decimals (`12.3456 FAR`). It floors
123
+ rather than rounds, so the card can never claim more than the account holds;
124
+ the exact value stays in `balanceWei` for any calculation. The formatted
125
+ string from older Hosts is handled the same way.
126
+
127
+ ## 0.8.1 — 2026-11-10
128
+
129
+ The panel now wears the wallet's visual language.
130
+
131
+ Taken from the Android client's `ui/theme` (the frosted-glass D+E theme), adapted
132
+ to a web panel: the palette still resolves to Harness tokens, so light and dark
133
+ follow the host instead of hard-coding colours.
134
+
135
+ - Token layer in `client.js`: `colors` (ink / hairline / divider / four surfaces /
136
+ brand + soft fills / three orb hues / glass edge), plus `radius` (20 card, 16
137
+ dialog, 14 block, 10 field, 12 tile), `space` (22 page, 18 card, 14 section,
138
+ 12 row, 8 inline, 4 tight) and `type` (24 display … 10.5 kicker). Literal font
139
+ sizes in the page blocks were folded into the scale (58 sites).
140
+ - `Backdrop`: three blurred colour orbs behind the content, which is what makes
141
+ a translucent panel read as glass.
142
+ - `Card` is the single panel primitive: translucent surface, 1px highlight edge,
143
+ soft lift, 20px radius. `SectionTitle` gives every block the wallet's small
144
+ wide-bold label register.
145
+ - Header is one glass bar: the mark in a tinted tile, title + badges, hint below,
146
+ refresh on the right. Tabs became a floating segmented pill so the row reads as
147
+ a control, not a page divider.
148
+ - Buttons are gradient CTA / soft secondary / danger; badges are tinted fills
149
+ rather than outline rings; drop zone is a lifted area with a brand glyph.
150
+ - Data table: uppercase kicker headers, hairline row dividers, tabular numerals,
151
+ name + shortened intent id (full id in the tooltip).
152
+ - Task row: the intent id is shortened to match the table, the percentage is
153
+ promoted out of the muted grey.
154
+
155
+ Verified by rendering both palettes off the real component tree. Tests unchanged:
156
+ 25 + 18 + 73 + 58 = 174 checks green.
157
+
158
+ ## 0.8.0 — 2026-11-10
159
+
160
+ The Agent Key is sealed under a PIN; nothing readable is left on disk.
161
+
162
+ - New `lib/pin.js`: the `far_` string is encrypted with AES-256-GCM under a key
163
+ derived by scrypt (N=2^15, per-record 16-byte salt, 12-byte IV). Stored record
164
+ is `{v:2, kdf:'scrypt', salt, iv, tag, ct}`: no plaintext, and the KDF
165
+ parameters travel with it so a future change stays readable.
166
+ - **A PIN is mandatory when saving a key.** `POST /key` without one is refused
167
+ (`missing_pin`), so a profile can never quietly hold a clear-text key.
168
+ - The decrypted key lives only in this process's memory. `POST /key/lock`
169
+ drops it and stops the queue; `lockIdleMinutes` (default 15, 0 = never)
170
+ expires it after inactivity. A Host restart starts locked.
171
+ - A profile that already had a plaintext key is reported as `legacy`: the panel
172
+ flags it, uploads are refused with `pin_required`, and `POST /key/secure`
173
+ re-seals the same key in place with a PIN (no re-entry, no upload).
174
+ - Wrong PINs are counted: five misses cost a 30-second pause (`pin_blocked`,
175
+ 429). A wrong PIN and a tampered record fail identically, so the error cannot
176
+ be used as a decryption oracle.
177
+ - Panel: the key card is now a vault (set PIN / unlock / lock now / clear), the
178
+ upload tab gates itself with an inline PIN field, and the status card shows
179
+ `Locked` instead of pretending the chain is down.
180
+ - Errors: `agent_key_locked` / `pin_required` → 423, `wrong_pin` → 401,
181
+ `pin_blocked` → 429. `GET /health` reports `encrypted` and `locked`.
182
+ - Tests: new `scripts/unit-pin.mjs` (18 checks: round trip, wrong PIN, tampered
183
+ ciphertext/tag, malformed salt, PIN policy, Unicode normalisation), host 73,
184
+ client 58.
185
+
186
+ ## 0.7.6 — 2026-11-10
187
+
188
+ Upload authorization on the chunked path (the 0.6.5 fix missed it).
189
+
190
+ - Every shard goes through the **chunked** upload route (`/upload?offset=…`,
191
+ `?final=1`) whenever `ChainApi.uploadShardChunk` exists, and 0.6.5 only signed
192
+ the one-shot `/upload` variant. A miner in enforce mode kept answering
193
+ `upload auth: upload authorization is missing` for every shard, so a segment
194
+ never reached its receipt quorum.
195
+ - `ChainApi.uploadShardChunk` now accepts the EIP-712 envelope, and the chunked
196
+ v3 path attaches it on the direct route as well as the relay route (including
197
+ the empty final chunk that only reclaims a receipt).
198
+ - SDK `@falari/agent` 0.2.3; new SDK test pins the three headers on a direct
199
+ chunked upload.
200
+
201
+ ## 0.7.5 — 2026-11-10
202
+
203
+ The Data tab paints before the chain answers.
204
+
205
+ - The intent list is cached in `localStorage` (keyed by the master account, so a
206
+ key or network change never flashes another account's files) and rendered
207
+ immediately; the chain's answer replaces it when it lands, with
208
+ `Showing the last snapshot while the chain answers` next to the refresh button.
209
+ - The first open in a profile shows five skeleton rows that pulse
210
+ (`@keyframes falari-skeleton`), instead of a one-line "loading".
211
+ - Each row's title is the file name plus the intent id
212
+ (`DBX.app.zip intent_e152ffa100…571d84`); the copy button still copies the full
213
+ id and a private intent without a readable name shows `Untitled` rather than
214
+ repeating its id.
215
+
216
+ ## 0.7.4 — 2026-11-10
217
+
218
+ Audit of the upload and cancel flows; eight interaction defects fixed.
219
+
220
+ Upload:
221
+
222
+ - The estimate priced the **sum** of the selected files while every file becomes
223
+ its own order, so the deposit looked far larger than any single upload. It now
224
+ prices the largest file and says so when several are selected.
225
+ - A file that queued successfully stayed in the selection, so a retry sent it a
226
+ second time (a second escrow). Queued files are dropped from the list.
227
+ - The upload button is disabled while the quote says the budget is short, instead
228
+ of streaming a whole file and failing at the end.
229
+ - The progress line names the file being sent (`Sending 2/3 · name · size`); the
230
+ streaming phase had no feedback at all.
231
+ - The drop zone is keyboard reachable (role, tabIndex, Enter/Space).
232
+
233
+ Cancel:
234
+
235
+ - **Every unfinished upload can now be cancelled**, including `failed` and
236
+ `needs_source` rows: those hold an escrow but only offered Delete, so the money
237
+ was unreachable from the UI.
238
+ - A task with no on-chain order gets its own dialog (`Cancel upload`) that states
239
+ nothing is refunded, instead of a page of 0 FAR rows.
240
+ - A settled (`finalized`) order reports `unavailable: 'finalized'` and explains
241
+ that cancelling falls back to the early-termination rule, instead of showing a
242
+ zero escrow as if everything had been refunded.
243
+ - A failed preview read no longer aborts the termination, and a termination the
244
+ chain has not confirmed yet reports `pending` instead of claiming success.
245
+ - Escape and backdrop clicks are ignored while the cancel transaction is in
246
+ flight, so the dialog cannot vanish mid-mutation.
247
+
248
+ Tests: host 56, client 45.
249
+
250
+ ## 0.7.3 — 2026-11-10
251
+
252
+ Cancelling no longer loses the race against the upload driver.
253
+
254
+ - The chain rejected the terminate with `code 32: account sequence mismatch,
255
+ expected 28, got 27`: the upload driver's batch commit consumed the sequence
256
+ the cancel had signed against. The bundled SDK now rebuilds the unsigned
257
+ transaction against the fresh sequence and re-signs, up to three attempts, on
258
+ both the agent (`/tx/exec`) and owner (`/tx/build`) paths.
259
+ - `classifyChainError` reports the race as `sequence_mismatch` /
260
+ `retryable: true`, so a terminate that still fails is queued for another
261
+ attempt instead of parking in `failed` with a retry button.
262
+ - SDK version `@falari/agent` 0.2.2; three new SDK tests pin the retry, the
263
+ no-retry on an unrelated rejection, and the classification.
264
+
265
+ ## 0.7.2 — 2026-11-10
266
+
267
+ The status card speaks the interface language.
268
+
269
+ - **Permission tiers** render as 写入数据 / 管理数据 / 转账资金 (`Write data` /
270
+ `Manage data` / `Transfer funds`) instead of the wire names; an unknown tier
271
+ still falls back to its wire value, and the list separator follows the locale.
272
+ - **Key state** renders as 正常 / 已过期 / 已吊销 / 已冻结 / 已停用 / 待生效.
273
+ - Wire names stay unchanged in the API and in `falari_status`, so a signature,
274
+ a log line or a support thread still matches the chain.
275
+
276
+ ## 0.7.1 — 2026-11-10
277
+
278
+ - The refund dialog prints `0 FAR` instead of `-0 FAR` when a deduction is zero,
279
+ and the byte formatter keeps one decimal below 100 KiB.
280
+
281
+ ## 0.7.0 — 2026-11-10
282
+
283
+ Cancelling an upload asks first, and shows the money.
284
+
285
+ - **Cancel and refund** now opens a dialog instead of terminating on the click:
286
+ file, status, bytes already uploaded, the escrow the chain holds, the
287
+ cancellation rate (`voluntary_cancellation_fee_bps`), the withheld
288
+ compensation, the terminate service fee billed from the main balance, and the
289
+ amount going back to the main account.
290
+ - The numbers come from the chain, not from a local guess:
291
+ `GET /api/falari-harness/tasks/:id/refund` reads the intent (`locked_fee_wei`,
292
+ `uploaded_size`, `refunded_fee_wei`) and `/params`
293
+ (`voluntary_cancellation_fee_bps`, `min_cancellation_fee`,
294
+ `service_fees.terminate`) and mirrors `closeUnfinalizedIntent`: the fee is
295
+ `max(min_cancellation_fee, escrow * bps / 10000)` capped at the escrow, and an
296
+ intent with no committed receipt is closed without compensation.
297
+ - Confirming posts `/tasks/:id/cancel`, which terminates, then re-reads the
298
+ intent and shows the amount the chain actually released.
299
+ - `formatFar` / `formatBytes` helpers; the dialog closes on Escape or a backdrop
300
+ click.
301
+
302
+ ## 0.6.5 — 2026-11-10
303
+
304
+ Uploads reach miners that enforce the EIP-712 upload authorization.
305
+
306
+ - The bundled SDK signed the envelope only for relayed shards. A miner with its
307
+ own HTTP endpoint got a bare `POST /upload` and refused it with
308
+ `upload auth: upload authorization is missing`, so an upload ended with
309
+ `only 0/1 segments reached 4 receipts on 3 distinct miners`.
310
+ - `ChainApi.uploadShard` now accepts the envelope, and the v3 upload paths
311
+ (whole shard, chunk, empty final chunk) sign one per attempt on the direct
312
+ route as well. The nonce is single-use, so every retry re-signs.
313
+ - SDK: new tests assert the three `X-Falari-Upload-*` headers on a direct upload
314
+ and that an upload without a signer stays unsigned.
315
+
316
+ ## 0.6.4 — 2026-11-10
317
+
318
+ The panel paints the last known status instead of waiting for the chain.
319
+
320
+ - The Host keeps the status view in memory and in
321
+ `<dataDir>/status-cache.json` (0600, keyed by network + master + agent
322
+ address). `GET /api/falari-harness/status` answers from that cache immediately
323
+ and starts one background chain read when the cached view is older than 15
324
+ seconds; `?refresh=1` forces a read. A failed read keeps the last good numbers
325
+ and reports the error beside them.
326
+ - The panel and the settings page store the status in `localStorage`, so the
327
+ card renders before the first request completes, then polls every 5 seconds
328
+ and shows newer numbers when they arrive. The header shows `Updated HH:MM:SS`
329
+ and `refreshing…` while a read is in flight.
330
+ - `falari_status` gained `refresh: true` to bypass the cache. Clearing the key
331
+ removes the cached view on both sides.
332
+
333
+ ## 0.6.3 — 2026-11-10
334
+
335
+ The status card shows one address.
336
+
337
+ - The Agent Key's own address is gone from the status view and the panel: the
338
+ row now reads **Main address / 主地址** with the owner account, which is the
339
+ account that pays and owns the data.
340
+ - `keyStatusFrom` no longer returns `agentKey.agentAddress`, so the
341
+ `falari_status` tool result does not carry it either. The key's ordinal, state,
342
+ tiers, allowance and expiry still identify it.
343
+
344
+ ## 0.6.2 — 2026-11-09
345
+
346
+ The official Falari mark replaces the placeholder cube.
347
+
348
+ - `icon.svg` is the real logo: isometric cube measured from the supplied
349
+ artwork (hexagon, three lit faces, the inner edges drawn as bands inside the
350
+ faces, the `>_` prompt on the left face) in a 100×100 box.
351
+ - The panel header and the sidebar use the same geometry; a hairline silhouette
352
+ keeps the white cube readable on white surfaces, and the sidebar's 20px mark
353
+ uses a bolder stroke and darker faces.
354
+ - Gradient ids are per instance (`useId`), so several marks can render at once.
355
+
356
+ ## 0.6.1 — 2026-11-09
357
+
358
+ Configuration is reachable from the plugin itself, and the plugin metadata reads
359
+ in both languages.
360
+
361
+ - The panel gained a fourth tab, **Settings / 设置**, holding the Agent Key form,
362
+ the status card and the storage configuration — the plugin's own settings
363
+ surface, independent of the plugin-manager page.
364
+ - The Host now declares `configure({ auto: false })` to the Settings service, so
365
+ DSH knows this plugin ships its own configuration page instead of expecting a
366
+ schema-generated form. Skipped when the service is absent.
367
+ - `plugins.bundle.config` and `plugins.row.config` honor `view: 'summary'` with a
368
+ one-line summary, as the plugin manager expects for compact rows.
369
+ - `locale/*.json` titles and descriptions now carry both languages
370
+ (`Falari Data / Falari 数据`), so the plugin reads the same in any UI language.
371
+
372
+ ## 0.6.0 — 2026-11-09
373
+
374
+ The plugin's settings live on its own page, not in the global Settings dialog.
375
+
376
+ - Removed the `settings.section` registration; the Agent Key form, status and
377
+ configuration card now render through `plugins.bundle.config` (the bundle page)
378
+ and `plugins.row.config` (the row's configure page), both keyed by this package.
379
+ - Copy across the panel, the tools and the Host errors now points at
380
+ *Plugins → Falari Data → Configure* instead of the Settings dialog.
381
+ - Dropped the duplicated page header, since the host heads these pages with the
382
+ plugin title and description; two locale keys and three checks updated.
383
+
384
+ ## 0.5.0 — 2026-11-09
385
+
386
+ Settings become editable and the offline test suite is complete.
387
+
388
+ - `POST /api/falari-harness/config` writes network, default privacy, default
389
+ duration, upload size limit, download directory and the
390
+ upload-outside-workspace switch through the profile's config editor. Saving
391
+ restarts the plugin row, and the panel says so.
392
+ - Field-level validation for every accepted value; unknown fields and empty
393
+ patches are refused with a reason instead of being dropped.
394
+ - `scripts/unit-host.mjs`: config validation/defaults, file-name sanitizing, HTTP
395
+ status mapping and error mapping.
396
+ - `docs/test-plan.md`: automated coverage, live testnet steps and panel checks.
397
+ - Removed the placeholder component and 22 unused locale keys; English and
398
+ Chinese dictionaries stay at 141 keys each.
399
+
400
+ ## 0.4.0 — 2026-11-09
401
+
402
+ Intent listing and downloads.
403
+
404
+ - `GET /intents`, `GET /intents/:id`, `POST /downloads`, `GET /downloads/:id`.
405
+ - `falari_list` and `falari_download` Agent tools.
406
+ - Private intent names are decrypted locally when this machine holds the data
407
+ key; otherwise the intent id is shown.
408
+ - Downloads run as tasks with progress, are written only inside the download
409
+ directory, and never overwrite an existing file.
410
+ - Upload and download tasks share one list; the panel gained *My data* and
411
+ *Download* tabs.
412
+
413
+ ## 0.3.0 — 2026-11-09
414
+
415
+ Uploads.
416
+
417
+ - `POST /quote`, `POST /uploads` (raw octet-stream, streamed to a 0600 temp file)
418
+ and the upload queue driven by the SDK's persistent ledger.
419
+ - `falari_quote`, `falari_upload` and `falari_task` tools; uploads from the model
420
+ are restricted to the session working directory.
421
+ - Upload panel with drag-and-drop, privacy and duration, automatic quote with a
422
+ budget check, and per-task pause/resume/retry/cancel.
423
+ - Panel routes answer loopback peers only (`allowRemoteRoutes` to change);
424
+ cross-origin writes are rejected.
425
+ - `scripts/build-sdk.sh` bundles the unpublished `@falari/agent` and
426
+ `@falari/precompiles` packages into `lib/falari-sdk.cjs`.
427
+
428
+ ## 0.2.0 — 2026-11-09
429
+
430
+ Agent Key management.
431
+
432
+ - The key is stored in the DSH credential store as `falari-harness/agent-key`;
433
+ the browser never receives it and no route returns it.
434
+ - Key validation (format, registration, network) with distinct error codes.
435
+ - `falari_status` tool and the settings page status card: agent address, master,
436
+ tiers, allowance, balance, expiry, miners.
437
+
438
+ ## 0.1.0 — 2026-11-09
439
+
440
+ Initial bundle.
441
+
442
+ - `@falari/harness` bundle manifest, row patch, icon, locale metadata.
443
+ - Host half activates; Web half registers a sidebar entry, a data panel and a
444
+ settings page.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Falari
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,213 @@
1
- # Temporary Holding Version
1
+ # Falari Harness
2
+
3
+ Falari Data client for **DeepSeek Harness** (DSH): upload files to Falari
4
+ storage and download them back, with an Agent Key as the only credential. The
5
+ same six operations are exposed to the model as tools and to the user as a
6
+ panel.
7
+
8
+ - Design and milestone log: [docs/plan.md](docs/plan.md)
9
+ - Test plan: [docs/test-plan.md](docs/test-plan.md)
10
+ - Release notes: [CHANGELOG.md](CHANGELOG.md)
11
+
12
+ The plugin wears the Falari mark: `icon.png` is the rendered logo with a
13
+ transparent background for the plugin list; the panel header shows the original
14
+ render (dark backdrop, cropped to the cube); the sidebar draws crisp vector art
15
+ so the `>_` survives at 20 px.
16
+
17
+ ## Status
18
+
19
+ | Phase | Scope | State |
20
+ |---|---|---|
21
+ | M0 | Bundle scaffold, install, panel/settings placeholders | done — installed in the `desktop` profile (2026-11-09) |
22
+ | M1 | Agent Key storage, `falari_status` tool, settings page | done (v0.2.0) |
23
+ | M2 | Quotes, upload queue, upload panel, `falari_quote` / `falari_upload` / `falari_task` | done (v0.3.0) |
24
+ | M3 | Intent list, download/decrypt, `falari_list` / `falari_download` | done (v0.4.0) |
25
+ | M4 | Editable settings, unit tests, test plan | done (v0.5.0); live acceptance pending a testnet key |
26
+ | M5 | Repository layout, changelog, license, manifest check | done (v0.5.0) |
27
+
28
+ Live-chain verification still needs a real testnet Agent Key: the settings page
29
+ and every route/tool path are covered by the offline smoke tests, but nothing
30
+ here has performed a paid upload against the testnet yet.
31
+
32
+ The full design is in [docs/plan.md](docs/plan.md).
33
+
34
+ ## Install (development)
35
+
36
+ Open **Plugins → Add** in DeepSeek Harness and point it at this directory, or
37
+ from an agent session call the plugin manager tool:
38
+
39
+ ```text
40
+ plugin_manager action=install_bundle target=/Users/dimash/Documents/code/falari/clients/harness
41
+ ```
42
+
43
+ The bundle is installed as a local link (`link:...`). Host code changes need the
44
+ plugin row to reload (toggle the bundle off/on, or restart DSH); client code
45
+ changes need a page refresh.
46
+
47
+ Storage duration can be a preset (permanent / 30d / 90d / 1y / 5y / 10y) or any
48
+ custom term in days, months or years. Fees follow the chain's `RequiredFee`:
49
+ whole days rounded up, minimum one day, and any term past 36 years is charged the
50
+ permanent price instead of growing forever.
51
+
52
+ Cancelling an upload opens a confirmation dialog with the refund breakdown
53
+ (escrow, cancellation rate, withheld compensation, terminate service fee, amount
54
+ returned) read from the chain before anything is signed.
55
+
56
+ Status reads are cached: the Host answers from `<dataDir>/status-cache.json` and
57
+ refreshes the chain in the background (a 15 s window), and the panel keeps its own
58
+ `localStorage` copy so the card renders before the first request returns.
59
+
60
+ The Agent Key is stored **encrypted under a PIN**, never in clear text: scrypt
61
+ derives the AES-256-GCM key from the PIN, the decrypted key lives only in memory,
62
+ and the panel locks it on demand or after `lockIdleMinutes` of inactivity
63
+ (default 60, presets or any value from 0 to 10080 minutes). A plugin row reload
64
+ keeps the unlock while the process lives. A
65
+ profile that already holds a plaintext key is flagged and re-sealed in place.
66
+
67
+ The Agent Key and the storage defaults are configured in the plugin itself, never
68
+ in the global Settings dialog:
69
+
70
+ - **Falari Data panel → Settings tab** — always available, whatever the plugin
71
+ page renders.
72
+ - **Plugins → Falari Data** — the bundle page card, or the row's **Configure**
73
+ control, driven by the `plugins.bundle.config` / `plugins.row.config` slots.
74
+ Both may take a browser refresh to appear after an install.
75
+
76
+ ## Configuration
77
+
78
+ The row accepts an optional `config` block (defaults shown). Put overrides in the
79
+ profile patch:
80
+
81
+ ```yaml
82
+ - id: falari-harness
83
+ name: '@falari/harness'
84
+ config:
85
+ network: testnet # testnet | mainnet (mainnet is not live yet)
86
+ defaultPrivacy: public # public | private
87
+ defaultDuration: permanent # permanent | 30d | 90d | 1y | 5y | 10y
88
+ maxFileSizeBytes: 2147483648
89
+ allowUploadOutsideWorkspace: false
90
+ allowRemoteRoutes: false # keep the panel routes on loopback only
91
+ # gatewayUrl / retrievalUrl / explorerUrl override the network presets
92
+ # timeoutMs defaults to 60000
93
+ # dataDir defaults to <DSH_PROFILE_DIR>/falari-harness
94
+ ```
95
+
96
+ The Agent Key is **not** configuration: it lives in the DSH credential store as
97
+ the record `falari-harness/agent-key`.
98
+
99
+ ## Tools
100
+
101
+ | Tool | Purpose |
102
+ |---|---|
103
+ | `falari_status` | Key state, tiers, allowance, balance, miners (`refresh: true` bypasses the cache) |
104
+ | `falari_quote` | Price an upload (`path` or `size_bytes`) and check the budget |
105
+ | `falari_upload` | Queue a workspace file; `wait: true` waits up to two minutes |
106
+ | `falari_task` | Read one task or the recent list |
107
+ | `falari_list` | This account's intents, newest first |
108
+ | `falari_download` | Download one intent, verify roots, decrypt when private |
109
+
110
+ The plugin configuration page writes these values back through the profile's
111
+ config editor, so saving restarts the plugin row.
112
+
113
+ Agent uploads are restricted to the Session working directory unless
114
+ `allowUploadOutsideWorkspace` is enabled. Files the user picks in the panel are
115
+ not subject to that guard.
116
+
117
+ ## Build
118
+
119
+ The Host half bundles the unpublished Falari SDK (`@falari/agent`) and the
120
+ precompiles package into `lib/falari-sdk.cjs`, so installing the bundle needs no
121
+ npm package for them:
122
+
123
+ ```sh
124
+ npm run build:sdk
125
+ ```
126
+
127
+ Run this after pulling a newer `sdk/sdk-agent-ts`.
128
+
129
+ ## Repository
130
+
131
+ The project lives in this directory and is published as its own repository,
132
+ `github.com/falarichain/harness`; the surrounding Falari checkout only hosts the
133
+ SDK it builds against and the docs it follows.
134
+
135
+ The package name `@falari/harness` is the bundle identity: `package.json` `name`,
136
+ the Loader row in `cordis.patch.yml` and the `id` passed to
137
+ `window.__ModuleLoader__.load` must all stay equal, because the client-modules
138
+ scan matches the installed manifest name against the Loader specifier.
139
+
140
+ `package.json` exports `.`, `./client`, `./cordis.patch.yml`, `./package.json`,
141
+ `./locale/*.json` and `./icon`, and ships `index.js`, `client.js`, `lib/**`, the
142
+ patch, locales, icon, README, CHANGELOG and LICENSE. Everything else
143
+ (`scripts/`, `docs/`) is repository-only. `npm pack --dry-run` lists 17 files.
144
+
145
+ Publishing is a push of this directory to a new repository; nothing else in the
146
+ manifest needs to change.
147
+
148
+ ## Layout
149
+
150
+ ```text
151
+ clients/harness/
152
+ ├── .gitignore
153
+ ├── CHANGELOG.md # release notes per milestone
154
+ ├── LICENSE # MIT, © 2026 Falari
155
+ ├── README.md
156
+ ├── package.json # dsh.bundle + dsh.client manifest, exports, files
157
+ ├── cordis.patch.yml # inserts the falari-harness row
158
+ ├── icon.png
159
+ ├── index.js # Host entry: wiring
160
+ ├── client.js # Web client half: panel, tabs, settings
161
+ ├── lib/
162
+ │ ├── config.js # row config resolution + edit validation
163
+ │ ├── errors.js # shared error shape and mapping
164
+ │ ├── routes.js # /api/falari-harness/* (same-origin, loopback)
165
+ │ ├── sdk.js # loads the bundled SDK
166
+ │ ├── service.js # FalariDataService: key, quote, budget, uploads, downloads
167
+ │ ├── tools.js # six falari_* Agent tools
168
+ │ └── falari-sdk.cjs # generated by npm run build:sdk (committed on purpose)
169
+ ├── scripts/
170
+ │ ├── build-sdk.sh # esbuild bundle of @falari/agent + precompiles
171
+ │ ├── unit-host.mjs # pure-helper unit tests
172
+ │ ├── smoke-host.mjs # HTTP route smoke tests against a patched SDK
173
+ │ └── smoke-client.mjs # client.js render smoke test in a VM
174
+ ├── locale/{en,zh}.json # plugin metadata
175
+ └── docs/
176
+ ├── plan.md # v0.1 design and milestone log
177
+ └── test-plan.md # automated, live and panel checks
178
+ ```
179
+
180
+ ## Test
181
+
182
+ ```sh
183
+ npm test # unit + Host routes + client render, all offline
184
+ ```
185
+
186
+ | Suite | What it does |
187
+ |---|---|
188
+ | `scripts/unit-host.mjs` | config validation/defaults, name sanitizing, status and error mapping |
189
+ | `scripts/smoke-host.mjs` | patches the SDK client and drives every HTTP route: status, key, quote, raw upload, task actions, intents, download, config save, origin and loopback guards |
190
+ | `scripts/smoke-client.mjs` | loads `client.js` in a VM and server-renders the three slot components |
191
+
192
+ 83 checks pass today. The manual and live testnet steps are in
193
+ [docs/test-plan.md](docs/test-plan.md).
194
+
195
+ ## Security
196
+
197
+ - The Agent Key is a bearer credential with upload rights. It is stored only in
198
+ the Host credential store; the browser never receives or persists the raw
199
+ `far_...` string, and no route returns it.
200
+ - Upload routes accept only same-origin requests; cross-origin writes are
201
+ rejected with 403.
202
+ - Routes answer loopback peers only unless `allowRemoteRoutes` is enabled, so a
203
+ listener bound to a wider address cannot be used to change the key or pay for
204
+ uploads.
205
+ - Browser uploads stream into a 0600 temp file under the plugin data directory
206
+ and are deleted once the task completes or is cancelled; files older than 24
207
+ hours are swept at startup.
208
+ - The panel shows a private file's Data Key once the upload completes, because
209
+ losing it makes the ciphertext unrecoverable.
210
+ - Downloads are written only inside the configured `downloadDir` (default
211
+ `<dataDir>/downloads`); an `out_path` that escapes it is refused, and an
212
+ existing file name gains a ` (1)` suffix instead of being overwritten.
2
213
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.