@ai-ecoverse/slicc-spectrum 1.99.3 → 1.101.0
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/README.md +20 -4
- package/dist/slicc-dummy.js +239 -23
- package/dist/slicc-dummy.js.map +3 -3
- package/dist/slicc-ui.js +639 -248
- package/dist/slicc-ui.js.map +3 -3
- package/dist/types/app/composer.d.ts +11 -0
- package/dist/types/app/markdown.d.ts +5 -1
- package/dist/types/app/settings.d.ts +9 -0
- package/dist/types/app/updates.d.ts +14 -1
- package/dist/types/dummy/index.d.ts +3 -2
- package/dist/types/dummy/updates.d.ts +5 -0
- package/dist/types/dummy.d.ts +2 -2
- package/dist/types/model/types.d.ts +27 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -356,6 +356,7 @@ They're also kept in `sessionStorage` (`slicc-spectrum.starting`, so per origin
|
|
|
356
356
|
Only a reload of the same tab brings them back (the navigation's type is `reload`); any other load, such as a duplicated tab that got a copy of the storage, clears them, so nothing is sent twice.
|
|
357
357
|
After a reload the rows come back in order with their text; attachments can't be restored, so a row that had any isn't sent on its own.
|
|
358
358
|
A row with text and lost files is held (`[data-held]`): it shows an alert icon instead of the clock, a second, muted line "Not sent: tides.csv wasn’t kept after reload." (or "Not sent: 2 files weren’t kept after reload.", `[data-lost]`), **Put back** (`data-action="restore-starting"`), which puts its text into the field (after any draft, on a new line), focuses it and drops the row so the files can be attached again, and **Dismiss**.
|
|
359
|
+
After **Put back**, a muted line under the field with a paperclip icon (`[data-reattach]`) names what to attach: "Attach tides.csv again.", "Attach tides.csv and notes.txt again." and, with more than three, "Attach these 4 files again: …"; it's a quiet button (`data-action="reattach"`) that opens the file picker. It follows the listed files by name: attaching tides.csv leaves "Attach notes.txt again.", other files don't change it, and removing a listed file brings it back; it hides while all of them are attached, and goes once the field is cleared or the message is sent. The list is kept with each agent's draft, and a file is listed once.
|
|
359
360
|
A row that had only attachments shows the alert icon, its file names and "Not kept after reload", and reads "Not sent. Attach the files again." (`[data-unsent]`) with **Dismiss**.
|
|
360
361
|
Neither kind is sent on arrival or holds the other rows back, which still go in order; they stay until they're put back or dismissed.
|
|
361
362
|
Text typed but not sent while it starts is kept for the page the same way, and stays in the field once the agent arrives.
|
|
@@ -418,6 +419,7 @@ The Browser rail button carries a dot (`data-driven`) while a tab is in use.
|
|
|
418
419
|
#### `<slicc-settings>`
|
|
419
420
|
|
|
420
421
|
Theme, the model and thinking level for new cones, composer and diff preferences (push to talk only where the browser has speech recognition), and accounts to connect, reconnect or disconnect.
|
|
422
|
+
When the `updates` port has `setDeveloper`, an Updates row has the **Developer mode** switch (`sp-switch[data-setting=developer]`, `checked` from `developer()` on every `items`), described as "Check npm for newer releases that aren't certified yet, and offer them as betas in Install / Update."; while `setDeveloper(on)` is pending it is disabled with `aria-busy` and a progress circle, and a rejection shows its message under it (`[data-error]`).
|
|
421
423
|
An account whose status is `signing-in` (or a non-API-key account while its `connect` is pending) reads “Signing in…”; when the port has `cancel(id)`, its row offers Cancel (`data-action="cancel-sign-in"`), and the rejected `connect` that follows shows no error.
|
|
422
424
|
Accounts with `group: 'mcp'` (MCP servers) get their own **MCP servers** heading (`.accounts[data-group=mcp]`).
|
|
423
425
|
An `api-key` account asks for its `secret`: “{provider} API key” by default, “{provider} token” for `secret: 'token'`; when it is `expired` it reads **API key rejected** or **Token rejected**.
|
|
@@ -472,11 +474,18 @@ It closes, clearing the field, when the request leaves `waiting` or while the va
|
|
|
472
474
|
#### `<slicc-updates>`
|
|
473
475
|
|
|
474
476
|
Install / Update: component states, version changes, last checks, download and link counts, action buttons, failures with Retry, and collapsible install logs behind a quiet chevron, which stay open across a layout switch.
|
|
477
|
+
A row with a pending version change (`from → to`, or a package's `version → offered`) and release notes shows a What’s new disclosure after the row's actions (in package rows, after the command chips), next to the Install log, `details[data-releases]` with `summary[data-action="whats-new"]`, which stays open across a layout switch like the log. It lists every release after the installed version up to the new one, newest first, each as `.release[data-version]` with its version linking to the release page (`a[data-action="release-notes"]`, named "Kernel 1.13.0 release notes") and its notes rendered as markdown in strict mode (`markdown(text, { strict: true })`: raw HTML tags are dropped and their text kept as plain text, only `https:` links stay links, and an image becomes a link with its alt text, so nothing loads; the notes' headings sit one level under the release's, through `level`). Notes over 8 KB (8,192 characters) per release are cut at the last line break before that, with "…" and a Read the full notes link (`[data-cut]`, named "Read the full notes for Kernel 1.13.0"); a release whose `url` isn't `https:` shows its version as plain text. The `<!-- cert-verify -->` block is dropped, and a block that starts with "certified" shows as a subtle Certified badge (`[data-certified]`); a heading line naming the version is dropped too, since the link already shows it. Without notes in the range there is no disclosure.
|
|
478
|
+
An up-to-date row (no pending change) links its installed release with Release notes next to the version; an installed optional package without one links its npm page instead (`a[data-action="npm-page"]`, named "npm page for Biome 2.2.4"). Pins and additions in Your packages show no link. Every link opens in a new tab.
|
|
475
479
|
An install shows one bar for update rows and package rows alike: it fills 0–50% while downloading and 50–100% while linking (`value` against `max-value="100"`). While the row runs it never goes back, not when pnpm's total grows and not when progress drops out between passes. While a download reports `bytes` with a total above 0, the first half fills by bytes and the value label reads "10.0 MB of 40.0 MB". Otherwise, and always while linking, the label falls back to the phase's "n of m" count. The bar starts at 0 again once the row settles.
|
|
476
480
|
Opens at boot until the agent is ready without failures.
|
|
477
481
|
Background updates stay in the header's update status; new failures reopen the panel, and after a reload into a boot that isn't ready yet such a panel closes once the agent is ready.
|
|
478
482
|
Every automatic close is saved to the layout.
|
|
479
483
|
When the port offers packages, an Optional packages section lists them below as compact rows, each with its state, version, download size, requirements, commands, progress, errors and Install log; packages never count in the header or keep the panel open at boot.
|
|
484
|
+
With a port that has `setDeveloper`, a row's `npm` check shows under its version: "npm: no newer release", "Checking npm…", its `reason` when the check failed (the state light stays), "{installed} (certified) · {latest} on npm, not offered: {reason}" (a certified update keeps its target, "6.6.0 (certified) → 6.7.0 · …", and so does a beta row, "6.13.5 beta · certified 6.6.0 → 6.7.0"), or, when `offered`, "{installed} (certified) · {latest} on npm, not certified" with "12 packages change in /opt/agent." (the folder in the label's parentheses; "1 package changes …"), followed by "@slicc/kernel-bridge and @slicc/vault stay at their certified versions." for `gated` ("… stays at its certified version." for one) and **Install beta**; `article[data-npm]` is the state.
|
|
485
|
+
A row with `beta` has a notice **Beta** badge by its name (`[data-beta]`), a notice "Beta installed" light while it is `current` or `installed` ("Up to date" means the certified version only; Ready to apply, Failed and the running states keep their words), reads "{version} beta · certified {certified}" and offers **Back to certified** next to its other actions; Install beta shows only while `npm` is `offered`.
|
|
486
|
+
While `npm` is `offered`, What’s new covers the releases from the installed version up to npm's `latest`, so an offered beta lists its own notes.
|
|
487
|
+
Install beta and Back to certified are secondary buttons and call `act`; without `setDeveloper` (a mirrored port) neither shows.
|
|
488
|
+
For now bios sets these on the agent row only; the other rows follow with bios.
|
|
480
489
|
Packages the user added with pnpm in `/opt/user`, or pinned, show up in their own Your packages section below it, under "Packages you added with pnpm in /opt/user. Seven’s updates keep them until you remove them." (plus "Pinned versions stay until you unpin them." while a row offers `unpin`), with a neutral Pinned light (a hold, not a success) or a positive Added light, their version against seven's, and Unpin or Remove.
|
|
481
490
|
Also available through View and the rails.
|
|
482
491
|
|
|
@@ -486,8 +495,8 @@ What agents remember, grouped by section: search, filter by scope and tag, expan
|
|
|
486
495
|
The scope picker starts on **Everyone** (`*`), which lists every memory with its scope on it (`.scope[data-scope]`); `global` reads **Shared** whatever the port calls it, and the cones and roles follow.
|
|
487
496
|
The count (“N of M memories”) counts the memories in the picked scope.
|
|
488
497
|
With a search or tag filter that hides everything it says “No memories match.”, otherwise “No memories yet.”, “No shared memories yet.” or “No memories for {scope} yet.”
|
|
489
|
-
Each memory is a row (`li.row[data-id]`, divided from the next by a rule) with one structure open or closed: the title line (`.head`, the disclosure button) keeps its chevron, title and marks where they are, and the text below it (`.content`) shows two lines closed and grows into the full text in place when the row opens, so opening only adds height below. It stays open across a layout switch. Open rows (`data-open`) take no fill, so nothing reads as selected; the chevron turns down. Height and chevron move like Spectrum's accordion (`--swc-animation-duration-100`, ease in and out), and not at all under `prefers-reduced-motion`. A click on a closed row's text opens it too.
|
|
490
|
-
An open row ends in an action row at the trailing end
|
|
498
|
+
Each memory is a row (`li.row[data-id]`, divided from the next by one straight hairline, a square-ended `--swc-border-width-100` rule in the neutral border colour that spans the row, even where the row's hover fill is rounded) with one structure open or closed: the title line (`.head`, the disclosure button) keeps its chevron, title and marks where they are, and the text below it (`.content`) shows two whole lines closed (the clamp leaves room for one paragraph gap, so a second paragraph's first line is never half cut and doesn't move when the row opens) and grows into the full text in place when the row opens, so opening only adds height below. It stays open across a layout switch. Open rows (`data-open`) take no fill, so nothing reads as selected; the chevron turns down. Height and chevron move like Spectrum's accordion (`--swc-animation-duration-100`, ease in and out), and not at all under `prefers-reduced-motion`. A click on a closed row's text opens it too.
|
|
499
|
+
An open row ends in an action row: first its tag and scope in words next to their icons (`.meta [data-label=tag]`, `[data-label=scope]`, such as “Project” and “harbor only”), since touch has no tooltips, while a closed row shows only the icons; the open row hides its title-line tag and scope icons (`visibility: hidden`, so the title keeps its place) instead of showing them twice; then at the trailing end **Edit** and **Forget** (`data-action="edit"`, `data-action="forget"`), Forget in the negative content colour and asking first with the destructive confirm.
|
|
491
500
|
At the end of the title line, small icons (`.mark`, `role="img"` with an `aria-label` and a tooltip) carry the metadata: the tag (`.tag[data-tag]`: User a file with a person, Feedback a comment, Project a briefcase), “From MEMORY.md” for free text outside an entry (`.from`), and under Everyone the scope (`.scope[data-scope]`: Shared a group of people, a cone a person, a role a person with a gear, labelled “Shared” or “{name} only”).
|
|
492
501
|
The tag is left out where the list already says it: under a section heading that names it (Projects, User, Feedback) and while the tag filter picks it.
|
|
493
502
|
Below 560 px of panel width, the search takes its own line over the two pickers.
|
|
@@ -806,7 +815,10 @@ Emit `items` whenever rows or readiness change.
|
|
|
806
815
|
Use `queued` for a row that waits for another component before it starts; it reads "Waiting" and doesn't count as updating.
|
|
807
816
|
Use `starting` for an installed row that is only starting up: it reads "Starting" without a checked line, and the header shows a neutral "Starting" only when nothing is failed, updating or ready.
|
|
808
817
|
A `failed` row with an installed `from` version reads "Update failed".
|
|
809
|
-
`act(id, action)` resolves when dispatched or rejects with a displayed error; actions are `retry`, `update-now`, `restart-agent` and `
|
|
818
|
+
`act(id, action)` resolves when dispatched or rejects with a displayed error; actions are `retry`, `update-now`, `restart-agent`, `reload`, `install-beta` and `back-to-certified` (the embedder asks for confirmation itself).
|
|
819
|
+
A row's and a package's optional `releases` (`UpdateRelease[]`) lists the GitHub releases the embedder knows for it, each with its `version`, the release page `url` and optional `notes` (the release body, semantic-release markdown as GitHub returns it). Send every release after the installed version up to the new one, so the panel can show the whole range, plus the installed one for its link; spectrum orders them by version and never fetches them itself. Leave it out, or leave out `notes`, when there's nothing to show.
|
|
820
|
+
Optional `developer()` (false by default) and `setDeveloper(on)`, on the owner's page only, turn developer mode on and off; `setDeveloper` resolves once settled or rejects with a displayed error, and the port emits `items` when either changes.
|
|
821
|
+
A row's optional `beta` (`{ version, certified }`) is set while a beta is installed, and its optional `npm` (`{ state, latest?, reason?, changes?: { count, gated } }`, `state` one of `checking`, `current`, `offered`, `not-offered` or `failed`) is `null` while developer mode is off.
|
|
810
822
|
Missing ports preserve existing adapters and report disconnected information in the panel.
|
|
811
823
|
Optional `packages()` returns `PackageItem` rows for optional tools, each with a short catalog `id`, its npm `package` (shown in the name's tooltip and at the top of its Install log), `label`, a one-sentence `description`, the `commands` it provides, `requires` (other package ids, read as "Uses Python" while it's installed, or "Installs Python too" on a row that isn't installed yet; an installed row leaves out a requirement that isn't installed, since its warning says so), optional `notes` (short plain-text lines shown muted under the row, such as "1 untested dependency: …"; leave it out when there's nothing to say), optional `warnings` (lines with an alert icon, `data-warnings`, such as "Needs Python, which isn't installed.
|
|
812
824
|
Install Python again to use it."; the dummy adds that one when a required package is removed), `state` (`available`, `queued`, `installing`, `installed`, `outdated`, `updating`, `removing` or `failed`), the installed `version`, the `offered` version and its `size` in bytes, `progress` (`InstallProgress`, or `null` for an indeterminate bar), `error`, `actions` (`install`, `update`, `remove`, `retry`, `unpin`) and an optional `log`.
|
|
@@ -867,6 +879,7 @@ An action is a secondary button that stays pending until `act(id, action)` settl
|
|
|
867
879
|
The close button, labelled "Dismiss: <title>", hides the notice at once and calls `dismiss(id)` synchronously; it shows again only if the port drops the notice from `list()` and brings it back later.
|
|
868
880
|
Emit `notices` whenever they change.
|
|
869
881
|
A notice with `dismissible: false` has no close button and stays until the port drops it.
|
|
882
|
+
An optional `session: true` marks a notice the embedder keeps for the session; the panel shows it like any other, and other fields pass through.
|
|
870
883
|
Hooks: `[data-notice="<id>"]`, buttons with `data-action`, `[data-dismiss]` and `[data-error]`.
|
|
871
884
|
|
|
872
885
|
#### `tabs` (optional)
|
|
@@ -960,9 +973,11 @@ Pass only the surfaces a follower has to `<slicc-app>` (agents, chat, terminal a
|
|
|
960
973
|
|
|
961
974
|
The dummy supports `createDummyModel({ updates: 'boot' | 'starting' | 'current' | 'available' | 'restart' | 'failure' })`, also selectable with `?updates=` on `/ui/`.
|
|
962
975
|
Fixtures stay stable for inspection; `model.updates.setScenario('current')` completes boot.
|
|
976
|
+
The dummy's kernel offers three releases from 1.12.0 to 1.13.0, two of them certified, the agent one release to 0.25.0, and the syntax grammars only their installed release; its uv package offers two releases, Python links its release and the other installed packages their npm pages.
|
|
963
977
|
Dummy Update now and Retry simulate download and link progress, and a dummy package install reports half its `size` in bytes on the way; Restart agent and Reload only change fixture state, never restart a real process or reload the browser.
|
|
964
978
|
`createDummyModel({ packages: 'mixed' | 'empty' | 'fail' | 'user' | 'elsewhere' | 'off' })` (or `?packages=` on `/ui/`, off by default) adds invented optional packages in every state: Install and Retry finish after a short wait and install missing requirements too, `fail` makes them fail, `user` adds a pinned agent-kit and two additions (one with a warning) that Unpin and Remove take out of the list, `elsewhere` has git installed for cone with pnpm and TypeScript there at an older version, whose Update keeps it in place, and Biome both from the panel and for cone, whose Remove leaves cone's copy, and removing Python while uv is installed asks through the `confirm` option (the `/ui/` page passes spectrum's `confirm`).
|
|
965
|
-
`
|
|
979
|
+
`createDummyModel({ beta: 'offered' | 'installed' | 'blocked' | 'failed' | 'off' })` (or `?beta=` on `/ui/`, off by default) turns developer mode on with an invented agent at 6.6.0 (certified) and 6.13.5 on npm, 12 packages and nothing gated: `offered` offers Install beta, `installed` runs the beta, `blocked` isn't offered because it needs a newer kernel, and `failed` is a failed beta install with Retry and Install beta; `setDeveloper` toggles it after a short wait (checking npm again when turned on), and Install beta and Back to certified simulate a short install.
|
|
980
|
+
`updateFixtures`, `betaFixtures`, `packageFixtures` and `DummyUpdates` are exported from `/dummy`.
|
|
966
981
|
`createDummyModel({ mounts: 'mount' | 'cancel' | 'fail' | 'needs' })` (or `?mounts=` on `/ui/`) turns on the optional mount members, which are off by default: `mountFolder` mounts invented files at `/mnt/photos`, resolves with `null`, or rejects, as `model.files.mount.scenario` says; `needs` also lists `/mnt/photos` in `needsFolder()` until a folder is inserted there.
|
|
967
982
|
`createDummyModel({ network: 'ok' | 'limited' | 'failing' | 'off' })` (or `?network=` on `/ui/`, default `limited`) sets the dummy network: slicc on this computer as a linked device (`route: 'link'`, with browser automation through it), the page fetch with CORS failures, or no route, all invented; `off` leaves out the port.
|
|
968
983
|
`model.network.setScenario()` switches it and Check again re-emits after a short wait.
|
|
@@ -1032,6 +1047,7 @@ Its buttons are plain Spectrum buttons that answer at once: Unlock unlocks, and
|
|
|
1032
1047
|
|
|
1033
1048
|
On phones the update status and the tray connection show only their status light (the tray's budget is hidden) and the network indicator a cloud icon, with the text in their labels and tooltips; the agent picker keeps room for the cone's name, cut with an ellipsis when long, and the bottom rail scrolls when needed.
|
|
1034
1049
|
The update status comes first so progress and failures remain reachable on narrow screens.
|
|
1050
|
+
While a row has `beta`, the update status shows a notice **Beta** badge (`[data-updates] [data-beta]`) after its light, or alone when nothing else applies, at every size; its tooltip and label add "{label} runs beta {version}, not certified", with the label's folder left out ("Agent runs beta 6.13.5, not certified").
|
|
1035
1051
|
|
|
1036
1052
|
### Kernel model
|
|
1037
1053
|
|
package/dist/slicc-dummy.js
CHANGED
|
@@ -1741,8 +1741,8 @@ var DummyAgent = class extends Emitter {
|
|
|
1741
1741
|
this.emit("message", { agentId, message: reply });
|
|
1742
1742
|
const steers = run.steers.splice(0);
|
|
1743
1743
|
for (const steer of steers) this.#post(agentId, this.#user(steer, "steer"));
|
|
1744
|
-
const
|
|
1745
|
-
({ steps, suggestion } = script(
|
|
1744
|
+
const latest2 = steers.at(-1);
|
|
1745
|
+
({ steps, suggestion } = script(latest2.text.trim(), latest2.attachments ?? []));
|
|
1746
1746
|
run.pace = this.#pace(agentId, steps);
|
|
1747
1747
|
reply = this.#reply(agent.model);
|
|
1748
1748
|
this.#post(agentId, reply);
|
|
@@ -4944,6 +4944,65 @@ var DummyTray = class extends Emitter {
|
|
|
4944
4944
|
|
|
4945
4945
|
// src/dummy/updates.ts
|
|
4946
4946
|
var checkedAt = Date.UTC(2026, 9, 8, 8, 42);
|
|
4947
|
+
var tag = (repo, version, notes2) => ({
|
|
4948
|
+
version,
|
|
4949
|
+
url: `https://github.com/harbor-demo/${repo}/releases/tag/v${version}`,
|
|
4950
|
+
...notes2 ? { notes: notes2 } : {}
|
|
4951
|
+
});
|
|
4952
|
+
var certified = (name2, version) => `
|
|
4953
|
+
|
|
4954
|
+
<!-- cert-verify -->
|
|
4955
|
+
certified @harbor/${name2}@${version} sha256=0f3c9a1e (covers ${version}; downstream: PASS)
|
|
4956
|
+
<!-- /cert-verify -->
|
|
4957
|
+
`;
|
|
4958
|
+
var notes = (repo, version, sections) => `# [${version}](https://github.com/harbor-demo/${repo}/compare/v${version}) (2026-10-07)
|
|
4959
|
+
|
|
4960
|
+
${sections}`;
|
|
4961
|
+
var releaseFixtures = {
|
|
4962
|
+
kernel: [
|
|
4963
|
+
tag(
|
|
4964
|
+
"harbor-kernel",
|
|
4965
|
+
"1.13.0",
|
|
4966
|
+
notes(
|
|
4967
|
+
"harbor-kernel",
|
|
4968
|
+
"1.13.0",
|
|
4969
|
+
"### Features\n\n* **fs:** watch a folder and get one event per batch of changes\n* **jobs:** `wait -n` returns the first job that finishes"
|
|
4970
|
+
) + certified("harbor-kernel", "1.13.0")
|
|
4971
|
+
),
|
|
4972
|
+
tag(
|
|
4973
|
+
"harbor-kernel",
|
|
4974
|
+
"1.12.2",
|
|
4975
|
+
notes(
|
|
4976
|
+
"harbor-kernel",
|
|
4977
|
+
"1.12.2",
|
|
4978
|
+
"### Bug Fixes\n\n* **net:** a slow DNS answer no longer stalls the next request"
|
|
4979
|
+
) + certified("harbor-kernel", "1.12.2")
|
|
4980
|
+
),
|
|
4981
|
+
tag(
|
|
4982
|
+
"harbor-kernel",
|
|
4983
|
+
"1.12.1",
|
|
4984
|
+
notes(
|
|
4985
|
+
"harbor-kernel",
|
|
4986
|
+
"1.12.1",
|
|
4987
|
+
"### Bug Fixes\n\n* **tty:** resizing a terminal mid-line keeps the cursor on its column"
|
|
4988
|
+
)
|
|
4989
|
+
),
|
|
4990
|
+
tag("harbor-kernel", "1.12.0")
|
|
4991
|
+
],
|
|
4992
|
+
agent: [
|
|
4993
|
+
tag(
|
|
4994
|
+
"harbor-agent",
|
|
4995
|
+
"0.25.0",
|
|
4996
|
+
notes(
|
|
4997
|
+
"harbor-agent",
|
|
4998
|
+
"0.25.0",
|
|
4999
|
+
"### Features\n\n* **tools:** the agent can read a tide table from a CSV file\n\n### Bug Fixes\n\n* **chat:** a long answer keeps its code blocks intact when it is cut"
|
|
5000
|
+
)
|
|
5001
|
+
),
|
|
5002
|
+
tag("harbor-agent", "0.24.0")
|
|
5003
|
+
],
|
|
5004
|
+
grammars: [tag("harbor-grammars", "4.5.0")]
|
|
5005
|
+
};
|
|
4947
5006
|
function updateFixtures(scenario) {
|
|
4948
5007
|
const components = [
|
|
4949
5008
|
["bios", "BIOS packages", "0.8.2"],
|
|
@@ -4963,7 +5022,8 @@ function updateFixtures(scenario) {
|
|
|
4963
5022
|
to: version,
|
|
4964
5023
|
checkedAt,
|
|
4965
5024
|
error: null,
|
|
4966
|
-
actions: []
|
|
5025
|
+
actions: [],
|
|
5026
|
+
...releaseFixtures[kind] ? { releases: releaseFixtures[kind] } : {}
|
|
4967
5027
|
}));
|
|
4968
5028
|
const agent = items[2];
|
|
4969
5029
|
if (scenario === "boot") {
|
|
@@ -5001,7 +5061,89 @@ function updateFixtures(scenario) {
|
|
|
5001
5061
|
}
|
|
5002
5062
|
return items;
|
|
5003
5063
|
}
|
|
5004
|
-
var
|
|
5064
|
+
var pin = "6.6.0";
|
|
5065
|
+
var latest = "6.13.5";
|
|
5066
|
+
var betaReleases = [
|
|
5067
|
+
tag(
|
|
5068
|
+
"harbor-agent",
|
|
5069
|
+
"6.13.5",
|
|
5070
|
+
notes(
|
|
5071
|
+
"harbor-agent",
|
|
5072
|
+
"6.13.5",
|
|
5073
|
+
"### Bug Fixes\n\n* **tools:** a tide table with a blank row no longer stops the import"
|
|
5074
|
+
)
|
|
5075
|
+
),
|
|
5076
|
+
tag(
|
|
5077
|
+
"harbor-agent",
|
|
5078
|
+
"6.10.0",
|
|
5079
|
+
notes(
|
|
5080
|
+
"harbor-agent",
|
|
5081
|
+
"6.10.0",
|
|
5082
|
+
"### Features\n\n* **chat:** answers can quote a buoy reading with its time and place"
|
|
5083
|
+
)
|
|
5084
|
+
),
|
|
5085
|
+
tag(
|
|
5086
|
+
"harbor-agent",
|
|
5087
|
+
"6.6.0",
|
|
5088
|
+
notes("harbor-agent", "6.6.0", "### Bug Fixes\n\n* **chat:** a pasted chart keeps its axes") + certified("harbor-agent", "6.6.0")
|
|
5089
|
+
)
|
|
5090
|
+
];
|
|
5091
|
+
var offered = { state: "offered", latest, changes: { count: 12, gated: [] } };
|
|
5092
|
+
var blocked = {
|
|
5093
|
+
state: "not-offered",
|
|
5094
|
+
latest,
|
|
5095
|
+
reason: "it needs kernel 1.14.0 or later"
|
|
5096
|
+
};
|
|
5097
|
+
var betaFailed = "Couldn\u2019t install the beta: pnpm stopped with an error, shown in the install log. Retry.";
|
|
5098
|
+
function betaActions(item) {
|
|
5099
|
+
return [
|
|
5100
|
+
...item.beta ? ["back-to-certified"] : [],
|
|
5101
|
+
...item.npm?.state === "offered" ? ["install-beta"] : []
|
|
5102
|
+
];
|
|
5103
|
+
}
|
|
5104
|
+
function withNpm(items, npm) {
|
|
5105
|
+
return items.map((item) => {
|
|
5106
|
+
if (item.kind !== "agent") return item;
|
|
5107
|
+
const next = { ...item, npm };
|
|
5108
|
+
const kept = item.actions.filter(
|
|
5109
|
+
(action) => action !== "install-beta" && action !== "back-to-certified"
|
|
5110
|
+
);
|
|
5111
|
+
return { ...next, actions: [...kept, ...betaActions(next)] };
|
|
5112
|
+
});
|
|
5113
|
+
}
|
|
5114
|
+
function betaFixtures(items, scenario) {
|
|
5115
|
+
return items.map((item) => {
|
|
5116
|
+
if (item.kind !== "agent") return item;
|
|
5117
|
+
const base = {
|
|
5118
|
+
...item,
|
|
5119
|
+
from: pin,
|
|
5120
|
+
to: pin,
|
|
5121
|
+
beta: null,
|
|
5122
|
+
npm: offered,
|
|
5123
|
+
releases: betaReleases
|
|
5124
|
+
};
|
|
5125
|
+
const next = scenario === "installed" ? {
|
|
5126
|
+
...base,
|
|
5127
|
+
from: latest,
|
|
5128
|
+
to: latest,
|
|
5129
|
+
beta: { version: latest, certified: pin },
|
|
5130
|
+
npm: { state: "current", latest }
|
|
5131
|
+
} : scenario === "blocked" ? { ...base, npm: blocked } : scenario === "failed" ? {
|
|
5132
|
+
...base,
|
|
5133
|
+
state: "failed",
|
|
5134
|
+
to: latest,
|
|
5135
|
+
error: betaFailed,
|
|
5136
|
+
log: `pnpm add @slicc/demo-agent@${latest}
|
|
5137
|
+
ERR_PNPM_FETCH_TIMEOUT Request timed out after 30000ms`
|
|
5138
|
+
} : base;
|
|
5139
|
+
const extra = betaActions(next);
|
|
5140
|
+
return {
|
|
5141
|
+
...next,
|
|
5142
|
+
actions: [...scenario === "failed" ? ["retry"] : item.actions, ...extra]
|
|
5143
|
+
};
|
|
5144
|
+
});
|
|
5145
|
+
}
|
|
5146
|
+
var offer = (id, label2, description, commands, offered2, size, requires) => ({
|
|
5005
5147
|
id,
|
|
5006
5148
|
package: `@harbor/${id}-wasm`,
|
|
5007
5149
|
label: label2,
|
|
@@ -5010,14 +5152,14 @@ var offer = (id, label2, description, commands, offered, size, requires) => ({
|
|
|
5010
5152
|
...requires ? { requires } : {},
|
|
5011
5153
|
state: "available",
|
|
5012
5154
|
version: null,
|
|
5013
|
-
offered,
|
|
5155
|
+
offered: offered2,
|
|
5014
5156
|
size,
|
|
5015
5157
|
progress: null,
|
|
5016
5158
|
error: null,
|
|
5017
5159
|
actions: ["install"]
|
|
5018
5160
|
});
|
|
5019
5161
|
var fetchFailed = "The download stopped. Check the connection and try again.";
|
|
5020
|
-
var yours = (project, name2, description, version,
|
|
5162
|
+
var yours = (project, name2, description, version, pin2, change = {}) => ({
|
|
5021
5163
|
id: `user:${project}:${name2}`,
|
|
5022
5164
|
package: name2,
|
|
5023
5165
|
label: name2.replace(/^@[^/]+\//, ""),
|
|
@@ -5028,9 +5170,9 @@ var yours = (project, name2, description, version, pin, change = {}) => ({
|
|
|
5028
5170
|
offered: null,
|
|
5029
5171
|
progress: null,
|
|
5030
5172
|
error: null,
|
|
5031
|
-
actions: [
|
|
5173
|
+
actions: [pin2.base === null ? "remove" : "unpin"],
|
|
5032
5174
|
origin: "user",
|
|
5033
|
-
pin: { ...
|
|
5175
|
+
pin: { ...pin2, project: `/opt/${project}` },
|
|
5034
5176
|
...change
|
|
5035
5177
|
});
|
|
5036
5178
|
var userPackages = () => [
|
|
@@ -5118,7 +5260,28 @@ function packageFixtures(scenario) {
|
|
|
5118
5260
|
notes: ["1 untested dependency: @harbor/wasi-sockets@0.4.2-1"],
|
|
5119
5261
|
actions: ["remove"]
|
|
5120
5262
|
});
|
|
5121
|
-
patch("
|
|
5263
|
+
patch("python", {
|
|
5264
|
+
releases: [
|
|
5265
|
+
{ version: "3.13.1", url: "https://github.com/harbor-demo/python-wasm/releases/tag/v3.13.1" }
|
|
5266
|
+
]
|
|
5267
|
+
});
|
|
5268
|
+
patch("uv", {
|
|
5269
|
+
state: "outdated",
|
|
5270
|
+
version: "0.8.1",
|
|
5271
|
+
actions: ["update", "remove"],
|
|
5272
|
+
releases: [
|
|
5273
|
+
{
|
|
5274
|
+
version: "0.9.2",
|
|
5275
|
+
url: "https://github.com/harbor-demo/uv-wasm/releases/tag/v0.9.2",
|
|
5276
|
+
notes: "## 0.9.2\n\n### Bug Fixes\n\n* `uv run` finds the project in a parent folder again"
|
|
5277
|
+
},
|
|
5278
|
+
{
|
|
5279
|
+
version: "0.9.0",
|
|
5280
|
+
url: "https://github.com/harbor-demo/uv-wasm/releases/tag/v0.9.0",
|
|
5281
|
+
notes: "## 0.9.0\n\n### Features\n\n* `uv add` keeps the comments in `pyproject.toml`"
|
|
5282
|
+
}
|
|
5283
|
+
]
|
|
5284
|
+
});
|
|
5122
5285
|
patch("esbuild", {
|
|
5123
5286
|
state: "installing",
|
|
5124
5287
|
actions: [],
|
|
@@ -5158,9 +5321,16 @@ var DummyUpdates = class extends Emitter {
|
|
|
5158
5321
|
#packages = [];
|
|
5159
5322
|
#fail = false;
|
|
5160
5323
|
#confirm;
|
|
5324
|
+
#beta;
|
|
5325
|
+
#developer = true;
|
|
5161
5326
|
constructor(scenario, clock2, options = {}) {
|
|
5162
5327
|
super();
|
|
5163
|
-
this.#
|
|
5328
|
+
this.#beta = options.beta;
|
|
5329
|
+
this.#items = this.#fixtures(scenario);
|
|
5330
|
+
if (options.beta) {
|
|
5331
|
+
this.developer = () => this.#developer;
|
|
5332
|
+
this.setDeveloper = (on) => this.#setDeveloper(on);
|
|
5333
|
+
}
|
|
5164
5334
|
this.#ready = scenario !== "boot" && scenario !== "starting";
|
|
5165
5335
|
this.#clock = clock2;
|
|
5166
5336
|
this.#confirm = options.confirm;
|
|
@@ -5290,8 +5460,32 @@ Done in 3.8s`
|
|
|
5290
5460
|
ready() {
|
|
5291
5461
|
return this.#ready;
|
|
5292
5462
|
}
|
|
5463
|
+
#fixtures(scenario) {
|
|
5464
|
+
const items = updateFixtures(scenario);
|
|
5465
|
+
if (!this.#beta) return items;
|
|
5466
|
+
const fixed = betaFixtures(items, this.#beta);
|
|
5467
|
+
return this.#developer ? fixed : withNpm(fixed, null);
|
|
5468
|
+
}
|
|
5469
|
+
async #setDeveloper(on) {
|
|
5470
|
+
await this.#clock.sleep(4);
|
|
5471
|
+
this.#developer = on;
|
|
5472
|
+
this.#npm(on ? { state: "checking" } : null);
|
|
5473
|
+
if (!on) return;
|
|
5474
|
+
await this.#clock.sleep(4);
|
|
5475
|
+
const agent = this.#items.find((item) => item.kind === "agent");
|
|
5476
|
+
this.#npm(this.#check(agent.from));
|
|
5477
|
+
}
|
|
5478
|
+
#check(version) {
|
|
5479
|
+
if (!this.#developer) return null;
|
|
5480
|
+
if (version === latest) return { state: "current", latest };
|
|
5481
|
+
return this.#beta === "blocked" ? blocked : offered;
|
|
5482
|
+
}
|
|
5483
|
+
#npm(npm) {
|
|
5484
|
+
this.#items = withNpm(this.#items, npm);
|
|
5485
|
+
this.emit("items", this.#items);
|
|
5486
|
+
}
|
|
5293
5487
|
setScenario(scenario) {
|
|
5294
|
-
this.#items =
|
|
5488
|
+
this.#items = this.#fixtures(scenario);
|
|
5295
5489
|
this.#ready = scenario !== "boot" && scenario !== "starting";
|
|
5296
5490
|
this.emit("items", this.#items);
|
|
5297
5491
|
}
|
|
@@ -5302,7 +5496,9 @@ Done in 3.8s`
|
|
|
5302
5496
|
async act(id, action) {
|
|
5303
5497
|
const item = this.#items.find((candidate) => candidate.id === id);
|
|
5304
5498
|
if (!item?.actions.includes(action)) throw new Error(`Update action unavailable: ${id}`);
|
|
5305
|
-
|
|
5499
|
+
const back = action === "back-to-certified";
|
|
5500
|
+
const beta = back || action === "install-beta" || action === "retry" && item.to === latest;
|
|
5501
|
+
if (action === "update-now" || action === "retry" || beta) {
|
|
5306
5502
|
this.#patch(id, {
|
|
5307
5503
|
state: "downloading",
|
|
5308
5504
|
progress: { phase: "download", done: 0, total: 95 },
|
|
@@ -5318,13 +5514,30 @@ Done in 3.8s`
|
|
|
5318
5514
|
});
|
|
5319
5515
|
await this.#clock.sleep(2);
|
|
5320
5516
|
}
|
|
5517
|
+
if (beta) {
|
|
5518
|
+
const version = back ? pin : latest;
|
|
5519
|
+
const next = {
|
|
5520
|
+
...this.#items.find((candidate) => candidate.id === id),
|
|
5521
|
+
state: "ready",
|
|
5522
|
+
from: version,
|
|
5523
|
+
to: version,
|
|
5524
|
+
progress: null,
|
|
5525
|
+
error: null,
|
|
5526
|
+
checkedAt,
|
|
5527
|
+
beta: back ? null : { version, certified: pin },
|
|
5528
|
+
npm: this.#check(version)
|
|
5529
|
+
};
|
|
5530
|
+
this.#patch(id, { ...next, actions: ["restart-agent", ...betaActions(next)] });
|
|
5531
|
+
return;
|
|
5532
|
+
}
|
|
5321
5533
|
const apply = action === "update-now" && ["agent", "bios", "kernel", "ui"].includes(item.kind);
|
|
5322
5534
|
this.#patch(id, {
|
|
5323
5535
|
state: apply ? "ready" : "installed",
|
|
5324
5536
|
from: apply ? item.from : item.to,
|
|
5325
5537
|
progress: null,
|
|
5326
5538
|
checkedAt,
|
|
5327
|
-
actions: apply ? [item.kind === "agent" ? "restart-agent" : "reload"] : []
|
|
5539
|
+
actions: apply ? [item.kind === "agent" ? "restart-agent" : "reload"] : [],
|
|
5540
|
+
...apply ? {} : { actions: betaActions(item) }
|
|
5328
5541
|
});
|
|
5329
5542
|
}
|
|
5330
5543
|
};
|
|
@@ -5431,7 +5644,7 @@ function refused(settings) {
|
|
|
5431
5644
|
if (bad)
|
|
5432
5645
|
throw vaultFailure("EINVAL", `The vault refused ${bad[0]}. Check that value and try again.`);
|
|
5433
5646
|
}
|
|
5434
|
-
function accountRecords(now2 = Date.now(),
|
|
5647
|
+
function accountRecords(now2 = Date.now(), pin2) {
|
|
5435
5648
|
return [
|
|
5436
5649
|
{
|
|
5437
5650
|
name: "MCP_DOCS_TOKEN",
|
|
@@ -5441,7 +5654,7 @@ function accountRecords(now2 = Date.now(), pin) {
|
|
|
5441
5654
|
source: "settings",
|
|
5442
5655
|
sourceDevice: null,
|
|
5443
5656
|
server: "docs",
|
|
5444
|
-
...
|
|
5657
|
+
...pin2 ? { url: pin2, pinned: true } : {},
|
|
5445
5658
|
domains: ["docs.example"],
|
|
5446
5659
|
rules: [{ domain: "docs.example", header: "Authorization", methods: [], paths: [] }],
|
|
5447
5660
|
exec: false,
|
|
@@ -6022,11 +6235,11 @@ var DummyVault = class extends Emitter {
|
|
|
6022
6235
|
await this.#time.sleep(5);
|
|
6023
6236
|
this.#unlocked();
|
|
6024
6237
|
for (const name2 of names) {
|
|
6025
|
-
const
|
|
6238
|
+
const pin2 = this.#list.audit.find(
|
|
6026
6239
|
(entry) => entry.type === "endpoints" && entry.pinned?.name === name2
|
|
6027
6240
|
);
|
|
6028
|
-
if (
|
|
6029
|
-
this.#log(name2, { type: "endpoints", unpinned:
|
|
6241
|
+
if (pin2?.type === "endpoints" && pin2.pinned)
|
|
6242
|
+
this.#log(name2, { type: "endpoints", unpinned: pin2.pinned });
|
|
6030
6243
|
}
|
|
6031
6244
|
this.emit("list", this.#list);
|
|
6032
6245
|
}
|
|
@@ -6063,20 +6276,20 @@ var DummyVault = class extends Emitter {
|
|
|
6063
6276
|
password: !!options.password,
|
|
6064
6277
|
...pick ? { pick: [...pick] } : {}
|
|
6065
6278
|
});
|
|
6066
|
-
const
|
|
6067
|
-
if (!pick) return { stage: "pick", candidates:
|
|
6279
|
+
const offered2 = kind === "keepass" ? [...candidates, duplicate] : candidates;
|
|
6280
|
+
if (!pick) return { stage: "pick", candidates: offered2.map((item) => ({ ...item })) };
|
|
6068
6281
|
const chosen = new Map(
|
|
6069
6282
|
pick.map(
|
|
6070
6283
|
(item) => typeof item === "string" ? [item, "secret"] : [item.name, item.kind]
|
|
6071
6284
|
)
|
|
6072
6285
|
);
|
|
6073
|
-
const taken =
|
|
6286
|
+
const taken = offered2.filter((item) => chosen.has(item.name) && !item.refused);
|
|
6074
6287
|
return {
|
|
6075
6288
|
stage: "done",
|
|
6076
6289
|
imported: taken.map((item) => this.#seal(item.name, `import:${kind}`, chosen.get(item.name))),
|
|
6077
|
-
skipped:
|
|
6290
|
+
skipped: offered2.filter((item) => !taken.includes(item)).map((item) => item.name),
|
|
6078
6291
|
reasons: Object.fromEntries(
|
|
6079
|
-
|
|
6292
|
+
offered2.flatMap((item) => item.refused ? [[item.name, item.refused]] : [])
|
|
6080
6293
|
)
|
|
6081
6294
|
};
|
|
6082
6295
|
}
|
|
@@ -6167,6 +6380,7 @@ function createDummyModel({
|
|
|
6167
6380
|
updates = "current",
|
|
6168
6381
|
packages = "off",
|
|
6169
6382
|
confirm,
|
|
6383
|
+
beta = "off",
|
|
6170
6384
|
mounts = "off",
|
|
6171
6385
|
network = "limited",
|
|
6172
6386
|
tailnet,
|
|
@@ -6247,6 +6461,7 @@ function createDummyModel({
|
|
|
6247
6461
|
tray: new DummyTray(tray, time),
|
|
6248
6462
|
updates: new DummyUpdates(updates, time, {
|
|
6249
6463
|
...packages === "off" ? {} : { packages },
|
|
6464
|
+
...beta === "off" ? {} : { beta },
|
|
6250
6465
|
confirm
|
|
6251
6466
|
}),
|
|
6252
6467
|
...network === "off" ? {} : { network: new DummyNetwork(network, time, tailnet, links, files3, leaders) },
|
|
@@ -6284,6 +6499,7 @@ export {
|
|
|
6284
6499
|
DummyVaultButton,
|
|
6285
6500
|
actionLimits,
|
|
6286
6501
|
approvalFixtures,
|
|
6502
|
+
betaFixtures,
|
|
6287
6503
|
browserHistory,
|
|
6288
6504
|
createDummyModel,
|
|
6289
6505
|
createGuestModel,
|