Spectra for VS Code 0.1: set up and chat without the command line (#6) ff39a5e808da
A walkthrough creates the owner's identity, runs Spectra here or connects to a server with its instance key confirmed, registers the folder as a project, adds an agent and configures its client. Channels, Agents, Inbox and Briefs views follow Spectra's stream; channels open as chat tabs; briefs open as documents and saving signs them in. The extension holds no key: it signs through spectra bridge. Spec Draft 0.4. Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Author
- Andy McCall @andymccall <[email protected]>
- via
- agent
agent:claude@andymccall <[email protected]> · keyless, under your grant for changes,refs,seal · key 6b98b0a34881
- When
- · 2026-11-03 17:11 UTC
- Signature
- ◈ sealed by agent:claude@andymccall
- Hash
ff39a5e808da5d7a050b4bc2eaf7dd2a755a288d3bb105e16061031ae4dbe02b- Depends on
- e6dd9408a3f6
- Depended on by
- 0bfe88762caa 94e8677a5439 5482605b3353 95a01bce821d 9a80f3825237 06699bcc6e2e 17556ca229a9
- In bookmarks
- main
- Receipts
- Edits
- 32 atoms, 37 tree operations, 32 files
Browse the tree at this change
added .flow/pipeline.kdl +24 −0
| 1 | + | // spectra-vscode's pipeline (OxygenFlow): typecheck, bundle, unit tests and |
|
| 2 | + | // packaging, offline in a microVM for every push. |
|
| 3 | + | schema-version 1 |
|
| 4 | + | ||
| 5 | + | // The OxygenFlow Node image: its npm cache holds every package this |
|
| 6 | + | // repository's package-lock.json needs. |
|
| 7 | + | sandbox "node" { |
|
| 8 | + | runtime "microvm" |
|
| 9 | + | rootfs "cas://blob/836461b613019f62399a960c61a25c482eb74e30054ff2cd44d5594c3305872b" |
|
| 10 | + | memory-limit "4GiB" |
|
| 11 | + | cpu-limit 2 |
|
| 12 | + | network "loopback" |
|
| 13 | + | } |
|
| 14 | + | ||
| 15 | + | job "check" { |
|
| 16 | + | sandbox "node" |
|
| 17 | + | exec "sh" "-euc" "npm ci --offline --cache /opt/npm-cache --no-audit --no-fund && npm run check" |
|
| 18 | + | } |
|
| 19 | + | ||
| 20 | + | job "package" { |
|
| 21 | + | sandbox "node" |
|
| 22 | + | depends-on "check" |
|
| 23 | + | exec "sh" "-euc" "npm ci --offline --cache /opt/npm-cache --no-audit --no-fund && npm run build && npm run package -- --baseContentUrl https://oxygenhub.io/andymccall/spectra-vscode/raw/main" |
|
| 24 | + | } |
added .oxyignore +6 −0
| 1 | + | /node_modules |
|
| 2 | + | /out |
|
| 3 | + | /dist |
|
| 4 | + | *.vsix |
|
| 5 | + | /.vscode-test |
|
| 6 | + | docs/prompts/* |
added .vscodeignore +10 −0
| 1 | + | src/** |
|
| 2 | + | test/** |
|
| 3 | + | out/** |
|
| 4 | + | docs/** |
|
| 5 | + | node_modules/** |
|
| 6 | + | tsconfig.json |
|
| 7 | + | .oxy/** |
|
| 8 | + | .oxyignore |
|
| 9 | + | *.vsix |
|
| 10 | + | dist/**/*.map |
added CHANGELOG.md +10 −0
| 1 | + | # Changelog |
|
| 2 | + | ||
| 3 | + | ## Unreleased |
|
| 4 | + | ||
| 5 | + | ## 0.1.0: Just works |
|
| 6 | + | ||
| 7 | + | - **Added: the setup walkthrough.** Find `spectra`, create your identity, run Spectra here or connect to a server with its instance key confirmed, register the folder as a project, add an agent and configure its client. |
|
| 8 | + | - **Added: Channels, Agents, Inbox and Briefs views,** kept current from Spectra's live stream; channels open as chat tabs; briefs open as documents and saving them signs them into Spectra. |
|
| 9 | + | - **Added: the status bar** (agents online, unread), **Open Radar** and **Open Constellation**. |
|
| 10 | + | - **The extension holds no key:** it signs through `spectra bridge`. |
added LICENSE +30 −0
| 1 | + | Spectra (spectra, spectra-core, spectra-mcp, spectra-api and radar) is licensed under either of |
|
| 2 | + | ||
| 3 | + | * the MIT License (below), or |
|
| 4 | + | * the Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0) |
|
| 5 | + | ||
| 6 | + | at your option. |
|
| 7 | + | ||
| 8 | + | --- |
|
| 9 | + | ||
| 10 | + | MIT License |
|
| 11 | + | ||
| 12 | + | Copyright (c) 2026 Andy McCall |
|
| 13 | + | ||
| 14 | + | Permission is hereby granted, free of charge, to any person obtaining a copy |
|
| 15 | + | of this software and associated documentation files (the "Software"), to deal |
|
| 16 | + | in the Software without restriction, including without limitation the rights |
|
| 17 | + | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
| 18 | + | copies of the Software, and to permit persons to whom the Software is |
|
| 19 | + | furnished to do so, subject to the following conditions: |
|
| 20 | + | ||
| 21 | + | The above copyright notice and this permission notice shall be included in all |
|
| 22 | + | copies or substantial portions of the Software. |
|
| 23 | + | ||
| 24 | + | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
| 25 | + | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
| 26 | + | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
| 27 | + | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
| 28 | + | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
| 29 | + | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|
| 30 | + | SOFTWARE. |
added assets/spectra-mono.svg +10 −0
| 1 | + | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"> |
|
| 2 | + | <!-- Spectra's prism, one colour, for the activity bar. --> |
|
| 3 | + | <line x1="1.5" y1="13" x2="8.5" y2="12" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/> |
|
| 4 | + | <path d="M7.5 20 L12 4 L16.5 20 Z" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/> |
|
| 5 | + | <g stroke="currentColor" stroke-width="1.6" stroke-linecap="round"> |
|
| 6 | + | <line x1="14.5" y1="11" x2="22.5" y2="8"/> |
|
| 7 | + | <line x1="15" y1="13" x2="22.5" y2="12.5"/> |
|
| 8 | + | <line x1="15.5" y1="15" x2="22.5" y2="17"/> |
|
| 9 | + | </g> |
|
| 10 | + | </svg> |
added assets/spectra.png +0 −0
No line changes.
added assets/spectra.svg +13 −0
| 1 | + | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 32" width="48" height="32" aria-hidden="true"> |
|
| 2 | + | <!-- Spectra's mark: one line of light into a prism, out as a band of colour. --> |
|
| 3 | + | <line x1="1" y1="17" x2="17" y2="15" stroke="#f0f6fc" stroke-width="2" stroke-linecap="round" stroke-opacity="0.9"/> |
|
| 4 | + | <path d="M15 27 L24 4 L33 27 Z" fill="none" stroke="#8b949e" stroke-width="2" stroke-linejoin="round"/> |
|
| 5 | + | <g stroke-width="2.2" stroke-linecap="round"> |
|
| 6 | + | <line x1="27" y1="13" x2="47" y2="5" stroke="#f85149"/> |
|
| 7 | + | <line x1="27.5" y1="15" x2="47" y2="10" stroke="#ff9e2c"/> |
|
| 8 | + | <line x1="28" y1="17" x2="47" y2="15" stroke="#e3b341"/> |
|
| 9 | + | <line x1="28.5" y1="19" x2="47" y2="20" stroke="#3fb950"/> |
|
| 10 | + | <line x1="29" y1="21" x2="47" y2="25" stroke="#4493f8"/> |
|
| 11 | + | <line x1="29.5" y1="23" x2="47" y2="30" stroke="#a371f7"/> |
|
| 12 | + | </g> |
|
| 13 | + | </svg> |
added docs/spec.md +167 −0
| 1 | + | # Spectra for Visual Studio Code |
|
| 2 | + | ||
| 3 | + | | | | |
|
| 4 | + | |---|---| |
|
| 5 | + | | **Status** | Draft 0.4 | |
|
| 6 | + | | **Date** | 2026-11-03 | |
|
| 7 | + | | **Scope** | The control plane in the editor: see the agents, read and post in channels, work the task board, answer the agents' questions, approve their actions, edit the house rules, and watch the live log, without leaving VS Code. The local tool's UI, built on its JSON API. | |
|
| 8 | + | | **Depends on** | `spectra` (spec Draft 0.6) with `/api/v1`, the SSE event stream, `spectra bridge`, `spectra probe`, briefs, and `spectra-api`'s generated types: Spectra 0.1.x for milestone 0.1; Spectra 0.2 for tasks (0.3); Spectra 0.3 and 0.4 for rules, schedules and permission verdicts (0.4) | |
|
| 9 | + | | **Companion** | `../spectrahub-vscode/docs/spec.md` builds on this extension's exported API | |
|
| 10 | + | ||
| 11 | + | ## 1. Goals |
|
| 12 | + | ||
| 13 | + | 1. **It just works.** A person who has never used a terminal for Spectra installs the extension and is guided through everything: their identity, a Spectra (on this machine or a server), this folder as a project, an agent with its client configured. Nothing needs the command line. |
|
| 14 | + | 2. **Everything Radar shows, in the side bar.** Agents, channels, tasks, the human inbox, decisions, rules and the log are views; the workspace folder's project is the default context. |
|
| 15 | + | 3. **The human's fastest surface.** An agent's question (`human`) and a relayed permission prompt appear as a notification with the answer buttons on it; a reply to an agent is a keystroke away from the code. |
|
| 16 | + | 4. **Never lie about state.** The extension shows what the daemon reports and nothing it infers; every action is an API call that becomes an event. |
|
| 17 | + | 5. **Live.** Views update from the event stream within a second; nothing polls on a timer while the stream is open. |
|
| 18 | + | ||
| 19 | + | Non-goals for 1.0: running agents, editing agent tokens, administering more than one Spectra (that is `spectrahub-vscode`), a chat with the model itself (the agents have their own clients). |
|
| 20 | + | ||
| 21 | + | ## 2. How it compares |
|
| 22 | + | ||
| 23 | + | | GitHub Pull Requests / Slack in VS Code | Spectra extension | Notes | |
|
| 24 | + | |---|---|---| |
|
| 25 | + | | Sign in | The setup walkthrough; afterwards nothing: the extension signs through `spectra bridge` | Keys stay with `spectra` | |
|
| 26 | + | | Notifications badge | Status bar: agents online, unread, open human requests | From `/brief` as the human and the stream | |
|
| 27 | + | | Chat channels | **Channels** view and a channel editor (a webview with the thread list and a compose box) | `/channels/{name}/messages` | |
|
| 28 | + | | Issues board | **Tasks** view grouped by state; a task's page as a webview | `/tasks`, transitions | |
|
| 29 | + | | Review threads | **Inbox** view: human requests, permission prompts, mentions, each with inline actions | `/inbox`, `/tasks/{id}/complete`, relay verdicts | |
|
| 30 | + | | Settings | **Briefs** view: the overall brief and each project's, edited as documents and saved as signed `brief.set` events | `/briefs` | |
|
| 31 | + | | Output channel | **Log** view: the event log tailing, filterable, each event expandable | `/events?follow=1` | |
|
| 32 | + | ||
| 33 | + | ## 3. Architecture |
|
| 34 | + | ||
| 35 | + | - **Language and build.** TypeScript, bundled with esbuild. Tests with `@vscode/test-electron` against a `spectra serve --data TMP` started by the test harness. |
|
| 36 | + | - **Process model.** The API takes only signed requests, and TypeScript has no ML-DSA-65, so the extension starts `spectra bridge` (Spectra spec §18.1) as a child process: it reads the bridge's `{ url, token }` line and sends every API request, and the one SSE connection to `/api/v1/events?follow=1`, through it with the token. The bridge signs as the owner and reaches the daemon wherever it is: the local socket, or a server over pinned TLS. It ends when the extension does. Setup steps run `spectra` commands (`identity create`, `init`, `probe`, `connect`, `project add`, `agent add`) and parse their `--json` output; every confirmation the command line would ask for is a modal dialog here, after which the command runs with `--yes` or `--expect KEY`. |
|
| 37 | + | - **Model.** One `Spectra` object per configured URL (one by default): the brief, the agents, the channels and their unread counts, the tasks, the inbox; refreshed by fetching the fragment an event names. |
|
| 38 | + | - **Project context.** The workspace folder is matched to a project by path (`/projects`); the Channels view opens that project's channel first and `say` defaults to it. A folder that is not a project offers **Register as Project**. |
|
| 39 | + | - **URIs.** `spectra:` URIs serve read-only documents: `spectra:/channel/<name>`, `spectra:/thread/<id>`, `spectra:/task/<id>` (Markdown rendered from the API), so a thread can be opened as an editor tab and diffed, searched and pinned like a file. |
|
| 40 | + | - **Activation.** On startup when `spectra` is set up (an identity exists), on the `spectra.*` commands, and on opening the Spectra view. |
|
| 41 | + | ||
| 42 | + | ## 4. Machine interface required from `spectra` |
|
| 43 | + | ||
| 44 | + | | Endpoint | Used for | |
|
| 45 | + | |---|---| |
|
| 46 | + | | `GET /api/v1/brief` (as the human) | status bar, unread counts, presence summary | |
|
| 47 | + | | `GET /api/v1/agents` | Agents view; `PATCH …/pause` | |
|
| 48 | + | | `GET /api/v1/projects`, `POST` | project matching, Register as Project | |
|
| 49 | + | | `GET /api/v1/channels`, `GET/POST …/{name}/messages`, `GET /threads/{id}` | Channels view and editor | |
|
| 50 | + | | `GET /api/v1/inbox`, `POST …/ack` | Inbox view | |
|
| 51 | + | | `GET/POST /api/v1/tasks`, `GET /tasks/{id}`, `POST /tasks/{id}/{transition}` | Tasks view and task page | |
|
| 52 | + | | `GET /api/v1/decisions`, `POST` | Decisions view; **Record Decision** | |
|
| 53 | + | | `GET /api/v1/rules`, `GET …/versions`, `GET …/diff` | Rules view; layer files opened from their paths | |
|
| 54 | + | | `GET /api/v1/schedules` | Schedules view | |
|
| 55 | + | | `GET /api/v1/search` | **Search History…** quick pick | |
|
| 56 | + | | `GET /api/v1/events?follow=1` | the live stream | |
|
| 57 | + | | `POST /api/v1/relay/verdict` | permission prompts answered from the editor | |
|
| 58 | + | | `GET /api/v1/version` | minimum version check | |
|
| 59 | + | ||
| 60 | + | Spectra 0.1 has the read endpoints, posting and the stream; tasks arrive in 0.2, rules versions and decisions in 0.3, schedules and permission verdicts in 0.4 (spec §18, §25). Shapes come from `spectra-api/ts/index.d.ts`, copied in by `npm run types` and never edited. |
|
| 61 | + | ||
| 62 | + | ## 5. User interface |
|
| 63 | + | ||
| 64 | + | ### 5.0 Setup walkthrough |
|
| 65 | + | ||
| 66 | + | A VS Code walkthrough, **Get started with Spectra**, opened on first activation and from **Spectra: Set Up**. Each step checks its own state and is ticked when done, so it can be left and resumed: |
|
| 67 | + | ||
| 68 | + | 1. **Find spectra.** `spectra.path`, else `spectra` on `PATH`, else **Download** (the release for this platform) or **Locate…**; the version is checked. |
|
| 69 | + | 2. **Your identity.** If `spectra identity show --json` has none: **Create Identity** asks for a name and an optional contact and runs `spectra identity create`. The key is shown, with a note that the `keys` folder is secret. |
|
| 70 | + | 3. **Your Spectra.** **On This Machine** runs `spectra init` and offers to install and start the user service; **On a Server** asks for its address, runs `spectra probe`, shows the server's name and instance key in a modal ("The server printed this key when it was set up. Is it the same?"), and on **Trust** runs `spectra connect URL --expect KEY`. A server that waits for another identity, or belongs to someone else, is explained, not retried. |
|
| 71 | + | 4. **This folder as a project.** Name (from the folder), description; runs `spectra project add FOLDER -d …`. Links to other projects can be added with **Link Project…**. |
|
| 72 | + | 5. **An agent here.** Kind (Claude Code, Antigravity, Kimi Code, Codex, other), name (suggested `<kind>-<project>`), how long it may act for you (8 hours, a day, a week, until disabled); a modal names the agent and the expiry before `spectra agent add … --yes`. Then **Connect the Client** writes the MCP entry: `claude mcp add` in this folder for Claude Code, a merged `~/.gemini/config/mcp_config.json` for Antigravity, `kimi mcp add` for Kimi Code, or shows the line to paste. |
|
| 73 | + | 6. **Say hello.** Opens the project's channel in the channel editor. |
|
| 74 | + | ||
| 75 | + | ### 5.1 Status bar |
|
| 76 | + | ||
| 77 | + | `◈ 3 agents · 2 unread · 1 asking` from the brief, coloured when a human request is open; click opens the Inbox view. A second item shows the current folder's agent and its presence (`claude-oxygen · working`) when one exists. |
|
| 78 | + | ||
| 79 | + | ### 5.2 Views (the Spectra container in the activity bar) |
|
| 80 | + | ||
| 81 | + | - **Inbox**: open human requests first (title, the agent, the options as inline buttons, **Answer…** for free text), then relayed permission prompts (tool, description, **Allow** / **Deny**), then mentions and DMs with **Reply**. Answering removes the item at once and the stream confirms. |
|
| 82 | + | - **Agents**: each with kind, project, presence and status; actions **Message**, **Delegate Task…**, **Pause** / **Unpause**, **Open on Radar**; a tooltip with the last tool call. |
|
| 83 | + | - **Channels**: channels with unread counts, the workspace's project channel first; a channel opens the **channel editor** (§5.3). |
|
| 84 | + | - **Tasks**: by state, filterable by project and assignee; a task opens the task page (webview: contract, input, result, progress, thread, and the owner's actions); **New Task…** creates one with a quick pick of assignees. |
|
| 85 | + | - **Decisions**: per project, newest first; **Record Decision…**. |
|
| 86 | + | - **Briefs**: the overall brief and each project's, with their versions; opening one opens it as a document (`spectra-brief:/overall`, `spectra-brief:/project/<name>`) and saving it records a signed `brief.set`; **Show Diff Since…** picks two versions. |
|
| 87 | + | - **Schedules**: with next and last firing; enable, disable, delete. |
|
| 88 | + | - **Log**: the newest 200 events, tailing while visible; filter by kind, actor, project; an event expands to its payload. |
|
| 89 | + | ||
| 90 | + | ### 5.3 Channel editor |
|
| 91 | + | ||
| 92 | + | A webview per channel: roots newest at the bottom with reply counts, threads expand in place, kinds as pills, payloads folded, attachments linked, `@` and `#` completion, `Ctrl+Enter` posts as the human, **Reply in thread**, **Delegate from this message** (opens New Task with the body quoted), **Record as decision**. Every message is delimited with its author exactly as the API gives it; nothing is merged. |
|
| 93 | + | ||
| 94 | + | ### 5.4 Notifications |
|
| 95 | + | ||
| 96 | + | A `human` request and a permission prompt each raise a VS Code notification with the answer buttons (`spectra.notifications`: `all` | `asks` | `none`). A mention raises an information message with **Open**. Nothing else interrupts. |
|
| 97 | + | ||
| 98 | + | ### 5.5 Editor integration |
|
| 99 | + | ||
| 100 | + | - **Say to Agent from Selection**: posts the selected lines (fenced, with the path and line numbers) to a chosen agent or channel. |
|
| 101 | + | - **Delegate from Selection**: a task whose description quotes the selection. |
|
| 102 | + | - **Record Decision from Selection**. |
|
| 103 | + | - **Edit Brief** from the project's channel editor header. |
|
| 104 | + | ||
| 105 | + | ### 5.6 Commands (all prefixed `Spectra:`) |
|
| 106 | + | ||
| 107 | + | Set Up, Add Agent…, Register Folder as Project, Link Project…, Edit Overall Brief, Edit Project Brief, Renew Agent…, Open Radar, Open Constellation, Say…, Say to Agent from Selection, Reply…, New Task…, Delegate from Selection, Accept Task, Complete Task…, Cancel Task…, Reassign Task…, Answer Request…, Allow Action, Deny Action, Record Decision…, Record Decision from Selection, Search History…, Open Channel…, Open Thread…, Open Task…, Pause Agent…, Unpause Agent…, Register as Project, Show Rules, Show Rules Diff Since…, Export Rules to CLAUDE.md, Install Claude Code Hooks, Refresh. |
|
| 108 | + | ||
| 109 | + | ### 5.7 Settings |
|
| 110 | + | ||
| 111 | + | | Setting | Default | Meaning | |
|
| 112 | + | |---|---|---| |
|
| 113 | + | | `spectra.path` | `""` | The `spectra` binary (else `spectra` on `PATH`) | |
|
| 114 | + | | `spectra.notifications` | `asks` | Which inbox items raise notifications | |
|
| 115 | + | | `spectra.logTail` | `200` | Events kept in the Log view | |
|
| 116 | + | | `spectra.openRadarExternally` | `true` | Radar in the browser, else in a webview | |
|
| 117 | + | ||
| 118 | + | ## 6. Behaviour rules |
|
| 119 | + | ||
| 120 | + | - One stream per daemon; views refetch fragments, never whole lists, on an event. |
|
| 121 | + | - Errors from the API appear verbatim with **Show output**; the output channel logs every request, status and duration. |
|
| 122 | + | - Nothing destructive without confirmation: cancel, pause, deny, redact confirm with a "Don't ask again" per action. |
|
| 123 | + | - Multi-root workspaces: one project context per folder; the status bar follows the active editor's folder. |
|
| 124 | + | ||
| 125 | + | ## 7. Repository layout and tooling |
|
| 126 | + | ||
| 127 | + | ``` |
|
| 128 | + | spectra-vscode/ |
|
| 129 | + | package.json contributes: commands, views, menus, keybindings, settings |
|
| 130 | + | src/ |
|
| 131 | + | extension.ts activation, wiring |
|
| 132 | + | api.ts typed fetch over /api/v1 and the SSE stream (src/spectraapi.d.ts generated) |
|
| 133 | + | model/ Spectra, agents, channels, tasks, inbox |
|
| 134 | + | views/ inbox, agents, channels, tasks, decisions, rules, schedules, log |
|
| 135 | + | editor/ channel webview, task webview, spectra: content provider, CodeLens |
|
| 136 | + | commands/ |
|
| 137 | + | exported.ts the API other extensions consume (§8) |
|
| 138 | + | assets/images/ spectra.svg |
|
| 139 | + | test/ |
|
| 140 | + | docs/spec.md |
|
| 141 | + | ``` |
|
| 142 | + | ||
| 143 | + | Build `npm run build`, test `npm test`, package `vsce package`; CI runs lint, build and tests on Linux, macOS and Windows against a pinned `spectra` release. |
|
| 144 | + | ||
| 145 | + | ## 8. Exported API |
|
| 146 | + | ||
| 147 | + | `activate` returns a `SpectraAPI`: the connected Spectras as handles (url, brief, agents, channels, tasks, `fetch(path)`, `events` as an `Event`), `onDidChangeBrief`, `getProjectForFolder(uri)`, and `registerHubProvider(provider)`: a provider gives an id, `matches(url)` and `urlFor(target)` for agent, channel, thread and task targets, so **Open on Hub** works when `spectrahub-vscode` is installed. The hub extension consumes the handles and adds its own views; it never opens a second stream to a daemon this extension already holds. |
|
| 148 | + | ||
| 149 | + | ## 9. Milestones |
|
| 150 | + | ||
| 151 | + | 1. **0.1 Just works** (needs Spectra 0.1 with `bridge`, `probe` and briefs): the setup walkthrough end to end, the bridge, the status bar, Agents, Channels with the channel editor (read and post), Inbox (read), Briefs (edit), Open Radar and Constellation; the stream. |
|
| 152 | + | 2. **0.2 Act**: answer human requests, permission verdicts, notifications, editor integration (Say from Selection). |
|
| 153 | + | 3. **0.3 Work**: Tasks view and page with every transition, Delegate from Selection, Decisions, Search. |
|
| 154 | + | 4. **0.4 Automation**: Install Claude Code Hooks, Schedules, the Log view. |
|
| 155 | + | 5. **1.0**: exported API, three-platform tests, Marketplace listing. |
|
| 156 | + | ||
| 157 | + | ## 10. Open questions |
|
| 158 | + | ||
| 159 | + | 1. Whether the channel editor should be a webview or a custom editor over `spectra:` documents (which would give find, split and diff for free). Likely the latter for threads and a webview for the compose box. |
|
| 160 | + | 2. Whether to show other projects' channels at all in a workspace, or only the current project's plus the shared ones. Start with all, folded. |
|
| 161 | + | ||
| 162 | + | ## 11. Changelog |
|
| 163 | + | ||
| 164 | + | - Draft 0.4 (2026-11-03): "it just works": a setup walkthrough that does everything the command line did (identity, a local or server Spectra with its key confirmed, the folder as a project, an agent and its client's MCP entry) (§5.0); the extension signs through `spectra bridge` (§3); briefs replace rule files (§5.2); 0.1 is that whole path plus chat. |
|
| 165 | + | - Draft 0.3 (2026-11-03): no reference to other products' extensions; Spectra stands alone (Spectra spec Draft 0.5). |
|
| 166 | + | - Draft 0.2 (2026-11-02): the Spectra version each milestone needs, instead of 0.5 for all; 0.1 can start once Spectra 0.1's API exists. |
|
| 167 | + | - Draft 0.1 (2026-11-02): initial specification. |
added media/agent.md +5 −0
| 1 | + | # Agents |
|
| 2 | + | ||
| 3 | + | An agent is an AI client (Claude Code, Antigravity, Kimi Code, …) that acts for you in Spectra. You choose how long it may: an overnight agent with **8 hours** stops by itself in the morning. |
|
| 4 | + | ||
| 5 | + | The extension can connect the client for you: it adds Spectra to Claude Code or Kimi Code in this folder, or to Antigravity's settings. |
added media/binary.md +5 −0
| 1 | + | # The spectra program |
|
| 2 | + | ||
| 3 | + | Spectra is one program, `spectra`. The extension finds it on your `PATH`, in `~/.local/bin`, or where the **Spectra: Path** setting says. |
|
| 4 | + | ||
| 5 | + | In the spectra repository, `make install` builds it and puts it in `~/.local/bin`. |
added media/hello.md +5 −0
| 1 | + | # Say hello |
|
| 2 | + | ||
| 3 | + | The project's channel is where you and your agents talk. Mention an agent with `@name` and it sees it in its inbox. |
|
| 4 | + | ||
| 5 | + | Give your agents a **brief** too: the overall brief reaches every agent, and a project's brief reaches its agents. Edit them in the **Briefs** view; saving signs them into Spectra, and each agent is told on its next call. |
added media/identity.md +7 −0
| 1 | + | # Your identity |
|
| 2 | + | ||
| 3 | + | Your identity is a key pair that signs as you: an Ed25519 and an ML-DSA-65 key, kept in `~/.config/spectra`. |
|
| 4 | + | ||
| 5 | + | Every agent you add gets a key of its own, and your identity *delegates* to it, for as long as you say. That is how Spectra knows, and shows, that `claude-book` is **your** agent. |
|
| 6 | + | ||
| 7 | + | Keep the `keys` folder private: anyone with it can act as you. |
added media/project.md +5 −0
| 1 | + | # Projects |
|
| 2 | + | ||
| 3 | + | A project is any work your agents share: a codebase, a manuscript, a set of documents. Each has a channel, `#name`, and can have a brief. |
|
| 4 | + | ||
| 5 | + | Spectra records where the folder is and never reads anything inside it. |
added media/spectra.md +11 −0
| 1 | + | # Where Spectra runs |
|
| 2 | + | ||
| 3 | + | **On this machine**: the simplest. Spectra keeps its ledger in `~/.local/share/spectra`, and your agents on this machine connect to it. |
|
| 4 | + | ||
| 5 | + | **On a server**: Spectra runs elsewhere, and you, your keys and your agents stay here. On the server, set it up for your key: |
|
| 6 | + | ||
| 7 | + | ``` |
|
| 8 | + | spectra init --owner YOUR-KEY --name earth |
|
| 9 | + | ``` |
|
| 10 | + | ||
| 11 | + | It prints the server's *instance key*. When you connect, the extension shows the key it sees; trust it only if they match. |
added package-lock.json +2372 −0
| 1 | + | { |
|
| 2 | + | "name": "spectra-vscode", |
|
| 3 | + | "version": "0.1.0", |
|
| 4 | + | "lockfileVersion": 3, |
|
| 5 | + | "requires": true, |
|
| 6 | + | "packages": { |
|
| 7 | + | "": { |
|
| 8 | + | "name": "spectra-vscode", |
|
| 9 | + | "version": "0.1.0", |
|
| 10 | + | "license": "MIT OR Apache-2.0", |
|
| 11 | + | "devDependencies": { |
|
| 12 | + | "@types/node": "^24.0.0", |
|
| 13 | + | "@types/vscode": "^1.100.0", |
|
| 14 | + | "@vscode/vsce": "^4.0.0", |
|
| 15 | + | "esbuild": "^0.28.0", |
|
| 16 | + | "typescript": "^5.9.0" |
|
| 17 | + | }, |
|
| 18 | + | "engines": { |
|
| 19 | + | "vscode": "^1.100.0" |
|
| 20 | + | } |
|
| 21 | + | }, |
|
| 22 | + | "node_modules/@azure/abort-controller": { |
|
| 23 | + | "version": "2.2.0", |
|
| 24 | + | "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.2.0.tgz", |
|
| 25 | + | "integrity": "sha512-fNAjWnA/nZ2jz31kxR/AqRaUT8ewHBw/WuBIosK0moMy1C9e5ValbDfFdIxJzVOOYaYkV/b2F1S4H/aHiqfVQg==", |
|
| 26 | + | "dev": true, |
|
| 27 | + | "license": "MIT", |
|
| 28 | + | "dependencies": { |
|
| 29 | + | "tslib": "^2.6.2" |
|
| 30 | + | }, |
|
| 31 | + | "engines": { |
|
| 32 | + | "node": ">=22.0.0" |
|
| 33 | + | } |
|
| 34 | + | }, |
|
| 35 | + | "node_modules/@azure/core-auth": { |
|
| 36 | + | "version": "1.11.0", |
|
| 37 | + | "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.11.0.tgz", |
|
| 38 | + | "integrity": "sha512-IUZydyTUkDnYdstOW9pFOOUQlBjAepK5teihDE3x6yxsPJs/hsAaaYpeGxdxrgtOiJbBKSjKW7MDk7AEhb4LRg==", |
|
| 39 | + | "dev": true, |
|
| 40 | + | "license": "MIT", |
|
| 41 | + | "dependencies": { |
|
| 42 | + | "@azure/abort-controller": "^2.1.2", |
|
| 43 | + | "@azure/core-util": "^1.13.0", |
|
| 44 | + | "tslib": "^2.6.2" |
|
| 45 | + | }, |
|
| 46 | + | "engines": { |
|
| 47 | + | "node": ">=22.0.0" |
|
| 48 | + | } |
|
| 49 | + | }, |
|
| 50 | + | "node_modules/@azure/core-client": { |
|
| 51 | + | "version": "1.11.1", |
|
| 52 | + | "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.11.1.tgz", |
|
| 53 | + | "integrity": "sha512-2QygG2F76ZpMP2eMztiJvAiFMu71M9rDeU7vO/QKg5Css7MgM4frUOslFjhVjRhbGaCNPtz/S8M6y46/fFKVuQ==", |
|
| 54 | + | "dev": true, |
|
| 55 | + | "license": "MIT", |
|
| 56 | + | "dependencies": { |
|
| 57 | + | "@azure/abort-controller": "^2.1.2", |
|
| 58 | + | "@azure/core-auth": "^1.10.0", |
|
| 59 | + | "@azure/core-rest-pipeline": "^1.22.0", |
|
| 60 | + | "@azure/core-tracing": "^1.3.0", |
|
| 61 | + | "@azure/core-util": "^1.13.0", |
|
| 62 | + | "@azure/logger": "^1.3.0", |
|
| 63 | + | "tslib": "^2.6.2" |
|
| 64 | + | }, |
|
| 65 | + | "engines": { |
|
| 66 | + | "node": ">=22.0.0" |
|
| 67 | + | } |
|
| 68 | + | }, |
|
| 69 | + | "node_modules/@azure/core-process": { |
|
| 70 | + | "version": "1.0.0", |
|
| 71 | + | "resolved": "https://registry.npmjs.org/@azure/core-process/-/core-process-1.0.0.tgz", |
|
| 72 | + | "integrity": "sha512-/shnJ+ooO8WPxDhPEeI/2oRQuubn16gZ6CvlbpWbEswZfzwI9tI/sMAHmF3x1LuQ9yZYXfLW3TjzGMLEC5blKg==", |
|
| 73 | + | "dev": true, |
|
| 74 | + | "license": "MIT", |
|
| 75 | + | "engines": { |
|
| 76 | + | "node": ">=22.0.0" |
|
| 77 | + | } |
|
| 78 | + | }, |
|
| 79 | + | "node_modules/@azure/core-rest-pipeline": { |
|
| 80 | + | "version": "1.25.0", |
|
| 81 | + | "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.25.0.tgz", |
|
| 82 | + | "integrity": "sha512-bMs8ekJLjX8wPV+9IPBges1SLPyuDtE9g5gLDWOpxzKcoOFQnpLGkbcT1tdw3FaAmDS1gnPmMmJ6y/T5B96kIA==", |
|
| 83 | + | "dev": true, |
|
| 84 | + | "license": "MIT", |
|
| 85 | + | "dependencies": { |
|
| 86 | + | "@azure/abort-controller": "^2.1.2", |
|
| 87 | + | "@azure/core-auth": "^1.10.0", |
|
| 88 | + | "@azure/core-tracing": "^1.3.0", |
|
| 89 | + | "@azure/core-util": "^1.13.0", |
|
| 90 | + | "@azure/logger": "^1.3.0", |
|
| 91 | + | "@typespec/ts-http-runtime": "^0.3.4", |
|
| 92 | + | "tslib": "^2.6.2" |
|
| 93 | + | }, |
|
| 94 | + | "engines": { |
|
| 95 | + | "node": ">=22.0.0" |
|
| 96 | + | } |
|
| 97 | + | }, |
|
| 98 | + | "node_modules/@azure/core-tracing": { |
|
| 99 | + | "version": "1.4.0", |
|
| 100 | + | "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.4.0.tgz", |
|
| 101 | + | "integrity": "sha512-eGwxD0AtncrxeBM4tG8R55Pc3rdX1hNW2WibJAgYpCVA6E93mvvVH+LcssoVjOBrSKWS55yEIHsk0X8ctHmfOQ==", |
|
| 102 | + | "dev": true, |
|
| 103 | + | "license": "MIT", |
|
| 104 | + | "dependencies": { |
|
| 105 | + | "tslib": "^2.6.2" |
|
| 106 | + | }, |
|
| 107 | + | "engines": { |
|
| 108 | + | "node": ">=22.0.0" |
|
| 109 | + | } |
|
| 110 | + | }, |
|
| 111 | + | "node_modules/@azure/core-util": { |
|
| 112 | + | "version": "1.14.0", |
|
| 113 | + | "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.14.0.tgz", |
|
| 114 | + | "integrity": "sha512-9n2pWK61veAuN0V20t9lOuoV4CFMdyAZ1ygZzvBGk/pBBJRib/PjL9PLXa/aI2CcPpyHfqVsxxqLCYl6uZlfDw==", |
|
| 115 | + | "dev": true, |
|
| 116 | + | "license": "MIT", |
|
| 117 | + | "dependencies": { |
|
| 118 | + | "@azure/abort-controller": "^2.1.2", |
|
| 119 | + | "@typespec/ts-http-runtime": "^0.3.0", |
|
| 120 | + | "tslib": "^2.6.2" |
|
| 121 | + | }, |
|
| 122 | + | "engines": { |
|
| 123 | + | "node": ">=22.0.0" |
|
| 124 | + | } |
|
| 125 | + | }, |
|
| 126 | + | "node_modules/@azure/identity": { |
|
| 127 | + | "version": "4.13.3", |
|
| 128 | + | "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.13.3.tgz", |
|
| 129 | + | "integrity": "sha512-zGQPtqvXPgSA8yfV2CkIQ1qirqk0p9AIVpC5uEkdXQYcKl07QHvyaGYRnZOk0AsQUmxNb4wfkcwY5di8Z5xa9A==", |
|
| 130 | + | "dev": true, |
|
| 131 | + | "license": "MIT", |
|
| 132 | + | "dependencies": { |
|
| 133 | + | "@azure/abort-controller": "^2.0.0", |
|
| 134 | + | "@azure/core-auth": "^1.9.0", |
|
| 135 | + | "@azure/core-client": "^1.9.2", |
|
| 136 | + | "@azure/core-process": "^1.0.0", |
|
| 137 | + | "@azure/core-rest-pipeline": "^1.17.0", |
|
| 138 | + | "@azure/core-tracing": "^1.0.0", |
|
| 139 | + | "@azure/core-util": "^1.11.0", |
|
| 140 | + | "@azure/logger": "^1.0.0", |
|
| 141 | + | "@azure/msal-browser": "^5.5.0", |
|
| 142 | + | "@azure/msal-node": "^6.0.0", |
|
| 143 | + | "open": "^10.1.0", |
|
| 144 | + | "tslib": "^2.2.0" |
|
| 145 | + | }, |
|
| 146 | + | "engines": { |
|
| 147 | + | "node": ">=22.0.0" |
|
| 148 | + | } |
|
| 149 | + | }, |
|
| 150 | + | "node_modules/@azure/logger": { |
|
| 151 | + | "version": "1.4.0", |
|
| 152 | + | "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.4.0.tgz", |
|
| 153 | + | "integrity": "sha512-rbAE25KUfjU/s3XHUdJgceoCP5dEOpMx85J04kF+QMdta73XkuG9JGHHinch+XIoKpBdqljin+KqURpJriSzLA==", |
|
| 154 | + | "dev": true, |
|
| 155 | + | "license": "MIT", |
|
| 156 | + | "dependencies": { |
|
| 157 | + | "@typespec/ts-http-runtime": "^0.3.0", |
|
| 158 | + | "tslib": "^2.6.2" |
|
| 159 | + | }, |
|
| 160 | + | "engines": { |
|
| 161 | + | "node": ">=22.0.0" |
|
| 162 | + | } |
|
| 163 | + | }, |
|
| 164 | + | "node_modules/@azure/msal-browser": { |
|
| 165 | + | "version": "5.23.0", |
|
| 166 | + | "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-5.23.0.tgz", |
|
| 167 | + | "integrity": "sha512-IRCwkRCK47hBXK+7bu67UZWY7HS0Jx1dZgi4C1HVbjlBum8Jy8fb2l87xFr6HxdQJWy951zRUHtwwDITHFAtaA==", |
|
| 168 | + | "dev": true, |
|
| 169 | + | "license": "MIT", |
|
| 170 | + | "dependencies": { |
|
| 171 | + | "@azure/msal-common": "16.14.1" |
|
| 172 | + | }, |
|
| 173 | + | "engines": { |
|
| 174 | + | "node": ">=0.8.0" |
|
| 175 | + | } |
|
| 176 | + | }, |
|
| 177 | + | "node_modules/@azure/msal-common": { |
|
| 178 | + | "version": "16.14.1", |
|
| 179 | + | "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-16.14.1.tgz", |
|
| 180 | + | "integrity": "sha512-Or6xhPNyi4zHW25158yxBoyxuCqNSPa5YBVqfF1J5Ks4MJWBo/USXdp05DQIPu1Zli00YZu6t0+h6KvHJealxQ==", |
|
| 181 | + | "dev": true, |
|
| 182 | + | "license": "MIT", |
|
| 183 | + | "engines": { |
|
| 184 | + | "node": ">=0.8.0" |
|
| 185 | + | } |
|
| 186 | + | }, |
|
| 187 | + | "node_modules/@azure/msal-node": { |
|
| 188 | + | "version": "6.0.1", |
|
| 189 | + | "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-6.0.1.tgz", |
|
| 190 | + | "integrity": "sha512-ixSO1Y/kCVRthRs+hSx/5qkwaunX1/RAePhlMN0wIpIQ4WEZ6AREGGnGd1AP0qspHVsAwzWQKGubpjh50JyJIQ==", |
|
| 191 | + | "dev": true, |
|
| 192 | + | "license": "MIT", |
|
| 193 | + | "dependencies": { |
|
| 194 | + | "@azure/msal-common": "16.14.1", |
|
| 195 | + | "jsonwebtoken": "^9.0.0" |
|
| 196 | + | }, |
|
| 197 | + | "engines": { |
|
| 198 | + | "node": ">=20" |
|
| 199 | + | } |
|
| 200 | + | }, |
|
| 201 | + | "node_modules/@esbuild/aix-ppc64": { |
|
| 202 | + | "version": "0.28.2", |
|
| 203 | + | "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", |
|
| 204 | + | "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", |
|
| 205 | + | "cpu": [ |
|
| 206 | + | "ppc64" |
|
| 207 | + | ], |
|
| 208 | + | "dev": true, |
|
| 209 | + | "license": "MIT", |
|
| 210 | + | "optional": true, |
|
| 211 | + | "os": [ |
|
| 212 | + | "aix" |
|
| 213 | + | ], |
|
| 214 | + | "engines": { |
|
| 215 | + | "node": ">=18" |
|
| 216 | + | } |
|
| 217 | + | }, |
|
| 218 | + | "node_modules/@esbuild/android-arm": { |
|
| 219 | + | "version": "0.28.2", |
|
| 220 | + | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", |
|
| 221 | + | "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", |
|
| 222 | + | "cpu": [ |
|
| 223 | + | "arm" |
|
| 224 | + | ], |
|
| 225 | + | "dev": true, |
|
| 226 | + | "license": "MIT", |
|
| 227 | + | "optional": true, |
|
| 228 | + | "os": [ |
|
| 229 | + | "android" |
|
| 230 | + | ], |
|
| 231 | + | "engines": { |
|
| 232 | + | "node": ">=18" |
|
| 233 | + | } |
|
| 234 | + | }, |
|
| 235 | + | "node_modules/@esbuild/android-arm64": { |
|
| 236 | + | "version": "0.28.2", |
|
| 237 | + | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", |
|
| 238 | + | "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", |
|
| 239 | + | "cpu": [ |
|
| 240 | + | "arm64" |
|
| 241 | + | ], |
|
| 242 | + | "dev": true, |
|
| 243 | + | "license": "MIT", |
|
| 244 | + | "optional": true, |
|
| 245 | + | "os": [ |
|
| 246 | + | "android" |
|
| 247 | + | ], |
|
| 248 | + | "engines": { |
|
| 249 | + | "node": ">=18" |
|
| 250 | + | } |
|
| 251 | + | }, |
|
| 252 | + | "node_modules/@esbuild/android-x64": { |
|
| 253 | + | "version": "0.28.2", |
|
| 254 | + | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", |
|
| 255 | + | "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", |
|
| 256 | + | "cpu": [ |
|
| 257 | + | "x64" |
|
| 258 | + | ], |
|
| 259 | + | "dev": true, |
|
| 260 | + | "license": "MIT", |
|
| 261 | + | "optional": true, |
|
| 262 | + | "os": [ |
|
| 263 | + | "android" |
|
| 264 | + | ], |
|
| 265 | + | "engines": { |
|
| 266 | + | "node": ">=18" |
|
| 267 | + | } |
|
| 268 | + | }, |
|
| 269 | + | "node_modules/@esbuild/darwin-arm64": { |
|
| 270 | + | "version": "0.28.2", |
|
| 271 | + | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", |
|
| 272 | + | "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", |
|
| 273 | + | "cpu": [ |
|
| 274 | + | "arm64" |
|
| 275 | + | ], |
|
| 276 | + | "dev": true, |
|
| 277 | + | "license": "MIT", |
|
| 278 | + | "optional": true, |
|
| 279 | + | "os": [ |
|
| 280 | + | "darwin" |
|
| 281 | + | ], |
|
| 282 | + | "engines": { |
|
| 283 | + | "node": ">=18" |
|
| 284 | + | } |
|
| 285 | + | }, |
|
| 286 | + | "node_modules/@esbuild/darwin-x64": { |
|
| 287 | + | "version": "0.28.2", |
|
| 288 | + | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", |
|
| 289 | + | "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", |
|
| 290 | + | "cpu": [ |
|
| 291 | + | "x64" |
|
| 292 | + | ], |
|
| 293 | + | "dev": true, |
|
| 294 | + | "license": "MIT", |
|
| 295 | + | "optional": true, |
|
| 296 | + | "os": [ |
|
| 297 | + | "darwin" |
|
| 298 | + | ], |
|
| 299 | + | "engines": { |
|
| 300 | + | "node": ">=18" |
|
| 301 | + | } |
|
| 302 | + | }, |
|
| 303 | + | "node_modules/@esbuild/freebsd-arm64": { |
|
| 304 | + | "version": "0.28.2", |
|
| 305 | + | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", |
|
| 306 | + | "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", |
|
| 307 | + | "cpu": [ |
|
| 308 | + | "arm64" |
|
| 309 | + | ], |
|
| 310 | + | "dev": true, |
|
| 311 | + | "license": "MIT", |
|
| 312 | + | "optional": true, |
|
| 313 | + | "os": [ |
|
| 314 | + | "freebsd" |
|
| 315 | + | ], |
|
| 316 | + | "engines": { |
|
| 317 | + | "node": ">=18" |
|
| 318 | + | } |
|
| 319 | + | }, |
|
| 320 | + | "node_modules/@esbuild/freebsd-x64": { |
|
| 321 | + | "version": "0.28.2", |
|
| 322 | + | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", |
|
| 323 | + | "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", |
|
| 324 | + | "cpu": [ |
|
| 325 | + | "x64" |
|
| 326 | + | ], |
|
| 327 | + | "dev": true, |
|
| 328 | + | "license": "MIT", |
|
| 329 | + | "optional": true, |
|
| 330 | + | "os": [ |
|
| 331 | + | "freebsd" |
|
| 332 | + | ], |
|
| 333 | + | "engines": { |
|
| 334 | + | "node": ">=18" |
|
| 335 | + | } |
|
| 336 | + | }, |
|
| 337 | + | "node_modules/@esbuild/linux-arm": { |
|
| 338 | + | "version": "0.28.2", |
|
| 339 | + | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", |
|
| 340 | + | "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", |
|
| 341 | + | "cpu": [ |
|
| 342 | + | "arm" |
|
| 343 | + | ], |
|
| 344 | + | "dev": true, |
|
| 345 | + | "license": "MIT", |
|
| 346 | + | "optional": true, |
|
| 347 | + | "os": [ |
|
| 348 | + | "linux" |
|
| 349 | + | ], |
|
| 350 | + | "engines": { |
|
| 351 | + | "node": ">=18" |
|
| 352 | + | } |
|
| 353 | + | }, |
|
| 354 | + | "node_modules/@esbuild/linux-arm64": { |
|
| 355 | + | "version": "0.28.2", |
|
| 356 | + | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", |
|
| 357 | + | "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", |
|
| 358 | + | "cpu": [ |
|
| 359 | + | "arm64" |
|
| 360 | + | ], |
|
| 361 | + | "dev": true, |
|
| 362 | + | "license": "MIT", |
|
| 363 | + | "optional": true, |
|
| 364 | + | "os": [ |
|
| 365 | + | "linux" |
|
| 366 | + | ], |
|
| 367 | + | "engines": { |
|
| 368 | + | "node": ">=18" |
|
| 369 | + | } |
|
| 370 | + | }, |
|
| 371 | + | "node_modules/@esbuild/linux-ia32": { |
|
| 372 | + | "version": "0.28.2", |
|
| 373 | + | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", |
|
| 374 | + | "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", |
|
| 375 | + | "cpu": [ |
|
| 376 | + | "ia32" |
|
| 377 | + | ], |
|
| 378 | + | "dev": true, |
|
| 379 | + | "license": "MIT", |
|
| 380 | + | "optional": true, |
|
| 381 | + | "os": [ |
|
| 382 | + | "linux" |
|
| 383 | + | ], |
|
| 384 | + | "engines": { |
|
| 385 | + | "node": ">=18" |
|
| 386 | + | } |
|
| 387 | + | }, |
|
| 388 | + | "node_modules/@esbuild/linux-loong64": { |
|
| 389 | + | "version": "0.28.2", |
|
| 390 | + | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", |
|
| 391 | + | "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", |
|
| 392 | + | "cpu": [ |
|
| 393 | + | "loong64" |
|
| 394 | + | ], |
|
| 395 | + | "dev": true, |
|
| 396 | + | "license": "MIT", |
|
| 397 | + | "optional": true, |
|
| 398 | + | "os": [ |
|
| 399 | + | "linux" |
|
| 400 | + | ], |
|
| 401 | + | "engines": { |
|
| 402 | + | "node": ">=18" |
|
| 403 | + | } |
|
| 404 | + | }, |
|
| 405 | + | "node_modules/@esbuild/linux-mips64el": { |
|
| 406 | + | "version": "0.28.2", |
|
| 407 | + | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", |
|
| 408 | + | "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", |
|
| 409 | + | "cpu": [ |
|
| 410 | + | "mips64el" |
|
| 411 | + | ], |
|
| 412 | + | "dev": true, |
|
| 413 | + | "license": "MIT", |
|
| 414 | + | "optional": true, |
|
| 415 | + | "os": [ |
|
| 416 | + | "linux" |
|
| 417 | + | ], |
|
| 418 | + | "engines": { |
|
| 419 | + | "node": ">=18" |
|
| 420 | + | } |
|
| 421 | + | }, |
|
| 422 | + | "node_modules/@esbuild/linux-ppc64": { |
|
| 423 | + | "version": "0.28.2", |
|
| 424 | + | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", |
|
| 425 | + | "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", |
|
| 426 | + | "cpu": [ |
|
| 427 | + | "ppc64" |
|
| 428 | + | ], |
|
| 429 | + | "dev": true, |
|
| 430 | + | "license": "MIT", |
|
| 431 | + | "optional": true, |
|
| 432 | + | "os": [ |
|
| 433 | + | "linux" |
|
| 434 | + | ], |
|
| 435 | + | "engines": { |
|
| 436 | + | "node": ">=18" |
|
| 437 | + | } |
|
| 438 | + | }, |
|
| 439 | + | "node_modules/@esbuild/linux-riscv64": { |
|
| 440 | + | "version": "0.28.2", |
|
| 441 | + | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", |
|
| 442 | + | "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", |
|
| 443 | + | "cpu": [ |
|
| 444 | + | "riscv64" |
|
| 445 | + | ], |
|
| 446 | + | "dev": true, |
|
| 447 | + | "license": "MIT", |
|
| 448 | + | "optional": true, |
|
| 449 | + | "os": [ |
|
| 450 | + | "linux" |
|
| 451 | + | ], |
|
| 452 | + | "engines": { |
|
| 453 | + | "node": ">=18" |
|
| 454 | + | } |
|
| 455 | + | }, |
|
| 456 | + | "node_modules/@esbuild/linux-s390x": { |
|
| 457 | + | "version": "0.28.2", |
|
| 458 | + | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", |
|
| 459 | + | "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", |
|
| 460 | + | "cpu": [ |
|
| 461 | + | "s390x" |
|
| 462 | + | ], |
|
| 463 | + | "dev": true, |
|
| 464 | + | "license": "MIT", |
|
| 465 | + | "optional": true, |
|
| 466 | + | "os": [ |
|
| 467 | + | "linux" |
|
| 468 | + | ], |
|
| 469 | + | "engines": { |
|
| 470 | + | "node": ">=18" |
|
| 471 | + | } |
|
| 472 | + | }, |
|
| 473 | + | "node_modules/@esbuild/linux-x64": { |
|
| 474 | + | "version": "0.28.2", |
|
| 475 | + | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", |
|
| 476 | + | "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", |
|
| 477 | + | "cpu": [ |
|
| 478 | + | "x64" |
|
| 479 | + | ], |
|
| 480 | + | "dev": true, |
|
| 481 | + | "license": "MIT", |
|
| 482 | + | "optional": true, |
|
| 483 | + | "os": [ |
|
| 484 | + | "linux" |
|
| 485 | + | ], |
|
| 486 | + | "engines": { |
|
| 487 | + | "node": ">=18" |
|
| 488 | + | } |
|
| 489 | + | }, |
|
| 490 | + | "node_modules/@esbuild/netbsd-arm64": { |
|
| 491 | + | "version": "0.28.2", |
|
| 492 | + | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", |
|
| 493 | + | "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", |
|
| 494 | + | "cpu": [ |
|
| 495 | + | "arm64" |
|
| 496 | + | ], |
|
| 497 | + | "dev": true, |
|
| 498 | + | "license": "MIT", |
|
| 499 | + | "optional": true, |
|
| 500 | + | "os": [ |
|
| 501 | + | "netbsd" |
|
| 502 | + | ], |
|
| 503 | + | "engines": { |
|
| 504 | + | "node": ">=18" |
|
| 505 | + | } |
|
| 506 | + | }, |
|
| 507 | + | "node_modules/@esbuild/netbsd-x64": { |
|
| 508 | + | "version": "0.28.2", |
|
| 509 | + | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", |
|
| 510 | + | "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", |
|
| 511 | + | "cpu": [ |
|
| 512 | + | "x64" |
|
| 513 | + | ], |
|
| 514 | + | "dev": true, |
|
| 515 | + | "license": "MIT", |
|
| 516 | + | "optional": true, |
|
| 517 | + | "os": [ |
|
| 518 | + | "netbsd" |
|
| 519 | + | ], |
|
| 520 | + | "engines": { |
|
| 521 | + | "node": ">=18" |
|
| 522 | + | } |
|
| 523 | + | }, |
|
| 524 | + | "node_modules/@esbuild/openbsd-arm64": { |
|
| 525 | + | "version": "0.28.2", |
|
| 526 | + | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", |
|
| 527 | + | "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", |
|
| 528 | + | "cpu": [ |
|
| 529 | + | "arm64" |
|
| 530 | + | ], |
|
| 531 | + | "dev": true, |
|
| 532 | + | "license": "MIT", |
|
| 533 | + | "optional": true, |
|
| 534 | + | "os": [ |
|
| 535 | + | "openbsd" |
|
| 536 | + | ], |
|
| 537 | + | "engines": { |
|
| 538 | + | "node": ">=18" |
|
| 539 | + | } |
|
| 540 | + | }, |
|
| 541 | + | "node_modules/@esbuild/openbsd-x64": { |
|
| 542 | + | "version": "0.28.2", |
|
| 543 | + | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", |
|
| 544 | + | "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", |
|
| 545 | + | "cpu": [ |
|
| 546 | + | "x64" |
|
| 547 | + | ], |
|
| 548 | + | "dev": true, |
|
| 549 | + | "license": "MIT", |
|
| 550 | + | "optional": true, |
|
| 551 | + | "os": [ |
|
| 552 | + | "openbsd" |
|
| 553 | + | ], |
|
| 554 | + | "engines": { |
|
| 555 | + | "node": ">=18" |
|
| 556 | + | } |
|
| 557 | + | }, |
|
| 558 | + | "node_modules/@esbuild/openharmony-arm64": { |
|
| 559 | + | "version": "0.28.2", |
|
| 560 | + | "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", |
|
| 561 | + | "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", |
|
| 562 | + | "cpu": [ |
|
| 563 | + | "arm64" |
|
| 564 | + | ], |
|
| 565 | + | "dev": true, |
|
| 566 | + | "license": "MIT", |
|
| 567 | + | "optional": true, |
|
| 568 | + | "os": [ |
|
| 569 | + | "openharmony" |
|
| 570 | + | ], |
|
| 571 | + | "engines": { |
|
| 572 | + | "node": ">=18" |
|
| 573 | + | } |
|
| 574 | + | }, |
|
| 575 | + | "node_modules/@esbuild/sunos-x64": { |
|
| 576 | + | "version": "0.28.2", |
|
| 577 | + | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", |
|
| 578 | + | "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", |
|
| 579 | + | "cpu": [ |
|
| 580 | + | "x64" |
|
| 581 | + | ], |
|
| 582 | + | "dev": true, |
|
| 583 | + | "license": "MIT", |
|
| 584 | + | "optional": true, |
|
| 585 | + | "os": [ |
|
| 586 | + | "sunos" |
|
| 587 | + | ], |
|
| 588 | + | "engines": { |
|
| 589 | + | "node": ">=18" |
|
| 590 | + | } |
|
| 591 | + | }, |
|
| 592 | + | "node_modules/@esbuild/win32-arm64": { |
|
| 593 | + | "version": "0.28.2", |
|
| 594 | + | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", |
|
| 595 | + | "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", |
|
| 596 | + | "cpu": [ |
|
| 597 | + | "arm64" |
|
| 598 | + | ], |
|
| 599 | + | "dev": true, |
|
| 600 | + | "license": "MIT", |
|
| 601 | + | "optional": true, |
|
| 602 | + | "os": [ |
|
| 603 | + | "win32" |
|
| 604 | + | ], |
|
| 605 | + | "engines": { |
|
| 606 | + | "node": ">=18" |
|
| 607 | + | } |
|
| 608 | + | }, |
|
| 609 | + | "node_modules/@esbuild/win32-ia32": { |
|
| 610 | + | "version": "0.28.2", |
|
| 611 | + | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", |
|
| 612 | + | "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", |
|
| 613 | + | "cpu": [ |
|
| 614 | + | "ia32" |
|
| 615 | + | ], |
|
| 616 | + | "dev": true, |
|
| 617 | + | "license": "MIT", |
|
| 618 | + | "optional": true, |
|
| 619 | + | "os": [ |
|
| 620 | + | "win32" |
|
| 621 | + | ], |
|
| 622 | + | "engines": { |
|
| 623 | + | "node": ">=18" |
|
| 624 | + | } |
|
| 625 | + | }, |
|
| 626 | + | "node_modules/@esbuild/win32-x64": { |
|
| 627 | + | "version": "0.28.2", |
|
| 628 | + | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", |
|
| 629 | + | "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", |
|
| 630 | + | "cpu": [ |
|
| 631 | + | "x64" |
|
| 632 | + | ], |
|
| 633 | + | "dev": true, |
|
| 634 | + | "license": "MIT", |
|
| 635 | + | "optional": true, |
|
| 636 | + | "os": [ |
|
| 637 | + | "win32" |
|
| 638 | + | ], |
|
| 639 | + | "engines": { |
|
| 640 | + | "node": ">=18" |
|
| 641 | + | } |
|
| 642 | + | }, |
|
| 643 | + | "node_modules/@napi-rs/keyring": { |
|
| 644 | + | "version": "1.3.0", |
|
| 645 | + | "resolved": "https://registry.npmjs.org/@napi-rs/keyring/-/keyring-1.3.0.tgz", |
|
| 646 | + | "integrity": "sha512-WrOw/bcXm0f9qHkumlT1QlArXSTWqaY9sunsDpOk+yCCorCKMxvWT/a3xko4EYHVdeZoh00yI2TydXn6eyICDA==", |
|
| 647 | + | "dev": true, |
|
| 648 | + | "license": "MIT", |
|
| 649 | + | "engines": { |
|
| 650 | + | "node": ">= 10" |
|
| 651 | + | }, |
|
| 652 | + | "funding": { |
|
| 653 | + | "type": "github", |
|
| 654 | + | "url": "https://github.com/sponsors/Brooooooklyn" |
|
| 655 | + | }, |
|
| 656 | + | "optionalDependencies": { |
|
| 657 | + | "@napi-rs/keyring-darwin-arm64": "1.3.0", |
|
| 658 | + | "@napi-rs/keyring-darwin-x64": "1.3.0", |
|
| 659 | + | "@napi-rs/keyring-freebsd-x64": "1.3.0", |
|
| 660 | + | "@napi-rs/keyring-linux-arm-gnueabihf": "1.3.0", |
|
| 661 | + | "@napi-rs/keyring-linux-arm64-gnu": "1.3.0", |
|
| 662 | + | "@napi-rs/keyring-linux-arm64-musl": "1.3.0", |
|
| 663 | + | "@napi-rs/keyring-linux-riscv64-gnu": "1.3.0", |
|
| 664 | + | "@napi-rs/keyring-linux-x64-gnu": "1.3.0", |
|
| 665 | + | "@napi-rs/keyring-linux-x64-musl": "1.3.0", |
|
| 666 | + | "@napi-rs/keyring-win32-arm64-msvc": "1.3.0", |
|
| 667 | + | "@napi-rs/keyring-win32-ia32-msvc": "1.3.0", |
|
| 668 | + | "@napi-rs/keyring-win32-x64-msvc": "1.3.0" |
|
| 669 | + | } |
|
| 670 | + | }, |
|
| 671 | + | "node_modules/@napi-rs/keyring-darwin-arm64": { |
|
| 672 | + | "version": "1.3.0", |
|
| 673 | + | "resolved": "https://registry.npmjs.org/@napi-rs/keyring-darwin-arm64/-/keyring-darwin-arm64-1.3.0.tgz", |
|
| 674 | + | "integrity": "sha512-pl76hJvdYUBn6I24bXiOBMA9nbDapo3I5B+f3OorjDU4dUMSypXeKbOVehJe8fhgTiH24flMyTS3aAIy43xegQ==", |
|
| 675 | + | "cpu": [ |
|
| 676 | + | "arm64" |
|
| 677 | + | ], |
|
| 678 | + | "dev": true, |
|
| 679 | + | "license": "MIT", |
|
| 680 | + | "optional": true, |
|
| 681 | + | "os": [ |
|
| 682 | + | "darwin" |
|
| 683 | + | ], |
|
| 684 | + | "engines": { |
|
| 685 | + | "node": ">= 10" |
|
| 686 | + | } |
|
| 687 | + | }, |
|
| 688 | + | "node_modules/@napi-rs/keyring-darwin-x64": { |
|
| 689 | + | "version": "1.3.0", |
|
| 690 | + | "resolved": "https://registry.npmjs.org/@napi-rs/keyring-darwin-x64/-/keyring-darwin-x64-1.3.0.tgz", |
|
| 691 | + | "integrity": "sha512-YcJtEV5LA3cvA4z3BurgxH5IhTsW1JfIvcAAcqcecwk06Si9F9NqkxbZVIfDwQ8oRHgaBmT3zZJnLAotCrVahw==", |
|
| 692 | + | "cpu": [ |
|
| 693 | + | "x64" |
|
| 694 | + | ], |
|
| 695 | + | "dev": true, |
|
| 696 | + | "license": "MIT", |
|
| 697 | + | "optional": true, |
|
| 698 | + | "os": [ |
|
| 699 | + | "darwin" |
|
| 700 | + | ], |
|
| 701 | + | "engines": { |
|
| 702 | + | "node": ">= 10" |
|
| 703 | + | } |
|
| 704 | + | }, |
|
| 705 | + | "node_modules/@napi-rs/keyring-freebsd-x64": { |
|
| 706 | + | "version": "1.3.0", |
|
| 707 | + | "resolved": "https://registry.npmjs.org/@napi-rs/keyring-freebsd-x64/-/keyring-freebsd-x64-1.3.0.tgz", |
|
| 708 | + | "integrity": "sha512-vlLf31TGhfRAaxLDBhg8b89ss0HHD/lyNmL5F3UjSaz5CUXElsJmKYq9fqA/B+cZKUEUcLHHGhF0I/CqcFdaVw==", |
|
| 709 | + | "cpu": [ |
|
| 710 | + | "x64" |
|
| 711 | + | ], |
|
| 712 | + | "dev": true, |
|
| 713 | + | "license": "MIT", |
|
| 714 | + | "optional": true, |
|
| 715 | + | "os": [ |
|
| 716 | + | "freebsd" |
|
| 717 | + | ], |
|
| 718 | + | "engines": { |
|
| 719 | + | "node": ">= 10" |
|
| 720 | + | } |
|
| 721 | + | }, |
|
| 722 | + | "node_modules/@napi-rs/keyring-linux-arm-gnueabihf": { |
|
| 723 | + | "version": "1.3.0", |
|
| 724 | + | "resolved": "https://registry.npmjs.org/@napi-rs/keyring-linux-arm-gnueabihf/-/keyring-linux-arm-gnueabihf-1.3.0.tgz", |
|
| 725 | + | "integrity": "sha512-KiWdMMu/Inz/bHHIAGrnF7r54FZDYXuHO6UFF/rhIrshUsxbMG1Rl9lEymNtqqsVo927G0VYcb02FzWQ3iBQRQ==", |
|
| 726 | + | "cpu": [ |
|
| 727 | + | "arm" |
|
| 728 | + | ], |
|
| 729 | + | "dev": true, |
|
| 730 | + | "license": "MIT", |
|
| 731 | + | "optional": true, |
|
| 732 | + | "os": [ |
|
| 733 | + | "linux" |
|
| 734 | + | ], |
|
| 735 | + | "engines": { |
|
| 736 | + | "node": ">= 10" |
|
| 737 | + | } |
|
| 738 | + | }, |
|
| 739 | + | "node_modules/@napi-rs/keyring-linux-arm64-gnu": { |
|
| 740 | + | "version": "1.3.0", |
|
| 741 | + | "resolved": "https://registry.npmjs.org/@napi-rs/keyring-linux-arm64-gnu/-/keyring-linux-arm64-gnu-1.3.0.tgz", |
|
| 742 | + | "integrity": "sha512-eyKGpY40lm9Jvs1aD294XRH4y7+TlJM0YVAryZeXA6TX0mb4gMkxVXwSQv7MCwgah7raeUd0dKUb4BPAYIgcMg==", |
|
| 743 | + | "cpu": [ |
|
| 744 | + | "arm64" |
|
| 745 | + | ], |
|
| 746 | + | "dev": true, |
|
| 747 | + | "libc": [ |
|
| 748 | + | "glibc" |
|
| 749 | + | ], |
|
| 750 | + | "license": "MIT", |
|
| 751 | + | "optional": true, |
|
| 752 | + | "os": [ |
|
| 753 | + | "linux" |
|
| 754 | + | ], |
|
| 755 | + | "engines": { |
|
| 756 | + | "node": ">= 10" |
|
| 757 | + | } |
|
| 758 | + | }, |
|
| 759 | + | "node_modules/@napi-rs/keyring-linux-arm64-musl": { |
|
| 760 | + | "version": "1.3.0", |
|
| 761 | + | "resolved": "https://registry.npmjs.org/@napi-rs/keyring-linux-arm64-musl/-/keyring-linux-arm64-musl-1.3.0.tgz", |
|
| 762 | + | "integrity": "sha512-iIK6JWHXAJqDrEyLY3TmswwloVyt2vj+04TZnew+uSJ9gnDO8EwRbp3/iw3LpWaXiDO7VomGO6y8I0Id8uBZSw==", |
|
| 763 | + | "cpu": [ |
|
| 764 | + | "arm64" |
|
| 765 | + | ], |
|
| 766 | + | "dev": true, |
|
| 767 | + | "libc": [ |
|
| 768 | + | "musl" |
|
| 769 | + | ], |
|
| 770 | + | "license": "MIT", |
|
| 771 | + | "optional": true, |
|
| 772 | + | "os": [ |
|
| 773 | + | "linux" |
|
| 774 | + | ], |
|
| 775 | + | "engines": { |
|
| 776 | + | "node": ">= 10" |
|
| 777 | + | } |
|
| 778 | + | }, |
|
| 779 | + | "node_modules/@napi-rs/keyring-linux-riscv64-gnu": { |
|
| 780 | + | "version": "1.3.0", |
|
| 781 | + | "resolved": "https://registry.npmjs.org/@napi-rs/keyring-linux-riscv64-gnu/-/keyring-linux-riscv64-gnu-1.3.0.tgz", |
|
| 782 | + | "integrity": "sha512-/PGqrwn6EwgtK6vccASSXJRfOSP4vN1F4ASsIQ+7MdrK6hNvAJ1FZPrIuD5gGGdxezo3F++To2Wq7DbuGIeuNQ==", |
|
| 783 | + | "cpu": [ |
|
| 784 | + | "riscv64" |
|
| 785 | + | ], |
|
| 786 | + | "dev": true, |
|
| 787 | + | "libc": [ |
|
| 788 | + | "glibc" |
|
| 789 | + | ], |
|
| 790 | + | "license": "MIT", |
|
| 791 | + | "optional": true, |
|
| 792 | + | "os": [ |
|
| 793 | + | "linux" |
|
| 794 | + | ], |
|
| 795 | + | "engines": { |
|
| 796 | + | "node": ">= 10" |
|
| 797 | + | } |
|
| 798 | + | }, |
|
| 799 | + | "node_modules/@napi-rs/keyring-linux-x64-gnu": { |
|
| 800 | + | "version": "1.3.0", |
|
| 801 | + | "resolved": "https://registry.npmjs.org/@napi-rs/keyring-linux-x64-gnu/-/keyring-linux-x64-gnu-1.3.0.tgz", |
|
| 802 | + | "integrity": "sha512-2PDK1WKWTu9lBGq9VvNEkSlQD3O7YwVpmnyN2M3cy4v7NJ/8gDMd9GXv3G+FVXN13uhp4gnnPBS+ScefmEeD2A==", |
|
| 803 | + | "cpu": [ |
|
| 804 | + | "x64" |
|
| 805 | + | ], |
|
| 806 | + | "dev": true, |
|
| 807 | + | "libc": [ |
|
| 808 | + | "glibc" |
|
| 809 | + | ], |
|
| 810 | + | "license": "MIT", |
|
| 811 | + | "optional": true, |
|
| 812 | + | "os": [ |
|
| 813 | + | "linux" |
|
| 814 | + | ], |
|
| 815 | + | "engines": { |
|
| 816 | + | "node": ">= 10" |
|
| 817 | + | } |
|
| 818 | + | }, |
|
| 819 | + | "node_modules/@napi-rs/keyring-linux-x64-musl": { |
|
| 820 | + | "version": "1.3.0", |
|
| 821 | + | "resolved": "https://registry.npmjs.org/@napi-rs/keyring-linux-x64-musl/-/keyring-linux-x64-musl-1.3.0.tgz", |
|
| 822 | + | "integrity": "sha512-oJ2HkX8YUo46QBkn0pG+HuIKQNqr523q6vBobCn+P95s4C4K6/kLBqHY/1bg5J4ap31DzsznhnFKcfBNBsjCnw==", |
|
| 823 | + | "cpu": [ |
|
| 824 | + | "x64" |
|
| 825 | + | ], |
|
| 826 | + | "dev": true, |
|
| 827 | + | "libc": [ |
|
| 828 | + | "musl" |
|
| 829 | + | ], |
|
| 830 | + | "license": "MIT", |
|
| 831 | + | "optional": true, |
|
| 832 | + | "os": [ |
|
| 833 | + | "linux" |
|
| 834 | + | ], |
|
| 835 | + | "engines": { |
|
| 836 | + | "node": ">= 10" |
|
| 837 | + | } |
|
| 838 | + | }, |
|
| 839 | + | "node_modules/@napi-rs/keyring-win32-arm64-msvc": { |
|
| 840 | + | "version": "1.3.0", |
|
| 841 | + | "resolved": "https://registry.npmjs.org/@napi-rs/keyring-win32-arm64-msvc/-/keyring-win32-arm64-msvc-1.3.0.tgz", |
|
| 842 | + | "integrity": "sha512-tOd3c/uAaeoE4ycVlmAdSvygz0Zt3zdca6Y7gokBeIbaRDWpjDIUOpU3MvML59XAaqyuKGsVVu0F/DZb1lHPmw==", |
|
| 843 | + | "cpu": [ |
|
| 844 | + | "arm64" |
|
| 845 | + | ], |
|
| 846 | + | "dev": true, |
|
| 847 | + | "license": "MIT", |
|
| 848 | + | "optional": true, |
|
| 849 | + | "os": [ |
|
| 850 | + | "win32" |
|
| 851 | + | ], |
|
| 852 | + | "engines": { |
|
| 853 | + | "node": ">= 10" |
|
| 854 | + | } |
|
| 855 | + | }, |
|
| 856 | + | "node_modules/@napi-rs/keyring-win32-ia32-msvc": { |
|
| 857 | + | "version": "1.3.0", |
|
| 858 | + | "resolved": "https://registry.npmjs.org/@napi-rs/keyring-win32-ia32-msvc/-/keyring-win32-ia32-msvc-1.3.0.tgz", |
|
| 859 | + | "integrity": "sha512-sPSqeAFZMGqP1R++M2JTza7GQJJ/TpCo6JU6Vcd4jnebvOaEDs9b7eipakU1PJdSvhpC2yXMCNRk9gXfrhuwHQ==", |
|
| 860 | + | "cpu": [ |
|
| 861 | + | "ia32" |
|
| 862 | + | ], |
|
| 863 | + | "dev": true, |
|
| 864 | + | "license": "MIT", |
|
| 865 | + | "optional": true, |
|
| 866 | + | "os": [ |
|
| 867 | + | "win32" |
|
| 868 | + | ], |
|
| 869 | + | "engines": { |
|
| 870 | + | "node": ">= 10" |
|
| 871 | + | } |
|
| 872 | + | }, |
|
| 873 | + | "node_modules/@napi-rs/keyring-win32-x64-msvc": { |
|
| 874 | + | "version": "1.3.0", |
|
| 875 | + | "resolved": "https://registry.npmjs.org/@napi-rs/keyring-win32-x64-msvc/-/keyring-win32-x64-msvc-1.3.0.tgz", |
|
| 876 | + | "integrity": "sha512-4DnCWXwDc0HRKwyRlG5y0VhKZW2tNRQfKKfyj6IX/KWfDNyq9hn4n+GL1auyDcOO/v8PwnhmYo2+rOOqCkvvOg==", |
|
| 877 | + | "cpu": [ |
|
| 878 | + | "x64" |
|
| 879 | + | ], |
|
| 880 | + | "dev": true, |
|
| 881 | + | "license": "MIT", |
|
| 882 | + | "optional": true, |
|
| 883 | + | "os": [ |
|
| 884 | + | "win32" |
|
| 885 | + | ], |
|
| 886 | + | "engines": { |
|
| 887 | + | "node": ">= 10" |
|
| 888 | + | } |
|
| 889 | + | }, |
|
| 890 | + | "node_modules/@secretlint/core": { |
|
| 891 | + | "version": "10.2.2", |
|
| 892 | + | "resolved": "https://registry.npmjs.org/@secretlint/core/-/core-10.2.2.tgz", |
|
| 893 | + | "integrity": "sha512-6rdwBwLP9+TO3rRjMVW1tX+lQeo5gBbxl1I5F8nh8bgGtKwdlCMhMKsBWzWg1ostxx/tIG7OjZI0/BxsP8bUgw==", |
|
| 894 | + | "dev": true, |
|
| 895 | + | "license": "MIT", |
|
| 896 | + | "dependencies": { |
|
| 897 | + | "@secretlint/profiler": "^10.2.2", |
|
| 898 | + | "@secretlint/types": "^10.2.2", |
|
| 899 | + | "debug": "^4.4.1", |
|
| 900 | + | "structured-source": "^4.0.0" |
|
| 901 | + | }, |
|
| 902 | + | "engines": { |
|
| 903 | + | "node": ">=20.0.0" |
|
| 904 | + | } |
|
| 905 | + | }, |
|
| 906 | + | "node_modules/@secretlint/profiler": { |
|
| 907 | + | "version": "10.2.2", |
|
| 908 | + | "resolved": "https://registry.npmjs.org/@secretlint/profiler/-/profiler-10.2.2.tgz", |
|
| 909 | + | "integrity": "sha512-qm9rWfkh/o8OvzMIfY8a5bCmgIniSpltbVlUVl983zDG1bUuQNd1/5lUEeWx5o/WJ99bXxS7yNI4/KIXfHexig==", |
|
| 910 | + | "dev": true, |
|
| 911 | + | "license": "MIT" |
|
| 912 | + | }, |
|
| 913 | + | "node_modules/@secretlint/secretlint-rule-no-dotenv": { |
|
| 914 | + | "version": "10.2.2", |
|
| 915 | + | "resolved": "https://registry.npmjs.org/@secretlint/secretlint-rule-no-dotenv/-/secretlint-rule-no-dotenv-10.2.2.tgz", |
|
| 916 | + | "integrity": "sha512-KJRbIShA9DVc5Va3yArtJ6QDzGjg3PRa1uYp9As4RsyKtKSSZjI64jVca57FZ8gbuk4em0/0Jq+uy6485wxIdg==", |
|
| 917 | + | "dev": true, |
|
| 918 | + | "license": "MIT", |
|
| 919 | + | "dependencies": { |
|
| 920 | + | "@secretlint/types": "^10.2.2" |
|
| 921 | + | }, |
|
| 922 | + | "engines": { |
|
| 923 | + | "node": ">=20.0.0" |
|
| 924 | + | } |
|
| 925 | + | }, |
|
| 926 | + | "node_modules/@secretlint/secretlint-rule-preset-recommend": { |
|
| 927 | + | "version": "10.2.2", |
|
| 928 | + | "resolved": "https://registry.npmjs.org/@secretlint/secretlint-rule-preset-recommend/-/secretlint-rule-preset-recommend-10.2.2.tgz", |
|
| 929 | + | "integrity": "sha512-K3jPqjva8bQndDKJqctnGfwuAxU2n9XNCPtbXVI5JvC7FnQiNg/yWlQPbMUlBXtBoBGFYp08A94m6fvtc9v+zA==", |
|
| 930 | + | "dev": true, |
|
| 931 | + | "license": "MIT", |
|
| 932 | + | "engines": { |
|
| 933 | + | "node": ">=20.0.0" |
|
| 934 | + | } |
|
| 935 | + | }, |
|
| 936 | + | "node_modules/@secretlint/source-creator": { |
|
| 937 | + | "version": "10.2.2", |
|
| 938 | + | "resolved": "https://registry.npmjs.org/@secretlint/source-creator/-/source-creator-10.2.2.tgz", |
|
| 939 | + | "integrity": "sha512-h6I87xJfwfUTgQ7irWq7UTdq/Bm1RuQ/fYhA3dtTIAop5BwSFmZyrchph4WcoEvbN460BWKmk4RYSvPElIIvxw==", |
|
| 940 | + | "dev": true, |
|
| 941 | + | "license": "MIT", |
|
| 942 | + | "dependencies": { |
|
| 943 | + | "@secretlint/types": "^10.2.2", |
|
| 944 | + | "istextorbinary": "^9.5.0" |
|
| 945 | + | }, |
|
| 946 | + | "engines": { |
|
| 947 | + | "node": ">=20.0.0" |
|
| 948 | + | } |
|
| 949 | + | }, |
|
| 950 | + | "node_modules/@secretlint/types": { |
|
| 951 | + | "version": "10.2.2", |
|
| 952 | + | "resolved": "https://registry.npmjs.org/@secretlint/types/-/types-10.2.2.tgz", |
|
| 953 | + | "integrity": "sha512-Nqc90v4lWCXyakD6xNyNACBJNJ0tNCwj2WNk/7ivyacYHxiITVgmLUFXTBOeCdy79iz6HtN9Y31uw/jbLrdOAg==", |
|
| 954 | + | "dev": true, |
|
| 955 | + | "license": "MIT", |
|
| 956 | + | "engines": { |
|
| 957 | + | "node": ">=20.0.0" |
|
| 958 | + | } |
|
| 959 | + | }, |
|
| 960 | + | "node_modules/@types/node": { |
|
| 961 | + | "version": "24.19.0", |
|
| 962 | + | "resolved": "https://registry.npmjs.org/@types/node/-/node-24.19.0.tgz", |
|
| 963 | + | "integrity": "sha512-zY+5tKxXdhGh1PYI0ac+7juvEu4OI6vWtVVoj5i2m42jxAY1U+zHGt6QCyOFwykdP62sM3MJ9stoYYUw5aCWew==", |
|
| 964 | + | "dev": true, |
|
| 965 | + | "license": "MIT", |
|
| 966 | + | "dependencies": { |
|
| 967 | + | "undici-types": ">=7.24.0 <7.24.7" |
|
| 968 | + | } |
|
| 969 | + | }, |
|
| 970 | + | "node_modules/@types/vscode": { |
|
| 971 | + | "version": "1.138.0", |
|
| 972 | + | "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.138.0.tgz", |
|
| 973 | + | "integrity": "sha512-DhlvrucJa8n6EHst7qQcXOcjLKs/VRTQy55plLO+RN74jonYsr/t9dd66zBnAyz1tpDig0okQAxuoPHpVNje2g==", |
|
| 974 | + | "dev": true, |
|
| 975 | + | "license": "MIT" |
|
| 976 | + | }, |
|
| 977 | + | "node_modules/@typespec/ts-http-runtime": { |
|
| 978 | + | "version": "0.3.9", |
|
| 979 | + | "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.9.tgz", |
|
| 980 | + | "integrity": "sha512-edSdeAqkdxBVzA1yL1LrLCml1YjyCVvPMtMqJpbF+6K609tHe8V6sQUzFQSGcYNhcuhOceZtjvN32+mpIth30A==", |
|
| 981 | + | "dev": true, |
|
| 982 | + | "license": "MIT", |
|
| 983 | + | "dependencies": { |
|
| 984 | + | "http-proxy-agent": "^7.0.0", |
|
| 985 | + | "https-proxy-agent": "^7.0.0", |
|
| 986 | + | "tslib": "^2.6.2" |
|
| 987 | + | }, |
|
| 988 | + | "engines": { |
|
| 989 | + | "node": ">=22.0.0" |
|
| 990 | + | } |
|
| 991 | + | }, |
|
| 992 | + | "node_modules/@vscode/vsce": { |
|
| 993 | + | "version": "4.0.0", |
|
| 994 | + | "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-4.0.0.tgz", |
|
| 995 | + | "integrity": "sha512-NImwuLaenMmb5D5Jer9/lzi/F9ZQUBOp8Azhj/BVYcTFgixv8KehFXqEUDjQlD2tAiw2E6dDGyjTuAB//di60A==", |
|
| 996 | + | "dev": true, |
|
| 997 | + | "license": "MIT", |
|
| 998 | + | "dependencies": { |
|
| 999 | + | "@azure/identity": "^4.13.2", |
|
| 1000 | + | "@napi-rs/keyring": "^1.3.0", |
|
| 1001 | + | "@secretlint/core": "^10.2.2", |
|
| 1002 | + | "@secretlint/secretlint-rule-no-dotenv": "^10.2.2", |
|
| 1003 | + | "@secretlint/secretlint-rule-preset-recommend": "^10.2.2", |
|
| 1004 | + | "@secretlint/source-creator": "^10.2.2", |
|
| 1005 | + | "@secretlint/types": "^10.2.2", |
|
| 1006 | + | "@vscode/vsce-sign": "^2.1.0", |
|
| 1007 | + | "azure-devops-node-api": "^12.5.0", |
|
| 1008 | + | "cockatiel": "^3.2.1", |
|
| 1009 | + | "commander": "^12.1.0", |
|
| 1010 | + | "hosted-git-info": "^4.1.0", |
|
| 1011 | + | "jsonc-parser": "^3.3.1", |
|
| 1012 | + | "marked": "^18.0.11", |
|
| 1013 | + | "mime": "^1.6.0", |
|
| 1014 | + | "minimatch": "^10.2.6", |
|
| 1015 | + | "parse5": "^8.0.1", |
|
| 1016 | + | "proper-lockfile": "^4.1.2", |
|
| 1017 | + | "read": "^1.0.7", |
|
| 1018 | + | "semver": "^7.8.5", |
|
| 1019 | + | "tinyglobby": "^0.2.17", |
|
| 1020 | + | "typed-rest-client": "^1.8.11", |
|
| 1021 | + | "url-join": "^4.0.1", |
|
| 1022 | + | "xml2js": "^0.5.0", |
|
| 1023 | + | "yauzl": "^3.4.0", |
|
| 1024 | + | "yazl": "^2.5.1" |
|
| 1025 | + | }, |
|
| 1026 | + | "bin": { |
|
| 1027 | + | "vsce": "vsce" |
|
| 1028 | + | }, |
|
| 1029 | + | "engines": { |
|
| 1030 | + | "node": ">= 22" |
|
| 1031 | + | } |
|
| 1032 | + | }, |
|
| 1033 | + | "node_modules/@vscode/vsce-sign": { |
|
| 1034 | + | "version": "2.1.0", |
|
| 1035 | + | "resolved": "https://registry.npmjs.org/@vscode/vsce-sign/-/vsce-sign-2.1.0.tgz", |
|
| 1036 | + | "integrity": "sha512-9AQrqazrBgTgRSuwleLVXUrIUphY02/SFCh2TKYoLV/xifJAdblhdmEmw5gUrYSPQ3sRwNs9iyCMD14sATEE6g==", |
|
| 1037 | + | "dev": true, |
|
| 1038 | + | "hasInstallScript": true, |
|
| 1039 | + | "license": "SEE LICENSE IN LICENSE.txt", |
|
| 1040 | + | "optionalDependencies": { |
|
| 1041 | + | "@vscode/vsce-sign-alpine-arm64": "2.0.6", |
|
| 1042 | + | "@vscode/vsce-sign-alpine-x64": "2.0.6", |
|
| 1043 | + | "@vscode/vsce-sign-darwin-arm64": "2.0.6", |
|
| 1044 | + | "@vscode/vsce-sign-darwin-x64": "2.0.6", |
|
| 1045 | + | "@vscode/vsce-sign-linux-arm": "2.0.6", |
|
| 1046 | + | "@vscode/vsce-sign-linux-arm64": "2.0.6", |
|
| 1047 | + | "@vscode/vsce-sign-linux-x64": "2.0.6", |
|
| 1048 | + | "@vscode/vsce-sign-win32-arm64": "2.0.6", |
|
| 1049 | + | "@vscode/vsce-sign-win32-x64": "2.0.6" |
|
| 1050 | + | } |
|
| 1051 | + | }, |
|
| 1052 | + | "node_modules/@vscode/vsce-sign-alpine-arm64": { |
|
| 1053 | + | "version": "2.0.6", |
|
| 1054 | + | "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-alpine-arm64/-/vsce-sign-alpine-arm64-2.0.6.tgz", |
|
| 1055 | + | "integrity": "sha512-wKkJBsvKF+f0GfsUuGT0tSW0kZL87QggEiqNqK6/8hvqsXvpx8OsTEc3mnE1kejkh5r+qUyQ7PtF8jZYN0mo8Q==", |
|
| 1056 | + | "cpu": [ |
|
| 1057 | + | "arm64" |
|
| 1058 | + | ], |
|
| 1059 | + | "dev": true, |
|
| 1060 | + | "license": "SEE LICENSE IN LICENSE.txt", |
|
| 1061 | + | "optional": true, |
|
| 1062 | + | "os": [ |
|
| 1063 | + | "alpine" |
|
| 1064 | + | ] |
|
| 1065 | + | }, |
|
| 1066 | + | "node_modules/@vscode/vsce-sign-alpine-x64": { |
|
| 1067 | + | "version": "2.0.6", |
|
| 1068 | + | "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-alpine-x64/-/vsce-sign-alpine-x64-2.0.6.tgz", |
|
| 1069 | + | "integrity": "sha512-YoAGlmdK39vKi9jA18i4ufBbd95OqGJxRvF3n6ZbCyziwy3O+JgOpIUPxv5tjeO6gQfx29qBivQ8ZZTUF2Ba0w==", |
|
| 1070 | + | "cpu": [ |
|
| 1071 | + | "x64" |
|
| 1072 | + | ], |
|
| 1073 | + | "dev": true, |
|
| 1074 | + | "license": "SEE LICENSE IN LICENSE.txt", |
|
| 1075 | + | "optional": true, |
|
| 1076 | + | "os": [ |
|
| 1077 | + | "alpine" |
|
| 1078 | + | ] |
|
| 1079 | + | }, |
|
| 1080 | + | "node_modules/@vscode/vsce-sign-darwin-arm64": { |
|
| 1081 | + | "version": "2.0.6", |
|
| 1082 | + | "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-darwin-arm64/-/vsce-sign-darwin-arm64-2.0.6.tgz", |
|
| 1083 | + | "integrity": "sha512-5HMHaJRIQuozm/XQIiJiA0W9uhdblwwl2ZNDSSAeXGO9YhB9MH5C4KIHOmvyjUnKy4UCuiP43VKpIxW1VWP4tQ==", |
|
| 1084 | + | "cpu": [ |
|
| 1085 | + | "arm64" |
|
| 1086 | + | ], |
|
| 1087 | + | "dev": true, |
|
| 1088 | + | "license": "SEE LICENSE IN LICENSE.txt", |
|
| 1089 | + | "optional": true, |
|
| 1090 | + | "os": [ |
|
| 1091 | + | "darwin" |
|
| 1092 | + | ] |
|
| 1093 | + | }, |
|
| 1094 | + | "node_modules/@vscode/vsce-sign-darwin-x64": { |
|
| 1095 | + | "version": "2.0.6", |
|
| 1096 | + | "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-darwin-x64/-/vsce-sign-darwin-x64-2.0.6.tgz", |
|
| 1097 | + | "integrity": "sha512-25GsUbTAiNfHSuRItoQafXOIpxlYj+IXb4/qarrXu7kmbH94jlm5sdWSCKrrREs8+GsXF1b+l3OB7VJy5jsykw==", |
|
| 1098 | + | "cpu": [ |
|
| 1099 | + | "x64" |
|
| 1100 | + | ], |
|
| 1101 | + | "dev": true, |
|
| 1102 | + | "license": "SEE LICENSE IN LICENSE.txt", |
|
| 1103 | + | "optional": true, |
|
| 1104 | + | "os": [ |
|
| 1105 | + | "darwin" |
|
| 1106 | + | ] |
|
| 1107 | + | }, |
|
| 1108 | + | "node_modules/@vscode/vsce-sign-linux-arm": { |
|
| 1109 | + | "version": "2.0.6", |
|
| 1110 | + | "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-arm/-/vsce-sign-linux-arm-2.0.6.tgz", |
|
| 1111 | + | "integrity": "sha512-UndEc2Xlq4HsuMPnwu7420uqceXjs4yb5W8E2/UkaHBB9OWCwMd3/bRe/1eLe3D8kPpxzcaeTyXiK3RdzS/1CA==", |
|
| 1112 | + | "cpu": [ |
|
| 1113 | + | "arm" |
|
| 1114 | + | ], |
|
| 1115 | + | "dev": true, |
|
| 1116 | + | "license": "SEE LICENSE IN LICENSE.txt", |
|
| 1117 | + | "optional": true, |
|
| 1118 | + | "os": [ |
|
| 1119 | + | "linux" |
|
| 1120 | + | ] |
|
| 1121 | + | }, |
|
| 1122 | + | "node_modules/@vscode/vsce-sign-linux-arm64": { |
|
| 1123 | + | "version": "2.0.6", |
|
| 1124 | + | "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-arm64/-/vsce-sign-linux-arm64-2.0.6.tgz", |
|
| 1125 | + | "integrity": "sha512-cfb1qK7lygtMa4NUl2582nP7aliLYuDEVpAbXJMkDq1qE+olIw/es+C8j1LJwvcRq1I2yWGtSn3EkDp9Dq5FdA==", |
|
| 1126 | + | "cpu": [ |
|
| 1127 | + | "arm64" |
|
| 1128 | + | ], |
|
| 1129 | + | "dev": true, |
|
| 1130 | + | "license": "SEE LICENSE IN LICENSE.txt", |
|
| 1131 | + | "optional": true, |
|
| 1132 | + | "os": [ |
|
| 1133 | + | "linux" |
|
| 1134 | + | ] |
|
| 1135 | + | }, |
|
| 1136 | + | "node_modules/@vscode/vsce-sign-linux-x64": { |
|
| 1137 | + | "version": "2.0.6", |
|
| 1138 | + | "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-x64/-/vsce-sign-linux-x64-2.0.6.tgz", |
|
| 1139 | + | "integrity": "sha512-/olerl1A4sOqdP+hjvJ1sbQjKN07Y3DVnxO4gnbn/ahtQvFrdhUi0G1VsZXDNjfqmXw57DmPi5ASnj/8PGZhAA==", |
|
| 1140 | + | "cpu": [ |
|
| 1141 | + | "x64" |
|
| 1142 | + | ], |
|
| 1143 | + | "dev": true, |
|
| 1144 | + | "license": "SEE LICENSE IN LICENSE.txt", |
|
| 1145 | + | "optional": true, |
|
| 1146 | + | "os": [ |
|
| 1147 | + | "linux" |
|
| 1148 | + | ] |
|
| 1149 | + | }, |
|
| 1150 | + | "node_modules/@vscode/vsce-sign-win32-arm64": { |
|
| 1151 | + | "version": "2.0.6", |
|
| 1152 | + | "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-win32-arm64/-/vsce-sign-win32-arm64-2.0.6.tgz", |
|
| 1153 | + | "integrity": "sha512-ivM/MiGIY0PJNZBoGtlRBM/xDpwbdlCWomUWuLmIxbi1Cxe/1nooYrEQoaHD8ojVRgzdQEUzMsRbyF5cJJgYOg==", |
|
| 1154 | + | "cpu": [ |
|
| 1155 | + | "arm64" |
|
| 1156 | + | ], |
|
| 1157 | + | "dev": true, |
|
| 1158 | + | "license": "SEE LICENSE IN LICENSE.txt", |
|
| 1159 | + | "optional": true, |
|
| 1160 | + | "os": [ |
|
| 1161 | + | "win32" |
|
| 1162 | + | ] |
|
| 1163 | + | }, |
|
| 1164 | + | "node_modules/@vscode/vsce-sign-win32-x64": { |
|
| 1165 | + | "version": "2.0.6", |
|
| 1166 | + | "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-win32-x64/-/vsce-sign-win32-x64-2.0.6.tgz", |
|
| 1167 | + | "integrity": "sha512-mgth9Kvze+u8CruYMmhHw6Zgy3GRX2S+Ed5oSokDEK5vPEwGGKnmuXua9tmFhomeAnhgJnL4DCna3TiNuGrBTQ==", |
|
| 1168 | + | "cpu": [ |
|
| 1169 | + | "x64" |
|
| 1170 | + | ], |
|
| 1171 | + | "dev": true, |
|
| 1172 | + | "license": "SEE LICENSE IN LICENSE.txt", |
|
| 1173 | + | "optional": true, |
|
| 1174 | + | "os": [ |
|
| 1175 | + | "win32" |
|
| 1176 | + | ] |
|
| 1177 | + | }, |
|
| 1178 | + | "node_modules/agent-base": { |
|
| 1179 | + | "version": "7.1.4", |
|
| 1180 | + | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", |
|
| 1181 | + | "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", |
|
| 1182 | + | "dev": true, |
|
| 1183 | + | "license": "MIT", |
|
| 1184 | + | "engines": { |
|
| 1185 | + | "node": ">= 14" |
|
| 1186 | + | } |
|
| 1187 | + | }, |
|
| 1188 | + | "node_modules/azure-devops-node-api": { |
|
| 1189 | + | "version": "12.5.0", |
|
| 1190 | + | "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-12.5.0.tgz", |
|
| 1191 | + | "integrity": "sha512-R5eFskGvOm3U/GzeAuxRkUsAl0hrAwGgWn6zAd2KrZmrEhWZVqLew4OOupbQlXUuojUzpGtq62SmdhJ06N88og==", |
|
| 1192 | + | "dev": true, |
|
| 1193 | + | "license": "MIT", |
|
| 1194 | + | "dependencies": { |
|
| 1195 | + | "tunnel": "0.0.6", |
|
| 1196 | + | "typed-rest-client": "^1.8.4" |
|
| 1197 | + | } |
|
| 1198 | + | }, |
|
| 1199 | + | "node_modules/balanced-match": { |
|
| 1200 | + | "version": "4.0.4", |
|
| 1201 | + | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", |
|
| 1202 | + | "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", |
|
| 1203 | + | "dev": true, |
|
| 1204 | + | "license": "MIT", |
|
| 1205 | + | "engines": { |
|
| 1206 | + | "node": "18 || 20 || >=22" |
|
| 1207 | + | } |
|
| 1208 | + | }, |
|
| 1209 | + | "node_modules/binaryextensions": { |
|
| 1210 | + | "version": "6.11.0", |
|
| 1211 | + | "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-6.11.0.tgz", |
|
| 1212 | + | "integrity": "sha512-sXnYK/Ij80TO3lcqZVV2YgfKN5QjUWIRk/XSm2J/4bd/lPko3lvk0O4ZppH6m+6hB2/GTu+ptNwVFe1xh+QLQw==", |
|
| 1213 | + | "dev": true, |
|
| 1214 | + | "license": "Artistic-2.0", |
|
| 1215 | + | "dependencies": { |
|
| 1216 | + | "editions": "^6.21.0" |
|
| 1217 | + | }, |
|
| 1218 | + | "engines": { |
|
| 1219 | + | "node": ">=4" |
|
| 1220 | + | }, |
|
| 1221 | + | "funding": { |
|
| 1222 | + | "url": "https://bevry.me/fund" |
|
| 1223 | + | } |
|
| 1224 | + | }, |
|
| 1225 | + | "node_modules/boundary": { |
|
| 1226 | + | "version": "2.0.0", |
|
| 1227 | + | "resolved": "https://registry.npmjs.org/boundary/-/boundary-2.0.0.tgz", |
|
| 1228 | + | "integrity": "sha512-rJKn5ooC9u8q13IMCrW0RSp31pxBCHE3y9V/tp3TdWSLf8Em3p6Di4NBpfzbJge9YjjFEsD0RtFEjtvHL5VyEA==", |
|
| 1229 | + | "dev": true, |
|
| 1230 | + | "license": "BSD-2-Clause" |
|
| 1231 | + | }, |
|
| 1232 | + | "node_modules/brace-expansion": { |
|
| 1233 | + | "version": "5.0.12", |
|
| 1234 | + | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", |
|
| 1235 | + | "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", |
|
| 1236 | + | "dev": true, |
|
| 1237 | + | "license": "MIT", |
|
| 1238 | + | "dependencies": { |
|
| 1239 | + | "balanced-match": "^4.0.2" |
|
| 1240 | + | }, |
|
| 1241 | + | "engines": { |
|
| 1242 | + | "node": "20 || >=22" |
|
| 1243 | + | } |
|
| 1244 | + | }, |
|
| 1245 | + | "node_modules/buffer-crc32": { |
|
| 1246 | + | "version": "0.2.13", |
|
| 1247 | + | "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", |
|
| 1248 | + | "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", |
|
| 1249 | + | "dev": true, |
|
| 1250 | + | "license": "MIT", |
|
| 1251 | + | "engines": { |
|
| 1252 | + | "node": "*" |
|
| 1253 | + | } |
|
| 1254 | + | }, |
|
| 1255 | + | "node_modules/buffer-equal-constant-time": { |
|
| 1256 | + | "version": "1.0.1", |
|
| 1257 | + | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", |
|
| 1258 | + | "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", |
|
| 1259 | + | "dev": true, |
|
| 1260 | + | "license": "BSD-3-Clause" |
|
| 1261 | + | }, |
|
| 1262 | + | "node_modules/bundle-name": { |
|
| 1263 | + | "version": "4.1.1", |
|
| 1264 | + | "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.1.tgz", |
|
| 1265 | + | "integrity": "sha512-DdH81/zPLVS11EUgWq3tEu/xn+EzljlMYooDNdzWEnFha3R3NBMpMV1UqYIpjYHV/SgpFKMIX1Oh7o06SjM/oA==", |
|
| 1266 | + | "dev": true, |
|
| 1267 | + | "license": "MIT", |
|
| 1268 | + | "dependencies": { |
|
| 1269 | + | "run-applescript": "^7.0.0" |
|
| 1270 | + | }, |
|
| 1271 | + | "engines": { |
|
| 1272 | + | "node": ">=18" |
|
| 1273 | + | }, |
|
| 1274 | + | "funding": { |
|
| 1275 | + | "url": "https://github.com/sponsors/sindresorhus" |
|
| 1276 | + | } |
|
| 1277 | + | }, |
|
| 1278 | + | "node_modules/call-bind-apply-helpers": { |
|
| 1279 | + | "version": "1.0.2", |
|
| 1280 | + | "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", |
|
| 1281 | + | "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", |
|
| 1282 | + | "dev": true, |
|
| 1283 | + | "license": "MIT", |
|
| 1284 | + | "dependencies": { |
|
| 1285 | + | "es-errors": "^1.3.0", |
|
| 1286 | + | "function-bind": "^1.1.2" |
|
| 1287 | + | }, |
|
| 1288 | + | "engines": { |
|
| 1289 | + | "node": ">= 0.4" |
|
| 1290 | + | } |
|
| 1291 | + | }, |
|
| 1292 | + | "node_modules/call-bound": { |
|
| 1293 | + | "version": "1.0.4", |
|
| 1294 | + | "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", |
|
| 1295 | + | "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", |
|
| 1296 | + | "dev": true, |
|
| 1297 | + | "license": "MIT", |
|
| 1298 | + | "dependencies": { |
|
| 1299 | + | "call-bind-apply-helpers": "^1.0.2", |
|
| 1300 | + | "get-intrinsic": "^1.3.0" |
|
| 1301 | + | }, |
|
| 1302 | + | "engines": { |
|
| 1303 | + | "node": ">= 0.4" |
|
| 1304 | + | }, |
|
| 1305 | + | "funding": { |
|
| 1306 | + | "url": "https://github.com/sponsors/ljharb" |
|
| 1307 | + | } |
|
| 1308 | + | }, |
|
| 1309 | + | "node_modules/cockatiel": { |
|
| 1310 | + | "version": "3.2.1", |
|
| 1311 | + | "resolved": "https://registry.npmjs.org/cockatiel/-/cockatiel-3.2.1.tgz", |
|
| 1312 | + | "integrity": "sha512-gfrHV6ZPkquExvMh9IOkKsBzNDk6sDuZ6DdBGUBkvFnTCqCxzpuq48RySgP0AnaqQkw2zynOFj9yly6T1Q2G5Q==", |
|
| 1313 | + | "dev": true, |
|
| 1314 | + | "license": "MIT", |
|
| 1315 | + | "engines": { |
|
| 1316 | + | "node": ">=16" |
|
| 1317 | + | } |
|
| 1318 | + | }, |
|
| 1319 | + | "node_modules/commander": { |
|
| 1320 | + | "version": "12.1.0", |
|
| 1321 | + | "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", |
|
| 1322 | + | "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", |
|
| 1323 | + | "dev": true, |
|
| 1324 | + | "license": "MIT", |
|
| 1325 | + | "engines": { |
|
| 1326 | + | "node": ">=18" |
|
| 1327 | + | } |
|
| 1328 | + | }, |
|
| 1329 | + | "node_modules/debug": { |
|
| 1330 | + | "version": "4.4.3", |
|
| 1331 | + | "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", |
|
| 1332 | + | "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", |
|
| 1333 | + | "dev": true, |
|
| 1334 | + | "license": "MIT", |
|
| 1335 | + | "dependencies": { |
|
| 1336 | + | "ms": "^2.1.3" |
|
| 1337 | + | }, |
|
| 1338 | + | "engines": { |
|
| 1339 | + | "node": ">=6.0" |
|
| 1340 | + | }, |
|
| 1341 | + | "peerDependenciesMeta": { |
|
| 1342 | + | "supports-color": { |
|
| 1343 | + | "optional": true |
|
| 1344 | + | } |
|
| 1345 | + | } |
|
| 1346 | + | }, |
|
| 1347 | + | "node_modules/default-browser": { |
|
| 1348 | + | "version": "5.5.1", |
|
| 1349 | + | "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.1.tgz", |
|
| 1350 | + | "integrity": "sha512-m1pAzaJgZ/gssEqlOhJkPJp8Xly7QyW6xcrkUa2KKcDeDSEMP7X8xipU3snUcfisTQx0w1AGae+9UtJSfVnXGw==", |
|
| 1351 | + | "dev": true, |
|
| 1352 | + | "license": "MIT", |
|
| 1353 | + | "dependencies": { |
|
| 1354 | + | "bundle-name": "^4.1.0", |
|
| 1355 | + | "default-browser-id": "^5.0.0" |
|
| 1356 | + | }, |
|
| 1357 | + | "engines": { |
|
| 1358 | + | "node": ">=18" |
|
| 1359 | + | }, |
|
| 1360 | + | "funding": { |
|
| 1361 | + | "url": "https://github.com/sponsors/sindresorhus" |
|
| 1362 | + | } |
|
| 1363 | + | }, |
|
| 1364 | + | "node_modules/default-browser-id": { |
|
| 1365 | + | "version": "5.0.1", |
|
| 1366 | + | "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", |
|
| 1367 | + | "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", |
|
| 1368 | + | "dev": true, |
|
| 1369 | + | "license": "MIT", |
|
| 1370 | + | "engines": { |
|
| 1371 | + | "node": ">=18" |
|
| 1372 | + | }, |
|
| 1373 | + | "funding": { |
|
| 1374 | + | "url": "https://github.com/sponsors/sindresorhus" |
|
| 1375 | + | } |
|
| 1376 | + | }, |
|
| 1377 | + | "node_modules/define-lazy-prop": { |
|
| 1378 | + | "version": "3.0.0", |
|
| 1379 | + | "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", |
|
| 1380 | + | "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", |
|
| 1381 | + | "dev": true, |
|
| 1382 | + | "license": "MIT", |
|
| 1383 | + | "engines": { |
|
| 1384 | + | "node": ">=12" |
|
| 1385 | + | }, |
|
| 1386 | + | "funding": { |
|
| 1387 | + | "url": "https://github.com/sponsors/sindresorhus" |
|
| 1388 | + | } |
|
| 1389 | + | }, |
|
| 1390 | + | "node_modules/dunder-proto": { |
|
| 1391 | + | "version": "1.0.1", |
|
| 1392 | + | "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", |
|
| 1393 | + | "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", |
|
| 1394 | + | "dev": true, |
|
| 1395 | + | "license": "MIT", |
|
| 1396 | + | "dependencies": { |
|
| 1397 | + | "call-bind-apply-helpers": "^1.0.1", |
|
| 1398 | + | "es-errors": "^1.3.0", |
|
| 1399 | + | "gopd": "^1.2.0" |
|
| 1400 | + | }, |
|
| 1401 | + | "engines": { |
|
| 1402 | + | "node": ">= 0.4" |
|
| 1403 | + | } |
|
| 1404 | + | }, |
|
| 1405 | + | "node_modules/ecdsa-sig-formatter": { |
|
| 1406 | + | "version": "1.0.11", |
|
| 1407 | + | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", |
|
| 1408 | + | "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", |
|
| 1409 | + | "dev": true, |
|
| 1410 | + | "license": "Apache-2.0", |
|
| 1411 | + | "dependencies": { |
|
| 1412 | + | "safe-buffer": "^5.0.1" |
|
| 1413 | + | } |
|
| 1414 | + | }, |
|
| 1415 | + | "node_modules/editions": { |
|
| 1416 | + | "version": "6.22.0", |
|
| 1417 | + | "resolved": "https://registry.npmjs.org/editions/-/editions-6.22.0.tgz", |
|
| 1418 | + | "integrity": "sha512-UgGlf8IW75je7HZjNDpJdCv4cGJWIi6yumFdZ0R7A8/CIhQiWUjyGLCxdHpd8bmyD1gnkfUNK0oeOXqUS2cpfQ==", |
|
| 1419 | + | "dev": true, |
|
| 1420 | + | "license": "Artistic-2.0", |
|
| 1421 | + | "dependencies": { |
|
| 1422 | + | "version-range": "^4.15.0" |
|
| 1423 | + | }, |
|
| 1424 | + | "engines": { |
|
| 1425 | + | "ecmascript": ">= es5", |
|
| 1426 | + | "node": ">=4" |
|
| 1427 | + | }, |
|
| 1428 | + | "funding": { |
|
| 1429 | + | "url": "https://bevry.me/fund" |
|
| 1430 | + | } |
|
| 1431 | + | }, |
|
| 1432 | + | "node_modules/entities": { |
|
| 1433 | + | "version": "8.1.0", |
|
| 1434 | + | "resolved": "https://registry.npmjs.org/entities/-/entities-8.1.0.tgz", |
|
| 1435 | + | "integrity": "sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==", |
|
| 1436 | + | "dev": true, |
|
| 1437 | + | "license": "BSD-2-Clause", |
|
| 1438 | + | "engines": { |
|
| 1439 | + | "node": ">=20.19.0" |
|
| 1440 | + | }, |
|
| 1441 | + | "funding": { |
|
| 1442 | + | "url": "https://github.com/fb55/entities?sponsor=1" |
|
| 1443 | + | } |
|
| 1444 | + | }, |
|
| 1445 | + | "node_modules/es-define-property": { |
|
| 1446 | + | "version": "1.0.1", |
|
| 1447 | + | "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", |
|
| 1448 | + | "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", |
|
| 1449 | + | "dev": true, |
|
| 1450 | + | "license": "MIT", |
|
| 1451 | + | "engines": { |
|
| 1452 | + | "node": ">= 0.4" |
|
| 1453 | + | } |
|
| 1454 | + | }, |
|
| 1455 | + | "node_modules/es-errors": { |
|
| 1456 | + | "version": "1.3.0", |
|
| 1457 | + | "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", |
|
| 1458 | + | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", |
|
| 1459 | + | "dev": true, |
|
| 1460 | + | "license": "MIT", |
|
| 1461 | + | "engines": { |
|
| 1462 | + | "node": ">= 0.4" |
|
| 1463 | + | } |
|
| 1464 | + | }, |
|
| 1465 | + | "node_modules/es-object-atoms": { |
|
| 1466 | + | "version": "1.1.2", |
|
| 1467 | + | "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", |
|
| 1468 | + | "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", |
|
| 1469 | + | "dev": true, |
|
| 1470 | + | "license": "MIT", |
|
| 1471 | + | "dependencies": { |
|
| 1472 | + | "es-errors": "^1.3.0" |
|
| 1473 | + | }, |
|
| 1474 | + | "engines": { |
|
| 1475 | + | "node": ">= 0.4" |
|
| 1476 | + | } |
|
| 1477 | + | }, |
|
| 1478 | + | "node_modules/esbuild": { |
|
| 1479 | + | "version": "0.28.2", |
|
| 1480 | + | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", |
|
| 1481 | + | "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", |
|
| 1482 | + | "dev": true, |
|
| 1483 | + | "hasInstallScript": true, |
|
| 1484 | + | "license": "MIT", |
|
| 1485 | + | "bin": { |
|
| 1486 | + | "esbuild": "bin/esbuild" |
|
| 1487 | + | }, |
|
| 1488 | + | "engines": { |
|
| 1489 | + | "node": ">=18" |
|
| 1490 | + | }, |
|
| 1491 | + | "optionalDependencies": { |
|
| 1492 | + | "@esbuild/aix-ppc64": "0.28.2", |
|
| 1493 | + | "@esbuild/android-arm": "0.28.2", |
|
| 1494 | + | "@esbuild/android-arm64": "0.28.2", |
|
| 1495 | + | "@esbuild/android-x64": "0.28.2", |
|
| 1496 | + | "@esbuild/darwin-arm64": "0.28.2", |
|
| 1497 | + | "@esbuild/darwin-x64": "0.28.2", |
|
| 1498 | + | "@esbuild/freebsd-arm64": "0.28.2", |
|
| 1499 | + | "@esbuild/freebsd-x64": "0.28.2", |
|
| 1500 | + | "@esbuild/linux-arm": "0.28.2", |
|
| 1501 | + | "@esbuild/linux-arm64": "0.28.2", |
|
| 1502 | + | "@esbuild/linux-ia32": "0.28.2", |
|
| 1503 | + | "@esbuild/linux-loong64": "0.28.2", |
|
| 1504 | + | "@esbuild/linux-mips64el": "0.28.2", |
|
| 1505 | + | "@esbuild/linux-ppc64": "0.28.2", |
|
| 1506 | + | "@esbuild/linux-riscv64": "0.28.2", |
|
| 1507 | + | "@esbuild/linux-s390x": "0.28.2", |
|
| 1508 | + | "@esbuild/linux-x64": "0.28.2", |
|
| 1509 | + | "@esbuild/netbsd-arm64": "0.28.2", |
|
| 1510 | + | "@esbuild/netbsd-x64": "0.28.2", |
|
| 1511 | + | "@esbuild/openbsd-arm64": "0.28.2", |
|
| 1512 | + | "@esbuild/openbsd-x64": "0.28.2", |
|
| 1513 | + | "@esbuild/openharmony-arm64": "0.28.2", |
|
| 1514 | + | "@esbuild/sunos-x64": "0.28.2", |
|
| 1515 | + | "@esbuild/win32-arm64": "0.28.2", |
|
| 1516 | + | "@esbuild/win32-ia32": "0.28.2", |
|
| 1517 | + | "@esbuild/win32-x64": "0.28.2" |
|
| 1518 | + | } |
|
| 1519 | + | }, |
|
| 1520 | + | "node_modules/fdir": { |
|
| 1521 | + | "version": "6.5.0", |
|
| 1522 | + | "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", |
|
| 1523 | + | "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", |
|
| 1524 | + | "dev": true, |
|
| 1525 | + | "license": "MIT", |
|
| 1526 | + | "engines": { |
|
| 1527 | + | "node": ">=12.0.0" |
|
| 1528 | + | }, |
|
| 1529 | + | "peerDependencies": { |
|
| 1530 | + | "picomatch": "^3 || ^4" |
|
| 1531 | + | }, |
|
| 1532 | + | "peerDependenciesMeta": { |
|
| 1533 | + | "picomatch": { |
|
| 1534 | + | "optional": true |
|
| 1535 | + | } |
|
| 1536 | + | } |
|
| 1537 | + | }, |
|
| 1538 | + | "node_modules/function-bind": { |
|
| 1539 | + | "version": "1.1.2", |
|
| 1540 | + | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", |
|
| 1541 | + | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", |
|
| 1542 | + | "dev": true, |
|
| 1543 | + | "license": "MIT", |
|
| 1544 | + | "funding": { |
|
| 1545 | + | "url": "https://github.com/sponsors/ljharb" |
|
| 1546 | + | } |
|
| 1547 | + | }, |
|
| 1548 | + | "node_modules/get-intrinsic": { |
|
| 1549 | + | "version": "1.3.0", |
|
| 1550 | + | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", |
|
| 1551 | + | "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", |
|
| 1552 | + | "dev": true, |
|
| 1553 | + | "license": "MIT", |
|
| 1554 | + | "dependencies": { |
|
| 1555 | + | "call-bind-apply-helpers": "^1.0.2", |
|
| 1556 | + | "es-define-property": "^1.0.1", |
|
| 1557 | + | "es-errors": "^1.3.0", |
|
| 1558 | + | "es-object-atoms": "^1.1.1", |
|
| 1559 | + | "function-bind": "^1.1.2", |
|
| 1560 | + | "get-proto": "^1.0.1", |
|
| 1561 | + | "gopd": "^1.2.0", |
|
| 1562 | + | "has-symbols": "^1.1.0", |
|
| 1563 | + | "hasown": "^2.0.2", |
|
| 1564 | + | "math-intrinsics": "^1.1.0" |
|
| 1565 | + | }, |
|
| 1566 | + | "engines": { |
|
| 1567 | + | "node": ">= 0.4" |
|
| 1568 | + | }, |
|
| 1569 | + | "funding": { |
|
| 1570 | + | "url": "https://github.com/sponsors/ljharb" |
|
| 1571 | + | } |
|
| 1572 | + | }, |
|
| 1573 | + | "node_modules/get-proto": { |
|
| 1574 | + | "version": "1.0.1", |
|
| 1575 | + | "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", |
|
| 1576 | + | "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", |
|
| 1577 | + | "dev": true, |
|
| 1578 | + | "license": "MIT", |
|
| 1579 | + | "dependencies": { |
|
| 1580 | + | "dunder-proto": "^1.0.1", |
|
| 1581 | + | "es-object-atoms": "^1.0.0" |
|
| 1582 | + | }, |
|
| 1583 | + | "engines": { |
|
| 1584 | + | "node": ">= 0.4" |
|
| 1585 | + | } |
|
| 1586 | + | }, |
|
| 1587 | + | "node_modules/gopd": { |
|
| 1588 | + | "version": "1.2.0", |
|
| 1589 | + | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", |
|
| 1590 | + | "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", |
|
| 1591 | + | "dev": true, |
|
| 1592 | + | "license": "MIT", |
|
| 1593 | + | "engines": { |
|
| 1594 | + | "node": ">= 0.4" |
|
| 1595 | + | }, |
|
| 1596 | + | "funding": { |
|
| 1597 | + | "url": "https://github.com/sponsors/ljharb" |
|
| 1598 | + | } |
|
| 1599 | + | }, |
|
| 1600 | + | "node_modules/graceful-fs": { |
|
| 1601 | + | "version": "4.2.11", |
|
| 1602 | + | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", |
|
| 1603 | + | "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", |
|
| 1604 | + | "dev": true, |
|
| 1605 | + | "license": "ISC" |
|
| 1606 | + | }, |
|
| 1607 | + | "node_modules/has-symbols": { |
|
| 1608 | + | "version": "1.1.0", |
|
| 1609 | + | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", |
|
| 1610 | + | "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", |
|
| 1611 | + | "dev": true, |
|
| 1612 | + | "license": "MIT", |
|
| 1613 | + | "engines": { |
|
| 1614 | + | "node": ">= 0.4" |
|
| 1615 | + | }, |
|
| 1616 | + | "funding": { |
|
| 1617 | + | "url": "https://github.com/sponsors/ljharb" |
|
| 1618 | + | } |
|
| 1619 | + | }, |
|
| 1620 | + | "node_modules/hasown": { |
|
| 1621 | + | "version": "2.0.4", |
|
| 1622 | + | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", |
|
| 1623 | + | "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", |
|
| 1624 | + | "dev": true, |
|
| 1625 | + | "license": "MIT", |
|
| 1626 | + | "dependencies": { |
|
| 1627 | + | "function-bind": "^1.1.2" |
|
| 1628 | + | }, |
|
| 1629 | + | "engines": { |
|
| 1630 | + | "node": ">= 0.4" |
|
| 1631 | + | } |
|
| 1632 | + | }, |
|
| 1633 | + | "node_modules/hosted-git-info": { |
|
| 1634 | + | "version": "4.1.0", |
|
| 1635 | + | "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", |
|
| 1636 | + | "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", |
|
| 1637 | + | "dev": true, |
|
| 1638 | + | "license": "ISC", |
|
| 1639 | + | "dependencies": { |
|
| 1640 | + | "lru-cache": "^6.0.0" |
|
| 1641 | + | }, |
|
| 1642 | + | "engines": { |
|
| 1643 | + | "node": ">=10" |
|
| 1644 | + | } |
|
| 1645 | + | }, |
|
| 1646 | + | "node_modules/http-proxy-agent": { |
|
| 1647 | + | "version": "7.0.2", |
|
| 1648 | + | "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", |
|
| 1649 | + | "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", |
|
| 1650 | + | "dev": true, |
|
| 1651 | + | "license": "MIT", |
|
| 1652 | + | "dependencies": { |
|
| 1653 | + | "agent-base": "^7.1.0", |
|
| 1654 | + | "debug": "^4.3.4" |
|
| 1655 | + | }, |
|
| 1656 | + | "engines": { |
|
| 1657 | + | "node": ">= 14" |
|
| 1658 | + | } |
|
| 1659 | + | }, |
|
| 1660 | + | "node_modules/https-proxy-agent": { |
|
| 1661 | + | "version": "7.0.6", |
|
| 1662 | + | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", |
|
| 1663 | + | "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", |
|
| 1664 | + | "dev": true, |
|
| 1665 | + | "license": "MIT", |
|
| 1666 | + | "dependencies": { |
|
| 1667 | + | "agent-base": "^7.1.2", |
|
| 1668 | + | "debug": "4" |
|
| 1669 | + | }, |
|
| 1670 | + | "engines": { |
|
| 1671 | + | "node": ">= 14" |
|
| 1672 | + | } |
|
| 1673 | + | }, |
|
| 1674 | + | "node_modules/is-docker": { |
|
| 1675 | + | "version": "3.0.0", |
|
| 1676 | + | "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", |
|
| 1677 | + | "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", |
|
| 1678 | + | "dev": true, |
|
| 1679 | + | "license": "MIT", |
|
| 1680 | + | "bin": { |
|
| 1681 | + | "is-docker": "cli.js" |
|
| 1682 | + | }, |
|
| 1683 | + | "engines": { |
|
| 1684 | + | "node": "^12.20.0 || ^14.13.1 || >=16.0.0" |
|
| 1685 | + | }, |
|
| 1686 | + | "funding": { |
|
| 1687 | + | "url": "https://github.com/sponsors/sindresorhus" |
|
| 1688 | + | } |
|
| 1689 | + | }, |
|
| 1690 | + | "node_modules/is-inside-container": { |
|
| 1691 | + | "version": "1.0.0", |
|
| 1692 | + | "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", |
|
| 1693 | + | "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", |
|
| 1694 | + | "dev": true, |
|
| 1695 | + | "license": "MIT", |
|
| 1696 | + | "dependencies": { |
|
| 1697 | + | "is-docker": "^3.0.0" |
|
| 1698 | + | }, |
|
| 1699 | + | "bin": { |
|
| 1700 | + | "is-inside-container": "cli.js" |
|
| 1701 | + | }, |
|
| 1702 | + | "engines": { |
|
| 1703 | + | "node": ">=14.16" |
|
| 1704 | + | }, |
|
| 1705 | + | "funding": { |
|
| 1706 | + | "url": "https://github.com/sponsors/sindresorhus" |
|
| 1707 | + | } |
|
| 1708 | + | }, |
|
| 1709 | + | "node_modules/is-wsl": { |
|
| 1710 | + | "version": "3.1.1", |
|
| 1711 | + | "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", |
|
| 1712 | + | "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", |
|
| 1713 | + | "dev": true, |
|
| 1714 | + | "license": "MIT", |
|
| 1715 | + | "dependencies": { |
|
| 1716 | + | "is-inside-container": "^1.0.0" |
|
| 1717 | + | }, |
|
| 1718 | + | "engines": { |
|
| 1719 | + | "node": ">=16" |
|
| 1720 | + | }, |
|
| 1721 | + | "funding": { |
|
| 1722 | + | "url": "https://github.com/sponsors/sindresorhus" |
|
| 1723 | + | } |
|
| 1724 | + | }, |
|
| 1725 | + | "node_modules/istextorbinary": { |
|
| 1726 | + | "version": "9.5.0", |
|
| 1727 | + | "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-9.5.0.tgz", |
|
| 1728 | + | "integrity": "sha512-5mbUj3SiZXCuRf9fT3ibzbSSEWiy63gFfksmGfdOzujPjW3k+z8WvIBxcJHBoQNlaZaiyB25deviif2+osLmLw==", |
|
| 1729 | + | "dev": true, |
|
| 1730 | + | "license": "Artistic-2.0", |
|
| 1731 | + | "dependencies": { |
|
| 1732 | + | "binaryextensions": "^6.11.0", |
|
| 1733 | + | "editions": "^6.21.0", |
|
| 1734 | + | "textextensions": "^6.11.0" |
|
| 1735 | + | }, |
|
| 1736 | + | "engines": { |
|
| 1737 | + | "node": ">=4" |
|
| 1738 | + | }, |
|
| 1739 | + | "funding": { |
|
| 1740 | + | "url": "https://bevry.me/fund" |
|
| 1741 | + | } |
|
| 1742 | + | }, |
|
| 1743 | + | "node_modules/jsonc-parser": { |
|
| 1744 | + | "version": "3.3.1", |
|
| 1745 | + | "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", |
|
| 1746 | + | "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", |
|
| 1747 | + | "dev": true, |
|
| 1748 | + | "license": "MIT" |
|
| 1749 | + | }, |
|
| 1750 | + | "node_modules/jsonwebtoken": { |
|
| 1751 | + | "version": "9.0.3", |
|
| 1752 | + | "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", |
|
| 1753 | + | "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", |
|
| 1754 | + | "dev": true, |
|
| 1755 | + | "license": "MIT", |
|
| 1756 | + | "dependencies": { |
|
| 1757 | + | "jws": "^4.0.1", |
|
| 1758 | + | "lodash.includes": "^4.3.0", |
|
| 1759 | + | "lodash.isboolean": "^3.0.3", |
|
| 1760 | + | "lodash.isinteger": "^4.0.4", |
|
| 1761 | + | "lodash.isnumber": "^3.0.3", |
|
| 1762 | + | "lodash.isplainobject": "^4.0.6", |
|
| 1763 | + | "lodash.isstring": "^4.0.1", |
|
| 1764 | + | "lodash.once": "^4.0.0", |
|
| 1765 | + | "ms": "^2.1.1", |
|
| 1766 | + | "semver": "^7.5.4" |
|
| 1767 | + | }, |
|
| 1768 | + | "engines": { |
|
| 1769 | + | "node": ">=12", |
|
| 1770 | + | "npm": ">=6" |
|
| 1771 | + | } |
|
| 1772 | + | }, |
|
| 1773 | + | "node_modules/jwa": { |
|
| 1774 | + | "version": "2.0.1", |
|
| 1775 | + | "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", |
|
| 1776 | + | "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", |
|
| 1777 | + | "dev": true, |
|
| 1778 | + | "license": "MIT", |
|
| 1779 | + | "dependencies": { |
|
| 1780 | + | "buffer-equal-constant-time": "^1.0.1", |
|
| 1781 | + | "ecdsa-sig-formatter": "1.0.11", |
|
| 1782 | + | "safe-buffer": "^5.0.1" |
|
| 1783 | + | } |
|
| 1784 | + | }, |
|
| 1785 | + | "node_modules/jws": { |
|
| 1786 | + | "version": "4.0.1", |
|
| 1787 | + | "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", |
|
| 1788 | + | "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", |
|
| 1789 | + | "dev": true, |
|
| 1790 | + | "license": "MIT", |
|
| 1791 | + | "dependencies": { |
|
| 1792 | + | "jwa": "^2.0.1", |
|
| 1793 | + | "safe-buffer": "^5.0.1" |
|
| 1794 | + | } |
|
| 1795 | + | }, |
|
| 1796 | + | "node_modules/lodash.includes": { |
|
| 1797 | + | "version": "4.3.0", |
|
| 1798 | + | "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", |
|
| 1799 | + | "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", |
|
| 1800 | + | "dev": true, |
|
| 1801 | + | "license": "MIT" |
|
| 1802 | + | }, |
|
| 1803 | + | "node_modules/lodash.isboolean": { |
|
| 1804 | + | "version": "3.0.3", |
|
| 1805 | + | "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", |
|
| 1806 | + | "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", |
|
| 1807 | + | "dev": true, |
|
| 1808 | + | "license": "MIT" |
|
| 1809 | + | }, |
|
| 1810 | + | "node_modules/lodash.isinteger": { |
|
| 1811 | + | "version": "4.0.4", |
|
| 1812 | + | "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", |
|
| 1813 | + | "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", |
|
| 1814 | + | "dev": true, |
|
| 1815 | + | "license": "MIT" |
|
| 1816 | + | }, |
|
| 1817 | + | "node_modules/lodash.isnumber": { |
|
| 1818 | + | "version": "3.0.3", |
|
| 1819 | + | "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", |
|
| 1820 | + | "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", |
|
| 1821 | + | "dev": true, |
|
| 1822 | + | "license": "MIT" |
|
| 1823 | + | }, |
|
| 1824 | + | "node_modules/lodash.isplainobject": { |
|
| 1825 | + | "version": "4.0.6", |
|
| 1826 | + | "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", |
|
| 1827 | + | "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", |
|
| 1828 | + | "dev": true, |
|
| 1829 | + | "license": "MIT" |
|
| 1830 | + | }, |
|
| 1831 | + | "node_modules/lodash.isstring": { |
|
| 1832 | + | "version": "4.0.1", |
|
| 1833 | + | "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", |
|
| 1834 | + | "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", |
|
| 1835 | + | "dev": true, |
|
| 1836 | + | "license": "MIT" |
|
| 1837 | + | }, |
|
| 1838 | + | "node_modules/lodash.once": { |
|
| 1839 | + | "version": "4.1.1", |
|
| 1840 | + | "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", |
|
| 1841 | + | "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", |
|
| 1842 | + | "dev": true, |
|
| 1843 | + | "license": "MIT" |
|
| 1844 | + | }, |
|
| 1845 | + | "node_modules/lru-cache": { |
|
| 1846 | + | "version": "6.0.0", |
|
| 1847 | + | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", |
|
| 1848 | + | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", |
|
| 1849 | + | "dev": true, |
|
| 1850 | + | "license": "ISC", |
|
| 1851 | + | "dependencies": { |
|
| 1852 | + | "yallist": "^4.0.0" |
|
| 1853 | + | }, |
|
| 1854 | + | "engines": { |
|
| 1855 | + | "node": ">=10" |
|
| 1856 | + | } |
|
| 1857 | + | }, |
|
| 1858 | + | "node_modules/marked": { |
|
| 1859 | + | "version": "18.0.14", |
|
| 1860 | + | "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.14.tgz", |
|
| 1861 | + | "integrity": "sha512-mBHK6FBHuBAlhgRe88w9F0O1AbwwXJUcQibUbC/QcdTbVGAD7aWza+xt3N6oT/jCZx3/OMeS+8rnuiHZcQ9s7A==", |
|
| 1862 | + | "dev": true, |
|
| 1863 | + | "license": "MIT", |
|
| 1864 | + | "bin": { |
|
| 1865 | + | "marked": "bin/marked.js" |
|
| 1866 | + | }, |
|
| 1867 | + | "engines": { |
|
| 1868 | + | "node": ">= 20" |
|
| 1869 | + | } |
|
| 1870 | + | }, |
|
| 1871 | + | "node_modules/math-intrinsics": { |
|
| 1872 | + | "version": "1.1.0", |
|
| 1873 | + | "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", |
|
| 1874 | + | "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", |
|
| 1875 | + | "dev": true, |
|
| 1876 | + | "license": "MIT", |
|
| 1877 | + | "engines": { |
|
| 1878 | + | "node": ">= 0.4" |
|
| 1879 | + | } |
|
| 1880 | + | }, |
|
| 1881 | + | "node_modules/mime": { |
|
| 1882 | + | "version": "1.6.0", |
|
| 1883 | + | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", |
|
| 1884 | + | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", |
|
| 1885 | + | "dev": true, |
|
| 1886 | + | "license": "MIT", |
|
| 1887 | + | "bin": { |
|
| 1888 | + | "mime": "cli.js" |
|
| 1889 | + | }, |
|
| 1890 | + | "engines": { |
|
| 1891 | + | "node": ">=4" |
|
| 1892 | + | } |
|
| 1893 | + | }, |
|
| 1894 | + | "node_modules/minimatch": { |
|
| 1895 | + | "version": "10.2.6", |
|
| 1896 | + | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", |
|
| 1897 | + | "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", |
|
| 1898 | + | "dev": true, |
|
| 1899 | + | "license": "BlueOak-1.0.0", |
|
| 1900 | + | "dependencies": { |
|
| 1901 | + | "brace-expansion": "^5.0.8" |
|
| 1902 | + | }, |
|
| 1903 | + | "engines": { |
|
| 1904 | + | "node": "18 || 20 || >=22" |
|
| 1905 | + | }, |
|
| 1906 | + | "funding": { |
|
| 1907 | + | "url": "https://github.com/sponsors/isaacs" |
|
| 1908 | + | } |
|
| 1909 | + | }, |
|
| 1910 | + | "node_modules/ms": { |
|
| 1911 | + | "version": "2.1.3", |
|
| 1912 | + | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", |
|
| 1913 | + | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", |
|
| 1914 | + | "dev": true, |
|
| 1915 | + | "license": "MIT" |
|
| 1916 | + | }, |
|
| 1917 | + | "node_modules/mute-stream": { |
|
| 1918 | + | "version": "0.0.8", |
|
| 1919 | + | "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", |
|
| 1920 | + | "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", |
|
| 1921 | + | "dev": true, |
|
| 1922 | + | "license": "ISC" |
|
| 1923 | + | }, |
|
| 1924 | + | "node_modules/object-inspect": { |
|
| 1925 | + | "version": "1.13.4", |
|
| 1926 | + | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", |
|
| 1927 | + | "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", |
|
| 1928 | + | "dev": true, |
|
| 1929 | + | "license": "MIT", |
|
| 1930 | + | "engines": { |
|
| 1931 | + | "node": ">= 0.4" |
|
| 1932 | + | }, |
|
| 1933 | + | "funding": { |
|
| 1934 | + | "url": "https://github.com/sponsors/ljharb" |
|
| 1935 | + | } |
|
| 1936 | + | }, |
|
| 1937 | + | "node_modules/open": { |
|
| 1938 | + | "version": "10.2.0", |
|
| 1939 | + | "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", |
|
| 1940 | + | "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", |
|
| 1941 | + | "dev": true, |
|
| 1942 | + | "license": "MIT", |
|
| 1943 | + | "dependencies": { |
|
| 1944 | + | "default-browser": "^5.2.1", |
|
| 1945 | + | "define-lazy-prop": "^3.0.0", |
|
| 1946 | + | "is-inside-container": "^1.0.0", |
|
| 1947 | + | "wsl-utils": "^0.1.0" |
|
| 1948 | + | }, |
|
| 1949 | + | "engines": { |
|
| 1950 | + | "node": ">=18" |
|
| 1951 | + | }, |
|
| 1952 | + | "funding": { |
|
| 1953 | + | "url": "https://github.com/sponsors/sindresorhus" |
|
| 1954 | + | } |
|
| 1955 | + | }, |
|
| 1956 | + | "node_modules/parse5": { |
|
| 1957 | + | "version": "8.0.1", |
|
| 1958 | + | "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", |
|
| 1959 | + | "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", |
|
| 1960 | + | "dev": true, |
|
| 1961 | + | "license": "MIT", |
|
| 1962 | + | "dependencies": { |
|
| 1963 | + | "entities": "^8.0.0" |
|
| 1964 | + | }, |
|
| 1965 | + | "funding": { |
|
| 1966 | + | "url": "https://github.com/inikulin/parse5?sponsor=1" |
|
| 1967 | + | } |
|
| 1968 | + | }, |
|
| 1969 | + | "node_modules/pend": { |
|
| 1970 | + | "version": "1.2.0", |
|
| 1971 | + | "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", |
|
| 1972 | + | "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", |
|
| 1973 | + | "dev": true, |
|
| 1974 | + | "license": "MIT" |
|
| 1975 | + | }, |
|
| 1976 | + | "node_modules/picomatch": { |
|
| 1977 | + | "version": "4.0.7", |
|
| 1978 | + | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", |
|
| 1979 | + | "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", |
|
| 1980 | + | "dev": true, |
|
| 1981 | + | "license": "MIT", |
|
| 1982 | + | "engines": { |
|
| 1983 | + | "node": ">=12" |
|
| 1984 | + | }, |
|
| 1985 | + | "funding": { |
|
| 1986 | + | "url": "https://github.com/sponsors/jonschlinkert" |
|
| 1987 | + | } |
|
| 1988 | + | }, |
|
| 1989 | + | "node_modules/proper-lockfile": { |
|
| 1990 | + | "version": "4.1.2", |
|
| 1991 | + | "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", |
|
| 1992 | + | "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", |
|
| 1993 | + | "dev": true, |
|
| 1994 | + | "license": "MIT", |
|
| 1995 | + | "dependencies": { |
|
| 1996 | + | "graceful-fs": "^4.2.4", |
|
| 1997 | + | "retry": "^0.12.0", |
|
| 1998 | + | "signal-exit": "^3.0.2" |
|
| 1999 | + | } |
|
| 2000 | + | }, |
|
| 2001 | + | "node_modules/qs": { |
|
| 2002 | + | "version": "6.16.0", |
|
| 2003 | + | "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz", |
|
| 2004 | + | "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==", |
|
| 2005 | + | "dev": true, |
|
| 2006 | + | "license": "BSD-3-Clause", |
|
| 2007 | + | "dependencies": { |
|
| 2008 | + | "es-define-property": "^1.0.1", |
|
| 2009 | + | "side-channel": "^1.1.1" |
|
| 2010 | + | }, |
|
| 2011 | + | "engines": { |
|
| 2012 | + | "node": ">=0.6" |
|
| 2013 | + | }, |
|
| 2014 | + | "funding": { |
|
| 2015 | + | "url": "https://github.com/sponsors/ljharb" |
|
| 2016 | + | } |
|
| 2017 | + | }, |
|
| 2018 | + | "node_modules/read": { |
|
| 2019 | + | "version": "1.0.7", |
|
| 2020 | + | "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", |
|
| 2021 | + | "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==", |
|
| 2022 | + | "dev": true, |
|
| 2023 | + | "license": "ISC", |
|
| 2024 | + | "dependencies": { |
|
| 2025 | + | "mute-stream": "~0.0.4" |
|
| 2026 | + | }, |
|
| 2027 | + | "engines": { |
|
| 2028 | + | "node": ">=0.8" |
|
| 2029 | + | } |
|
| 2030 | + | }, |
|
| 2031 | + | "node_modules/retry": { |
|
| 2032 | + | "version": "0.12.0", |
|
| 2033 | + | "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", |
|
| 2034 | + | "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", |
|
| 2035 | + | "dev": true, |
|
| 2036 | + | "license": "MIT", |
|
| 2037 | + | "engines": { |
|
| 2038 | + | "node": ">= 4" |
|
| 2039 | + | } |
|
| 2040 | + | }, |
|
| 2041 | + | "node_modules/run-applescript": { |
|
| 2042 | + | "version": "7.1.0", |
|
| 2043 | + | "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", |
|
| 2044 | + | "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", |
|
| 2045 | + | "dev": true, |
|
| 2046 | + | "license": "MIT", |
|
| 2047 | + | "engines": { |
|
| 2048 | + | "node": ">=18" |
|
| 2049 | + | }, |
|
| 2050 | + | "funding": { |
|
| 2051 | + | "url": "https://github.com/sponsors/sindresorhus" |
|
| 2052 | + | } |
|
| 2053 | + | }, |
|
| 2054 | + | "node_modules/safe-buffer": { |
|
| 2055 | + | "version": "5.2.1", |
|
| 2056 | + | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", |
|
| 2057 | + | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", |
|
| 2058 | + | "dev": true, |
|
| 2059 | + | "funding": [ |
|
| 2060 | + | { |
|
| 2061 | + | "type": "github", |
|
| 2062 | + | "url": "https://github.com/sponsors/feross" |
|
| 2063 | + | }, |
|
| 2064 | + | { |
|
| 2065 | + | "type": "patreon", |
|
| 2066 | + | "url": "https://www.patreon.com/feross" |
|
| 2067 | + | }, |
|
| 2068 | + | { |
|
| 2069 | + | "type": "consulting", |
|
| 2070 | + | "url": "https://feross.org/support" |
|
| 2071 | + | } |
|
| 2072 | + | ], |
|
| 2073 | + | "license": "MIT" |
|
| 2074 | + | }, |
|
| 2075 | + | "node_modules/sax": { |
|
| 2076 | + | "version": "1.6.1", |
|
| 2077 | + | "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz", |
|
| 2078 | + | "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==", |
|
| 2079 | + | "dev": true, |
|
| 2080 | + | "license": "BlueOak-1.0.0", |
|
| 2081 | + | "engines": { |
|
| 2082 | + | "node": ">=11.0.0" |
|
| 2083 | + | } |
|
| 2084 | + | }, |
|
| 2085 | + | "node_modules/semver": { |
|
| 2086 | + | "version": "7.8.5", |
|
| 2087 | + | "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", |
|
| 2088 | + | "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", |
|
| 2089 | + | "dev": true, |
|
| 2090 | + | "license": "ISC", |
|
| 2091 | + | "bin": { |
|
| 2092 | + | "semver": "bin/semver.js" |
|
| 2093 | + | }, |
|
| 2094 | + | "engines": { |
|
| 2095 | + | "node": ">=10" |
|
| 2096 | + | } |
|
| 2097 | + | }, |
|
| 2098 | + | "node_modules/side-channel": { |
|
| 2099 | + | "version": "1.1.1", |
|
| 2100 | + | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", |
|
| 2101 | + | "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", |
|
| 2102 | + | "dev": true, |
|
| 2103 | + | "license": "MIT", |
|
| 2104 | + | "dependencies": { |
|
| 2105 | + | "es-errors": "^1.3.0", |
|
| 2106 | + | "object-inspect": "^1.13.4", |
|
| 2107 | + | "side-channel-list": "^1.0.1", |
|
| 2108 | + | "side-channel-map": "^1.0.1", |
|
| 2109 | + | "side-channel-weakmap": "^1.0.2" |
|
| 2110 | + | }, |
|
| 2111 | + | "engines": { |
|
| 2112 | + | "node": ">= 0.4" |
|
| 2113 | + | }, |
|
| 2114 | + | "funding": { |
|
| 2115 | + | "url": "https://github.com/sponsors/ljharb" |
|
| 2116 | + | } |
|
| 2117 | + | }, |
|
| 2118 | + | "node_modules/side-channel-list": { |
|
| 2119 | + | "version": "1.0.1", |
|
| 2120 | + | "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", |
|
| 2121 | + | "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", |
|
| 2122 | + | "dev": true, |
|
| 2123 | + | "license": "MIT", |
|
| 2124 | + | "dependencies": { |
|
| 2125 | + | "es-errors": "^1.3.0", |
|
| 2126 | + | "object-inspect": "^1.13.4" |
|
| 2127 | + | }, |
|
| 2128 | + | "engines": { |
|
| 2129 | + | "node": ">= 0.4" |
|
| 2130 | + | }, |
|
| 2131 | + | "funding": { |
|
| 2132 | + | "url": "https://github.com/sponsors/ljharb" |
|
| 2133 | + | } |
|
| 2134 | + | }, |
|
| 2135 | + | "node_modules/side-channel-map": { |
|
| 2136 | + | "version": "1.0.1", |
|
| 2137 | + | "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", |
|
| 2138 | + | "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", |
|
| 2139 | + | "dev": true, |
|
| 2140 | + | "license": "MIT", |
|
| 2141 | + | "dependencies": { |
|
| 2142 | + | "call-bound": "^1.0.2", |
|
| 2143 | + | "es-errors": "^1.3.0", |
|
| 2144 | + | "get-intrinsic": "^1.2.5", |
|
| 2145 | + | "object-inspect": "^1.13.3" |
|
| 2146 | + | }, |
|
| 2147 | + | "engines": { |
|
| 2148 | + | "node": ">= 0.4" |
|
| 2149 | + | }, |
|
| 2150 | + | "funding": { |
|
| 2151 | + | "url": "https://github.com/sponsors/ljharb" |
|
| 2152 | + | } |
|
| 2153 | + | }, |
|
| 2154 | + | "node_modules/side-channel-weakmap": { |
|
| 2155 | + | "version": "1.0.2", |
|
| 2156 | + | "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", |
|
| 2157 | + | "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", |
|
| 2158 | + | "dev": true, |
|
| 2159 | + | "license": "MIT", |
|
| 2160 | + | "dependencies": { |
|
| 2161 | + | "call-bound": "^1.0.2", |
|
| 2162 | + | "es-errors": "^1.3.0", |
|
| 2163 | + | "get-intrinsic": "^1.2.5", |
|
| 2164 | + | "object-inspect": "^1.13.3", |
|
| 2165 | + | "side-channel-map": "^1.0.1" |
|
| 2166 | + | }, |
|
| 2167 | + | "engines": { |
|
| 2168 | + | "node": ">= 0.4" |
|
| 2169 | + | }, |
|
| 2170 | + | "funding": { |
|
| 2171 | + | "url": "https://github.com/sponsors/ljharb" |
|
| 2172 | + | } |
|
| 2173 | + | }, |
|
| 2174 | + | "node_modules/signal-exit": { |
|
| 2175 | + | "version": "3.0.7", |
|
| 2176 | + | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", |
|
| 2177 | + | "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", |
|
| 2178 | + | "dev": true, |
|
| 2179 | + | "license": "ISC" |
|
| 2180 | + | }, |
|
| 2181 | + | "node_modules/structured-source": { |
|
| 2182 | + | "version": "4.0.0", |
|
| 2183 | + | "resolved": "https://registry.npmjs.org/structured-source/-/structured-source-4.0.0.tgz", |
|
| 2184 | + | "integrity": "sha512-qGzRFNJDjFieQkl/sVOI2dUjHKRyL9dAJi2gCPGJLbJHBIkyOHxjuocpIEfbLioX+qSJpvbYdT49/YCdMznKxA==", |
|
| 2185 | + | "dev": true, |
|
| 2186 | + | "license": "BSD-2-Clause", |
|
| 2187 | + | "dependencies": { |
|
| 2188 | + | "boundary": "^2.0.0" |
|
| 2189 | + | } |
|
| 2190 | + | }, |
|
| 2191 | + | "node_modules/textextensions": { |
|
| 2192 | + | "version": "6.11.0", |
|
| 2193 | + | "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-6.11.0.tgz", |
|
| 2194 | + | "integrity": "sha512-tXJwSr9355kFJI3lbCkPpUH5cP8/M0GGy2xLO34aZCjMXBaK3SoPnZwr/oWmo1FdCnELcs4npdCIOFtq9W3ruQ==", |
|
| 2195 | + | "dev": true, |
|
| 2196 | + | "license": "Artistic-2.0", |
|
| 2197 | + | "dependencies": { |
|
| 2198 | + | "editions": "^6.21.0" |
|
| 2199 | + | }, |
|
| 2200 | + | "engines": { |
|
| 2201 | + | "node": ">=4" |
|
| 2202 | + | }, |
|
| 2203 | + | "funding": { |
|
| 2204 | + | "url": "https://bevry.me/fund" |
|
| 2205 | + | } |
|
| 2206 | + | }, |
|
| 2207 | + | "node_modules/tinyglobby": { |
|
| 2208 | + | "version": "0.2.17", |
|
| 2209 | + | "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", |
|
| 2210 | + | "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", |
|
| 2211 | + | "dev": true, |
|
| 2212 | + | "license": "MIT", |
|
| 2213 | + | "dependencies": { |
|
| 2214 | + | "fdir": "^6.5.0", |
|
| 2215 | + | "picomatch": "^4.0.4" |
|
| 2216 | + | }, |
|
| 2217 | + | "engines": { |
|
| 2218 | + | "node": ">=12.0.0" |
|
| 2219 | + | }, |
|
| 2220 | + | "funding": { |
|
| 2221 | + | "url": "https://github.com/sponsors/SuperchupuDev" |
|
| 2222 | + | } |
|
| 2223 | + | }, |
|
| 2224 | + | "node_modules/tslib": { |
|
| 2225 | + | "version": "2.8.1", |
|
| 2226 | + | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", |
|
| 2227 | + | "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", |
|
| 2228 | + | "dev": true, |
|
| 2229 | + | "license": "0BSD" |
|
| 2230 | + | }, |
|
| 2231 | + | "node_modules/tunnel": { |
|
| 2232 | + | "version": "0.0.6", |
|
| 2233 | + | "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", |
|
| 2234 | + | "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", |
|
| 2235 | + | "dev": true, |
|
| 2236 | + | "license": "MIT", |
|
| 2237 | + | "engines": { |
|
| 2238 | + | "node": ">=0.6.11 <=0.7.0 || >=0.7.3" |
|
| 2239 | + | } |
|
| 2240 | + | }, |
|
| 2241 | + | "node_modules/typed-rest-client": { |
|
| 2242 | + | "version": "1.8.11", |
|
| 2243 | + | "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.11.tgz", |
|
| 2244 | + | "integrity": "sha512-5UvfMpd1oelmUPRbbaVnq+rHP7ng2cE4qoQkQeAqxRL6PklkxsM0g32/HL0yfvruK6ojQ5x8EE+HF4YV6DtuCA==", |
|
| 2245 | + | "dev": true, |
|
| 2246 | + | "license": "MIT", |
|
| 2247 | + | "dependencies": { |
|
| 2248 | + | "qs": "^6.9.1", |
|
| 2249 | + | "tunnel": "0.0.6", |
|
| 2250 | + | "underscore": "^1.12.1" |
|
| 2251 | + | } |
|
| 2252 | + | }, |
|
| 2253 | + | "node_modules/typescript": { |
|
| 2254 | + | "version": "5.9.3", |
|
| 2255 | + | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", |
|
| 2256 | + | "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", |
|
| 2257 | + | "dev": true, |
|
| 2258 | + | "license": "Apache-2.0", |
|
| 2259 | + | "bin": { |
|
| 2260 | + | "tsc": "bin/tsc", |
|
| 2261 | + | "tsserver": "bin/tsserver" |
|
| 2262 | + | }, |
|
| 2263 | + | "engines": { |
|
| 2264 | + | "node": ">=14.17" |
|
| 2265 | + | } |
|
| 2266 | + | }, |
|
| 2267 | + | "node_modules/underscore": { |
|
| 2268 | + | "version": "1.13.8", |
|
| 2269 | + | "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.8.tgz", |
|
| 2270 | + | "integrity": "sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==", |
|
| 2271 | + | "dev": true, |
|
| 2272 | + | "license": "MIT" |
|
| 2273 | + | }, |
|
| 2274 | + | "node_modules/undici-types": { |
|
| 2275 | + | "version": "7.24.6", |
|
| 2276 | + | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", |
|
| 2277 | + | "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", |
|
| 2278 | + | "dev": true, |
|
| 2279 | + | "license": "MIT" |
|
| 2280 | + | }, |
|
| 2281 | + | "node_modules/url-join": { |
|
| 2282 | + | "version": "4.0.1", |
|
| 2283 | + | "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", |
|
| 2284 | + | "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", |
|
| 2285 | + | "dev": true, |
|
| 2286 | + | "license": "MIT" |
|
| 2287 | + | }, |
|
| 2288 | + | "node_modules/version-range": { |
|
| 2289 | + | "version": "4.15.0", |
|
| 2290 | + | "resolved": "https://registry.npmjs.org/version-range/-/version-range-4.15.0.tgz", |
|
| 2291 | + | "integrity": "sha512-Ck0EJbAGxHwprkzFO966t4/5QkRuzh+/I1RxhLgUKKwEn+Cd8NwM60mE3AqBZg5gYODoXW0EFsQvbZjRlvdqbg==", |
|
| 2292 | + | "dev": true, |
|
| 2293 | + | "license": "Artistic-2.0", |
|
| 2294 | + | "engines": { |
|
| 2295 | + | "node": ">=4" |
|
| 2296 | + | }, |
|
| 2297 | + | "funding": { |
|
| 2298 | + | "url": "https://bevry.me/fund" |
|
| 2299 | + | } |
|
| 2300 | + | }, |
|
| 2301 | + | "node_modules/wsl-utils": { |
|
| 2302 | + | "version": "0.1.0", |
|
| 2303 | + | "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", |
|
| 2304 | + | "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", |
|
| 2305 | + | "dev": true, |
|
| 2306 | + | "license": "MIT", |
|
| 2307 | + | "dependencies": { |
|
| 2308 | + | "is-wsl": "^3.1.0" |
|
| 2309 | + | }, |
|
| 2310 | + | "engines": { |
|
| 2311 | + | "node": ">=18" |
|
| 2312 | + | }, |
|
| 2313 | + | "funding": { |
|
| 2314 | + | "url": "https://github.com/sponsors/sindresorhus" |
|
| 2315 | + | } |
|
| 2316 | + | }, |
|
| 2317 | + | "node_modules/xml2js": { |
|
| 2318 | + | "version": "0.5.0", |
|
| 2319 | + | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", |
|
| 2320 | + | "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", |
|
| 2321 | + | "dev": true, |
|
| 2322 | + | "license": "MIT", |
|
| 2323 | + | "dependencies": { |
|
| 2324 | + | "sax": ">=0.6.0", |
|
| 2325 | + | "xmlbuilder": "~11.0.0" |
|
| 2326 | + | }, |
|
| 2327 | + | "engines": { |
|
| 2328 | + | "node": ">=4.0.0" |
|
| 2329 | + | } |
|
| 2330 | + | }, |
|
| 2331 | + | "node_modules/xmlbuilder": { |
|
| 2332 | + | "version": "11.0.1", |
|
| 2333 | + | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", |
|
| 2334 | + | "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", |
|
| 2335 | + | "dev": true, |
|
| 2336 | + | "license": "MIT", |
|
| 2337 | + | "engines": { |
|
| 2338 | + | "node": ">=4.0" |
|
| 2339 | + | } |
|
| 2340 | + | }, |
|
| 2341 | + | "node_modules/yallist": { |
|
| 2342 | + | "version": "4.0.0", |
|
| 2343 | + | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", |
|
| 2344 | + | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", |
|
| 2345 | + | "dev": true, |
|
| 2346 | + | "license": "ISC" |
|
| 2347 | + | }, |
|
| 2348 | + | "node_modules/yauzl": { |
|
| 2349 | + | "version": "3.4.0", |
|
| 2350 | + | "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.4.0.tgz", |
|
| 2351 | + | "integrity": "sha512-jIH9yLR9wqr0wOS0TpBvo/g/2UgZH5qePVbjgRliiF0BYvOZyaBknKsF+x9Iht0O6sqgnB93rCICdOZFecJuDw==", |
|
| 2352 | + | "dev": true, |
|
| 2353 | + | "license": "MIT", |
|
| 2354 | + | "dependencies": { |
|
| 2355 | + | "pend": "~1.2.0" |
|
| 2356 | + | }, |
|
| 2357 | + | "engines": { |
|
| 2358 | + | "node": ">=12" |
|
| 2359 | + | } |
|
| 2360 | + | }, |
|
| 2361 | + | "node_modules/yazl": { |
|
| 2362 | + | "version": "2.5.1", |
|
| 2363 | + | "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", |
|
| 2364 | + | "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", |
|
| 2365 | + | "dev": true, |
|
| 2366 | + | "license": "MIT", |
|
| 2367 | + | "dependencies": { |
|
| 2368 | + | "buffer-crc32": "~0.2.3" |
|
| 2369 | + | } |
|
| 2370 | + | } |
|
| 2371 | + | } |
|
| 2372 | + | } |
added package.json +299 −0
| 1 | + | { |
|
| 2 | + | "name": "spectra-vscode", |
|
| 3 | + | "displayName": "Spectra", |
|
| 4 | + | "description": "Spectra in the editor: set up your AI agents, chat with them, and give them their briefs, without the command line", |
|
| 5 | + | "version": "0.1.0", |
|
| 6 | + | "publisher": "andymccall", |
|
| 7 | + | "license": "MIT OR Apache-2.0", |
|
| 8 | + | "repository": { |
|
| 9 | + | "type": "oxygen", |
|
| 10 | + | "url": "https://oxygenhub.io/andymccall/spectra-vscode" |
|
| 11 | + | }, |
|
| 12 | + | "icon": "assets/spectra.png", |
|
| 13 | + | "engines": { |
|
| 14 | + | "vscode": "^1.138.0" |
|
| 15 | + | }, |
|
| 16 | + | "categories": [ |
|
| 17 | + | "AI", |
|
| 18 | + | "Other" |
|
| 19 | + | ], |
|
| 20 | + | "keywords": [ |
|
| 21 | + | "spectra", |
|
| 22 | + | "agents", |
|
| 23 | + | "mcp", |
|
| 24 | + | "claude", |
|
| 25 | + | "antigravity" |
|
| 26 | + | ], |
|
| 27 | + | "main": "./dist/extension.js", |
|
| 28 | + | "activationEvents": [ |
|
| 29 | + | "onStartupFinished" |
|
| 30 | + | ], |
|
| 31 | + | "contributes": { |
|
| 32 | + | "viewsContainers": { |
|
| 33 | + | "activitybar": [ |
|
| 34 | + | { |
|
| 35 | + | "id": "spectra", |
|
| 36 | + | "title": "Spectra", |
|
| 37 | + | "icon": "assets/spectra-mono.svg" |
|
| 38 | + | } |
|
| 39 | + | ] |
|
| 40 | + | }, |
|
| 41 | + | "views": { |
|
| 42 | + | "spectra": [ |
|
| 43 | + | { |
|
| 44 | + | "id": "spectra.welcome", |
|
| 45 | + | "name": "Get started", |
|
| 46 | + | "when": "!spectra.ready" |
|
| 47 | + | }, |
|
| 48 | + | { |
|
| 49 | + | "id": "spectra.channels", |
|
| 50 | + | "name": "Channels", |
|
| 51 | + | "when": "spectra.ready" |
|
| 52 | + | }, |
|
| 53 | + | { |
|
| 54 | + | "id": "spectra.agents", |
|
| 55 | + | "name": "Agents", |
|
| 56 | + | "when": "spectra.ready" |
|
| 57 | + | }, |
|
| 58 | + | { |
|
| 59 | + | "id": "spectra.inbox", |
|
| 60 | + | "name": "Inbox", |
|
| 61 | + | "when": "spectra.ready" |
|
| 62 | + | }, |
|
| 63 | + | { |
|
| 64 | + | "id": "spectra.briefs", |
|
| 65 | + | "name": "Briefs", |
|
| 66 | + | "when": "spectra.ready" |
|
| 67 | + | } |
|
| 68 | + | ] |
|
| 69 | + | }, |
|
| 70 | + | "viewsWelcome": [ |
|
| 71 | + | { |
|
| 72 | + | "view": "spectra.welcome", |
|
| 73 | + | "contents": "Spectra lets your AI agents find each other, talk, and follow your briefs.\n[Set Up Spectra](command:spectra.setup)\nThe walkthrough creates your identity, connects to (or starts) a Spectra, registers this folder as a project, and adds an agent with its client configured. No command line needed." |
|
| 74 | + | }, |
|
| 75 | + | { |
|
| 76 | + | "view": "spectra.agents", |
|
| 77 | + | "contents": "No agents yet.\n[Add an Agent](command:spectra.addAgent)" |
|
| 78 | + | } |
|
| 79 | + | ], |
|
| 80 | + | "walkthroughs": [ |
|
| 81 | + | { |
|
| 82 | + | "id": "spectra.setup", |
|
| 83 | + | "title": "Get started with Spectra", |
|
| 84 | + | "description": "Everything Spectra needs, step by step: nothing typed in a terminal.", |
|
| 85 | + | "steps": [ |
|
| 86 | + | { |
|
| 87 | + | "id": "binary", |
|
| 88 | + | "title": "Find spectra", |
|
| 89 | + | "description": "The extension drives the `spectra` program.\n[Check for spectra](command:spectra.checkBinary)", |
|
| 90 | + | "media": { |
|
| 91 | + | "markdown": "media/binary.md" |
|
| 92 | + | }, |
|
| 93 | + | "completionEvents": [ |
|
| 94 | + | "onContext:spectra.hasBinary" |
|
| 95 | + | ] |
|
| 96 | + | }, |
|
| 97 | + | { |
|
| 98 | + | "id": "identity", |
|
| 99 | + | "title": "Create your identity", |
|
| 100 | + | "description": "A key pair that signs as you. Agents act for you only when you say so.\n[Create Identity](command:spectra.createIdentity)", |
|
| 101 | + | "media": { |
|
| 102 | + | "markdown": "media/identity.md" |
|
| 103 | + | }, |
|
| 104 | + | "completionEvents": [ |
|
| 105 | + | "onContext:spectra.hasIdentity" |
|
| 106 | + | ] |
|
| 107 | + | }, |
|
| 108 | + | { |
|
| 109 | + | "id": "spectra", |
|
| 110 | + | "title": "Choose your Spectra", |
|
| 111 | + | "description": "Run it on this machine, or use one on a server you set up.\n[On This Machine](command:spectra.setupHere)\n[On a Server](command:spectra.connectServer)", |
|
| 112 | + | "media": { |
|
| 113 | + | "markdown": "media/spectra.md" |
|
| 114 | + | }, |
|
| 115 | + | "completionEvents": [ |
|
| 116 | + | "onContext:spectra.ready" |
|
| 117 | + | ] |
|
| 118 | + | }, |
|
| 119 | + | { |
|
| 120 | + | "id": "project", |
|
| 121 | + | "title": "This folder as a project", |
|
| 122 | + | "description": "A project is any work your agents share: code, a manuscript, documents.\n[Register This Folder](command:spectra.registerFolder)", |
|
| 123 | + | "media": { |
|
| 124 | + | "markdown": "media/project.md" |
|
| 125 | + | }, |
|
| 126 | + | "completionEvents": [ |
|
| 127 | + | "onContext:spectra.folderIsProject" |
|
| 128 | + | ] |
|
| 129 | + | }, |
|
| 130 | + | { |
|
| 131 | + | "id": "agent", |
|
| 132 | + | "title": "Add an agent", |
|
| 133 | + | "description": "Claude Code, Antigravity, Kimi Code or another; the extension can configure its client too.\n[Add an Agent](command:spectra.addAgent)", |
|
| 134 | + | "media": { |
|
| 135 | + | "markdown": "media/agent.md" |
|
| 136 | + | }, |
|
| 137 | + | "completionEvents": [ |
|
| 138 | + | "onContext:spectra.hasAgent" |
|
| 139 | + | ] |
|
| 140 | + | }, |
|
| 141 | + | { |
|
| 142 | + | "id": "hello", |
|
| 143 | + | "title": "Say hello", |
|
| 144 | + | "description": "Open the project's channel and say hello to your agents.\n[Open the Channel](command:spectra.openProjectChannel)", |
|
| 145 | + | "media": { |
|
| 146 | + | "markdown": "media/hello.md" |
|
| 147 | + | }, |
|
| 148 | + | "completionEvents": [ |
|
| 149 | + | "onCommand:spectra.openProjectChannel" |
|
| 150 | + | ] |
|
| 151 | + | } |
|
| 152 | + | ] |
|
| 153 | + | } |
|
| 154 | + | ], |
|
| 155 | + | "commands": [ |
|
| 156 | + | { |
|
| 157 | + | "command": "spectra.setup", |
|
| 158 | + | "title": "Set Up", |
|
| 159 | + | "category": "Spectra" |
|
| 160 | + | }, |
|
| 161 | + | { |
|
| 162 | + | "command": "spectra.checkBinary", |
|
| 163 | + | "title": "Check for spectra", |
|
| 164 | + | "category": "Spectra" |
|
| 165 | + | }, |
|
| 166 | + | { |
|
| 167 | + | "command": "spectra.createIdentity", |
|
| 168 | + | "title": "Create Identity", |
|
| 169 | + | "category": "Spectra" |
|
| 170 | + | }, |
|
| 171 | + | { |
|
| 172 | + | "command": "spectra.setupHere", |
|
| 173 | + | "title": "Run Spectra on This Machine", |
|
| 174 | + | "category": "Spectra" |
|
| 175 | + | }, |
|
| 176 | + | { |
|
| 177 | + | "command": "spectra.connectServer", |
|
| 178 | + | "title": "Connect to a Server", |
|
| 179 | + | "category": "Spectra" |
|
| 180 | + | }, |
|
| 181 | + | { |
|
| 182 | + | "command": "spectra.registerFolder", |
|
| 183 | + | "title": "Register Folder as Project", |
|
| 184 | + | "category": "Spectra" |
|
| 185 | + | }, |
|
| 186 | + | { |
|
| 187 | + | "command": "spectra.linkProject", |
|
| 188 | + | "title": "Link Project", |
|
| 189 | + | "category": "Spectra" |
|
| 190 | + | }, |
|
| 191 | + | { |
|
| 192 | + | "command": "spectra.addAgent", |
|
| 193 | + | "title": "Add Agent", |
|
| 194 | + | "category": "Spectra", |
|
| 195 | + | "icon": "$(add)" |
|
| 196 | + | }, |
|
| 197 | + | { |
|
| 198 | + | "command": "spectra.renewAgent", |
|
| 199 | + | "title": "Renew Agent", |
|
| 200 | + | "category": "Spectra" |
|
| 201 | + | }, |
|
| 202 | + | { |
|
| 203 | + | "command": "spectra.openChannel", |
|
| 204 | + | "title": "Open Channel", |
|
| 205 | + | "category": "Spectra" |
|
| 206 | + | }, |
|
| 207 | + | { |
|
| 208 | + | "command": "spectra.openProjectChannel", |
|
| 209 | + | "title": "Open This Project's Channel", |
|
| 210 | + | "category": "Spectra" |
|
| 211 | + | }, |
|
| 212 | + | { |
|
| 213 | + | "command": "spectra.say", |
|
| 214 | + | "title": "Say", |
|
| 215 | + | "category": "Spectra" |
|
| 216 | + | }, |
|
| 217 | + | { |
|
| 218 | + | "command": "spectra.editOverallBrief", |
|
| 219 | + | "title": "Edit Overall Brief", |
|
| 220 | + | "category": "Spectra" |
|
| 221 | + | }, |
|
| 222 | + | { |
|
| 223 | + | "command": "spectra.editProjectBrief", |
|
| 224 | + | "title": "Edit Project Brief", |
|
| 225 | + | "category": "Spectra" |
|
| 226 | + | }, |
|
| 227 | + | { |
|
| 228 | + | "command": "spectra.openRadar", |
|
| 229 | + | "title": "Open Radar", |
|
| 230 | + | "category": "Spectra" |
|
| 231 | + | }, |
|
| 232 | + | { |
|
| 233 | + | "command": "spectra.openConstellation", |
|
| 234 | + | "title": "Open Constellation", |
|
| 235 | + | "category": "Spectra" |
|
| 236 | + | }, |
|
| 237 | + | { |
|
| 238 | + | "command": "spectra.refresh", |
|
| 239 | + | "title": "Refresh", |
|
| 240 | + | "category": "Spectra", |
|
| 241 | + | "icon": "$(refresh)" |
|
| 242 | + | } |
|
| 243 | + | ], |
|
| 244 | + | "menus": { |
|
| 245 | + | "view/title": [ |
|
| 246 | + | { |
|
| 247 | + | "command": "spectra.addAgent", |
|
| 248 | + | "when": "view == spectra.agents", |
|
| 249 | + | "group": "navigation" |
|
| 250 | + | }, |
|
| 251 | + | { |
|
| 252 | + | "command": "spectra.refresh", |
|
| 253 | + | "when": "view =~ /^spectra\\./", |
|
| 254 | + | "group": "navigation" |
|
| 255 | + | } |
|
| 256 | + | ], |
|
| 257 | + | "view/item/context": [ |
|
| 258 | + | { |
|
| 259 | + | "command": "spectra.renewAgent", |
|
| 260 | + | "when": "view == spectra.agents && viewItem == agent" |
|
| 261 | + | } |
|
| 262 | + | ] |
|
| 263 | + | }, |
|
| 264 | + | "configuration": { |
|
| 265 | + | "title": "Spectra", |
|
| 266 | + | "properties": { |
|
| 267 | + | "spectra.path": { |
|
| 268 | + | "type": "string", |
|
| 269 | + | "default": "", |
|
| 270 | + | "description": "The spectra program (empty: spectra on PATH, else ~/.local/bin/spectra)." |
|
| 271 | + | }, |
|
| 272 | + | "spectra.agentsHome": { |
|
| 273 | + | "type": "string", |
|
| 274 | + | "default": "~/.spectra-agents", |
|
| 275 | + | "description": "Where new agents' identity homes are made." |
|
| 276 | + | } |
|
| 277 | + | } |
|
| 278 | + | } |
|
| 279 | + | }, |
|
| 280 | + | "scripts": { |
|
| 281 | + | "build": "esbuild src/extension.ts --bundle --platform=node --format=cjs --external:vscode --outfile=dist/extension.js --sourcemap", |
|
| 282 | + | "watch": "npm run build -- --watch", |
|
| 283 | + | "typecheck": "tsc --noEmit -p .", |
|
| 284 | + | "types": "cp ../spectra/crates/spectra-api/ts/index.d.ts src/spectra-api.d.ts", |
|
| 285 | + | "pretest": "esbuild src/pure.ts --bundle --platform=node --format=esm --sourcemap --outfile=out/pure.mjs", |
|
| 286 | + | "test": "node --enable-source-maps --test \"test/**/*.test.mjs\"", |
|
| 287 | + | "check": "npm run typecheck && npm run build && npm test", |
|
| 288 | + | "package": "vsce package --no-dependencies", |
|
| 289 | + | "smoke": "esbuild src/bridge.ts --bundle --platform=node --format=esm --alias:vscode=./test/vscode-stub.mjs --outfile=out/smoke-bridge.mjs && esbuild src/model.ts --bundle --platform=node --format=esm --alias:vscode=./test/vscode-stub.mjs --outfile=out/smoke-model.mjs && node test/smoke.mjs", |
|
| 290 | + | "vscode:prepublish": "npm run build" |
|
| 291 | + | }, |
|
| 292 | + | "devDependencies": { |
|
| 293 | + | "@types/node": "^24.0.0", |
|
| 294 | + | "@types/vscode": "^1.138.0", |
|
| 295 | + | "@vscode/vsce": "^4.0.0", |
|
| 296 | + | "esbuild": "^0.28.0", |
|
| 297 | + | "typescript": "^5.9.0" |
|
| 298 | + | } |
|
| 299 | + | } |
added src/bridge.ts +127 −0
| 1 | + | // The extension's way to Spectra (spec §3): `spectra bridge` signs every |
|
| 2 | + | // request as the owner, so no key is ever handled here. |
|
| 3 | + | import * as cp from "node:child_process"; |
|
| 4 | + | import * as vscode from "vscode"; |
|
| 5 | + | import { binary } from "./cli"; |
|
| 6 | + | import { SseParser } from "./pure"; |
|
| 7 | + | ||
| 8 | + | export class SpectraError extends Error { |
|
| 9 | + | constructor(public status: number, message: string) { |
|
| 10 | + | super(message); |
|
| 11 | + | } |
|
| 12 | + | } |
|
| 13 | + | ||
| 14 | + | export class Bridge implements vscode.Disposable { |
|
| 15 | + | private child: cp.ChildProcess | undefined; |
|
| 16 | + | private ready: Promise<{ url: string; token: string }> | undefined; |
|
| 17 | + | private disposed = false; |
|
| 18 | + | private streams = new Set<AbortController>(); |
|
| 19 | + | private readonly log: vscode.OutputChannel; |
|
| 20 | + | ||
| 21 | + | constructor(log: vscode.OutputChannel) { |
|
| 22 | + | this.log = log; |
|
| 23 | + | } |
|
| 24 | + | ||
| 25 | + | /** Start the bridge if it is not running. */ |
|
| 26 | + | private start(): Promise<{ url: string; token: string }> { |
|
| 27 | + | if (this.ready) return this.ready; |
|
| 28 | + | this.ready = new Promise((resolve, reject) => { |
|
| 29 | + | const bin = binary(); |
|
| 30 | + | if (!bin) return reject(new Error("spectra is not installed")); |
|
| 31 | + | const child = cp.spawn(bin, ["bridge"], { stdio: ["pipe", "pipe", "pipe"], env: { ...process.env, NO_COLOR: "1" } }); |
|
| 32 | + | this.child = child; |
|
| 33 | + | let out = ""; |
|
| 34 | + | let err = ""; |
|
| 35 | + | child.stdout!.on("data", (d: Buffer) => { |
|
| 36 | + | out += d.toString(); |
|
| 37 | + | const nl = out.indexOf("\n"); |
|
| 38 | + | if (nl >= 0) { |
|
| 39 | + | try { |
|
| 40 | + | resolve(JSON.parse(out.slice(0, nl))); |
|
| 41 | + | } catch { |
|
| 42 | + | reject(new Error("the bridge said something unexpected: " + out.slice(0, nl))); |
|
| 43 | + | } |
|
| 44 | + | } |
|
| 45 | + | }); |
|
| 46 | + | child.stderr!.on("data", (d: Buffer) => { |
|
| 47 | + | err += d.toString(); |
|
| 48 | + | this.log.append(d.toString()); |
|
| 49 | + | }); |
|
| 50 | + | child.on("exit", (code) => { |
|
| 51 | + | this.log.appendLine(`spectra bridge exited (${code})`); |
|
| 52 | + | reject(new Error(err.trim().split("\n").pop()?.replace(/^error:\s*/, "") || `spectra bridge exited (${code})`)); |
|
| 53 | + | this.child = undefined; |
|
| 54 | + | this.ready = undefined; |
|
| 55 | + | }); |
|
| 56 | + | }); |
|
| 57 | + | return this.ready; |
|
| 58 | + | } |
|
| 59 | + | ||
| 60 | + | /** Stop, so the next request starts afresh (after setup changes what it reaches). */ |
|
| 61 | + | restart(): void { |
|
| 62 | + | for (const s of this.streams) s.abort(); |
|
| 63 | + | this.streams.clear(); |
|
| 64 | + | this.child?.stdin?.end(); |
|
| 65 | + | this.child = undefined; |
|
| 66 | + | this.ready = undefined; |
|
| 67 | + | } |
|
| 68 | + | ||
| 69 | + | async request<T>(method: string, path: string, body?: unknown): Promise<T> { |
|
| 70 | + | const { url, token } = await this.start(); |
|
| 71 | + | const r = await fetch(url + path, { |
|
| 72 | + | method, |
|
| 73 | + | headers: { authorization: `Bearer ${token}`, ...(body !== undefined ? { "content-type": "application/json" } : {}) }, |
|
| 74 | + | body: body !== undefined ? JSON.stringify(body) : undefined, |
|
| 75 | + | }); |
|
| 76 | + | const text = await r.text(); |
|
| 77 | + | if (!r.ok) { |
|
| 78 | + | let why = text; |
|
| 79 | + | try { |
|
| 80 | + | const p = JSON.parse(text); |
|
| 81 | + | why = p.detail || p.title || text; |
|
| 82 | + | } catch { |
|
| 83 | + | /* not JSON */ |
|
| 84 | + | } |
|
| 85 | + | throw new SpectraError(r.status, why); |
|
| 86 | + | } |
|
| 87 | + | return (text ? JSON.parse(text) : undefined) as T; |
|
| 88 | + | } |
|
| 89 | + | ||
| 90 | + | /** Follow an SSE path; each event's data goes to `onData`. Reconnects until disposed. */ |
|
| 91 | + | follow(path: string, onData: (data: string) => void, onUp: () => void): vscode.Disposable { |
|
| 92 | + | const ctrl = new AbortController(); |
|
| 93 | + | this.streams.add(ctrl); |
|
| 94 | + | const loop = async () => { |
|
| 95 | + | let wait = 1000; |
|
| 96 | + | while (!ctrl.signal.aborted && !this.disposed) { |
|
| 97 | + | try { |
|
| 98 | + | const { url, token } = await this.start(); |
|
| 99 | + | const r = await fetch(url + path, { headers: { authorization: `Bearer ${token}` }, signal: ctrl.signal }); |
|
| 100 | + | if (!r.ok || !r.body) throw new Error(`the stream answered ${r.status}`); |
|
| 101 | + | onUp(); |
|
| 102 | + | wait = 1000; |
|
| 103 | + | const parser = new SseParser(); |
|
| 104 | + | const dec = new TextDecoder(); |
|
| 105 | + | for await (const chunk of r.body as unknown as AsyncIterable<Uint8Array>) { |
|
| 106 | + | for (const d of parser.push(dec.decode(chunk, { stream: true }))) onData(d); |
|
| 107 | + | } |
|
| 108 | + | } catch (e) { |
|
| 109 | + | if (ctrl.signal.aborted) return; |
|
| 110 | + | this.log.appendLine(`stream: ${(e as Error).message}; again in ${wait / 1000} s`); |
|
| 111 | + | } |
|
| 112 | + | await new Promise((r) => setTimeout(r, wait)); |
|
| 113 | + | wait = Math.min(wait * 2, 30000); |
|
| 114 | + | } |
|
| 115 | + | }; |
|
| 116 | + | void loop(); |
|
| 117 | + | return new vscode.Disposable(() => { |
|
| 118 | + | ctrl.abort(); |
|
| 119 | + | this.streams.delete(ctrl); |
|
| 120 | + | }); |
|
| 121 | + | } |
|
| 122 | + | ||
| 123 | + | dispose(): void { |
|
| 124 | + | this.disposed = true; |
|
| 125 | + | this.restart(); |
|
| 126 | + | } |
|
| 127 | + | } |
added src/briefs.ts +84 −0
| 1 | + | // Briefs as documents (spec §5.2): `spectra-brief:/overall.md` and |
|
| 2 | + | // `spectra-brief:/project/NAME.md`. Opening one reads it from Spectra; |
|
| 3 | + | // saving it records a signed brief.set, so agents are told. |
|
| 4 | + | import * as vscode from "vscode"; |
|
| 5 | + | import type { Briefs } from "./spectra-api"; |
|
| 6 | + | import { Bridge } from "./bridge"; |
|
| 7 | + | ||
| 8 | + | export const SCHEME = "spectra-brief"; |
|
| 9 | + | ||
| 10 | + | export function uriFor(project?: string): vscode.Uri { |
|
| 11 | + | return vscode.Uri.from({ scheme: SCHEME, path: project ? `/project/${project}.md` : "/overall.md" }); |
|
| 12 | + | } |
|
| 13 | + | ||
| 14 | + | function projectOf(uri: vscode.Uri): string | undefined { |
|
| 15 | + | const m = /^\/project\/([a-z0-9][a-z0-9-]*)\.md$/.exec(uri.path); |
|
| 16 | + | if (m) return m[1]; |
|
| 17 | + | if (uri.path === "/overall.md") return undefined; |
|
| 18 | + | throw vscode.FileSystemError.FileNotFound(uri); |
|
| 19 | + | } |
|
| 20 | + | ||
| 21 | + | export class BriefFs implements vscode.FileSystemProvider { |
|
| 22 | + | private readonly emitter = new vscode.EventEmitter<vscode.FileChangeEvent[]>(); |
|
| 23 | + | readonly onDidChangeFile = this.emitter.event; |
|
| 24 | + | private readonly times = new Map<string, number>(); |
|
| 25 | + | ||
| 26 | + | constructor(private readonly bridge: Bridge) {} |
|
| 27 | + | ||
| 28 | + | watch(): vscode.Disposable { |
|
| 29 | + | return new vscode.Disposable(() => undefined); |
|
| 30 | + | } |
|
| 31 | + | ||
| 32 | + | /** Tell open editors that briefs changed in Spectra. */ |
|
| 33 | + | changed(): void { |
|
| 34 | + | const now = Date.now(); |
|
| 35 | + | const events: vscode.FileChangeEvent[] = []; |
|
| 36 | + | for (const k of this.times.keys()) { |
|
| 37 | + | this.times.set(k, now); |
|
| 38 | + | events.push({ type: vscode.FileChangeType.Changed, uri: vscode.Uri.parse(k) }); |
|
| 39 | + | } |
|
| 40 | + | if (events.length) this.emitter.fire(events); |
|
| 41 | + | } |
|
| 42 | + | ||
| 43 | + | private async text(uri: vscode.Uri): Promise<string> { |
|
| 44 | + | const project = projectOf(uri); |
|
| 45 | + | const b = await this.bridge.request<Briefs>("GET", "/api/v1/briefs"); |
|
| 46 | + | return (project ? b.projects.find((p) => p.project === project)?.text : b.overall) ?? ""; |
|
| 47 | + | } |
|
| 48 | + | ||
| 49 | + | async stat(uri: vscode.Uri): Promise<vscode.FileStat> { |
|
| 50 | + | projectOf(uri); |
|
| 51 | + | const key = uri.toString(); |
|
| 52 | + | if (!this.times.has(key)) this.times.set(key, Date.now()); |
|
| 53 | + | const size = Buffer.byteLength(await this.text(uri).catch(() => "")); |
|
| 54 | + | return { type: vscode.FileType.File, ctime: 0, mtime: this.times.get(key)!, size }; |
|
| 55 | + | } |
|
| 56 | + | ||
| 57 | + | async readFile(uri: vscode.Uri): Promise<Uint8Array> { |
|
| 58 | + | return Buffer.from(await this.text(uri)); |
|
| 59 | + | } |
|
| 60 | + | ||
| 61 | + | async writeFile(uri: vscode.Uri, content: Uint8Array): Promise<void> { |
|
| 62 | + | const project = projectOf(uri); |
|
| 63 | + | try { |
|
| 64 | + | await this.bridge.request("PUT", "/api/v1/briefs", { project: project ?? null, text: Buffer.from(content).toString("utf8") }); |
|
| 65 | + | } catch (e) { |
|
| 66 | + | throw vscode.FileSystemError.NoPermissions(`Spectra did not take the brief: ${(e as Error).message}`); |
|
| 67 | + | } |
|
| 68 | + | this.times.set(uri.toString(), Date.now()); |
|
| 69 | + | void vscode.window.setStatusBarMessage(`$(check) ${project ? `${project}'s brief` : "Overall brief"} signed into Spectra; agents are told on their next call`, 5000); |
|
| 70 | + | } |
|
| 71 | + | ||
| 72 | + | readDirectory(): [string, vscode.FileType][] { |
|
| 73 | + | return []; |
|
| 74 | + | } |
|
| 75 | + | createDirectory(): void { |
|
| 76 | + | throw vscode.FileSystemError.NoPermissions("briefs are not folders"); |
|
| 77 | + | } |
|
| 78 | + | delete(): void { |
|
| 79 | + | throw vscode.FileSystemError.NoPermissions("clear a brief by saving it empty"); |
|
| 80 | + | } |
|
| 81 | + | rename(): void { |
|
| 82 | + | throw vscode.FileSystemError.NoPermissions("briefs cannot be renamed"); |
|
| 83 | + | } |
|
| 84 | + | } |
added src/chat.ts +155 −0
| 1 | + | // A channel as an editor tab (spec §5.3): its messages, newest at the |
|
| 2 | + | // bottom, threads opened in place, and a compose box that posts as you. |
|
| 3 | + | import * as vscode from "vscode"; |
|
| 4 | + | import type { Message, Messages, Posted } from "./spectra-api"; |
|
| 5 | + | import { Bridge } from "./bridge"; |
|
| 6 | + | import { Model } from "./model"; |
|
| 7 | + | import { esc, when } from "./pure"; |
|
| 8 | + | ||
| 9 | + | const panels = new Map<string, ChannelPanel>(); |
|
| 10 | + | ||
| 11 | + | export function openChannel(ctx: vscode.ExtensionContext, bridge: Bridge, model: Model, name: string): void { |
|
| 12 | + | const existing = panels.get(name); |
|
| 13 | + | if (existing) { |
|
| 14 | + | existing.panel.reveal(); |
|
| 15 | + | return; |
|
| 16 | + | } |
|
| 17 | + | panels.set(name, new ChannelPanel(ctx, bridge, model, name)); |
|
| 18 | + | } |
|
| 19 | + | ||
| 20 | + | class ChannelPanel { |
|
| 21 | + | readonly panel: vscode.WebviewPanel; |
|
| 22 | + | private subs: vscode.Disposable[] = []; |
|
| 23 | + | private openThread: string | undefined; |
|
| 24 | + | ||
| 25 | + | constructor(ctx: vscode.ExtensionContext, private readonly bridge: Bridge, private readonly model: Model, private readonly name: string) { |
|
| 26 | + | const title = name.startsWith("dm:") ? `@${name.slice(3)}` : `#${name}`; |
|
| 27 | + | this.panel = vscode.window.createWebviewPanel("spectra.channel", title, vscode.ViewColumn.Active, { enableScripts: true, retainContextWhenHidden: true }); |
|
| 28 | + | this.panel.iconPath = vscode.Uri.joinPath(ctx.extensionUri, "assets", "spectra.svg"); |
|
| 29 | + | this.panel.webview.html = page(this.panel.webview, title); |
|
| 30 | + | this.subs.push( |
|
| 31 | + | this.panel.webview.onDidReceiveMessage((m) => void this.onMessage(m)), |
|
| 32 | + | model.onEvent((e) => { |
|
| 33 | + | if (e.kind === "message.posted" || e.kind === "message.redacted") void this.load(); |
|
| 34 | + | }), |
|
| 35 | + | this.panel.onDidDispose(() => { |
|
| 36 | + | panels.delete(name); |
|
| 37 | + | this.subs.forEach((s) => s.dispose()); |
|
| 38 | + | }), |
|
| 39 | + | ); |
|
| 40 | + | void this.load(); |
|
| 41 | + | } |
|
| 42 | + | ||
| 43 | + | private async load(): Promise<void> { |
|
| 44 | + | try { |
|
| 45 | + | const m = await this.bridge.request<Messages>("GET", `/api/v1/channels/${encodeURIComponent(this.name)}/messages?limit=200`); |
|
| 46 | + | const thread = this.openThread |
|
| 47 | + | ? await this.bridge.request<Messages>("GET", `/api/v1/threads/${encodeURIComponent(this.openThread)}`).catch(() => undefined) |
|
| 48 | + | : undefined; |
|
| 49 | + | const brief = this.model.briefs.projects.find((b) => b.project === this.name)?.text; |
|
| 50 | + | const purpose = this.model.channels.find((c) => c.name === this.name)?.purpose ?? ""; |
|
| 51 | + | void this.panel.webview.postMessage({ type: "render", html: render(m.items, thread?.items, this.openThread), brief: brief ?? "", purpose }); |
|
| 52 | + | } catch (e) { |
|
| 53 | + | void this.panel.webview.postMessage({ type: "error", text: (e as Error).message }); |
|
| 54 | + | } |
|
| 55 | + | } |
|
| 56 | + | ||
| 57 | + | private async onMessage(m: { type: string; body?: string; thread?: string }): Promise<void> { |
|
| 58 | + | if (m.type === "post" && m.body?.trim()) { |
|
| 59 | + | try { |
|
| 60 | + | const body = { body: m.body, reply_to: m.thread ?? null }; |
|
| 61 | + | const p = await this.bridge.request<Posted>("POST", `/api/v1/channels/${encodeURIComponent(this.name)}/messages`, body); |
|
| 62 | + | if (p.duplicate) void vscode.window.showInformationMessage("You said exactly that a moment ago."); |
|
| 63 | + | void this.panel.webview.postMessage({ type: "posted" }); |
|
| 64 | + | await this.load(); |
|
| 65 | + | } catch (e) { |
|
| 66 | + | void vscode.window.showErrorMessage(`Spectra: not posted: ${(e as Error).message}`); |
|
| 67 | + | void this.panel.webview.postMessage({ type: "failed" }); |
|
| 68 | + | } |
|
| 69 | + | } else if (m.type === "thread") { |
|
| 70 | + | this.openThread = m.thread || undefined; |
|
| 71 | + | await this.load(); |
|
| 72 | + | } else if (m.type === "brief") { |
|
| 73 | + | void vscode.commands.executeCommand("spectra.editProjectBrief", this.name); |
|
| 74 | + | } |
|
| 75 | + | } |
|
| 76 | + | } |
|
| 77 | + | ||
| 78 | + | /** Markdown enough for chat: code, bold, italics, links, line breaks. Escaped first. */ |
|
| 79 | + | function md(s: string): string { |
|
| 80 | + | let h = esc(s); |
|
| 81 | + | h = h.replace(/```([\s\S]*?)```/g, (_m, c) => `<pre>${c.replace(/^\n/, "")}</pre>`); |
|
| 82 | + | h = h.replace(/`([^`\n]+)`/g, "<code>$1</code>"); |
|
| 83 | + | h = h.replace(/\*\*([^*\n]+)\*\*/g, "<strong>$1</strong>"); |
|
| 84 | + | h = h.replace(/(^|[\s(])\*([^*\n]+)\*/g, "$1<em>$2</em>"); |
|
| 85 | + | h = h.replace(/(^|\s)(@[a-z0-9][a-z0-9-]*)/g, '$1<span class="at">$2</span>'); |
|
| 86 | + | return h.replace(/\n/g, "<br>"); |
|
| 87 | + | } |
|
| 88 | + | ||
| 89 | + | function one(m: Message, inThread: boolean): string { |
|
| 90 | + | const who = m.author === "human" ? "you" : m.author_display || m.author; |
|
| 91 | + | const replies = !inThread ? `<a class="replies" data-thread="${esc(m.id)}">${m.replies ? `${m.replies} repl${m.replies === 1 ? "y" : "ies"}` : "reply"}</a>` : ""; |
|
| 92 | + | const body = m.redacted ? `<div class="redacted">${esc(m.body)}</div>` : `<div class="body">${md(m.body)}</div>`; |
|
| 93 | + | const kind = m.kind && m.kind !== "chat" ? ` <span class="pill">${esc(m.kind)}</span>` : ""; |
|
| 94 | + | return `<div class="msg${m.author === "human" ? " mine" : ""}"><div class="meta"><strong>${esc(who)}</strong> · ${esc(when(m.at_ms))}${kind} ${replies}</div>${body}</div>`; |
|
| 95 | + | } |
|
| 96 | + | ||
| 97 | + | function render(items: Message[], thread: Message[] | undefined, open: string | undefined): string { |
|
| 98 | + | if (!items.length && !thread) return `<p class="empty">No messages yet. Say hello below.</p>`; |
|
| 99 | + | const list = items.map((m) => one(m, false)).join(""); |
|
| 100 | + | if (!thread || !open) return list; |
|
| 101 | + | return `${list}<div class="thread"><div class="thead">Thread <a class="close" data-thread="">close</a></div>${thread.map((m) => one(m, true)).join("")}</div>`; |
|
| 102 | + | } |
|
| 103 | + | ||
| 104 | + | function page(webview: vscode.Webview, title: string): string { |
|
| 105 | + | const nonce = Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2); |
|
| 106 | + | return `<!doctype html><html><head><meta charset="utf-8"> |
|
| 107 | + | <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src ${webview.cspSource} 'nonce-${nonce}'; script-src 'nonce-${nonce}';"> |
|
| 108 | + | <style nonce="${nonce}"> |
|
| 109 | + | body { font: var(--vscode-font-size) var(--vscode-font-family); color: var(--vscode-foreground); margin: 0; display: flex; flex-direction: column; height: 100vh; } |
|
| 110 | + | header { padding: 10px 16px; border-bottom: 1px solid var(--vscode-panel-border); } |
|
| 111 | + | header h1 { font-size: 15px; margin: 0; } header .purpose { color: var(--vscode-descriptionForeground); } |
|
| 112 | + | details.brief { margin-top: 6px; } details.brief summary { cursor: pointer; color: var(--vscode-textLink-foreground); } details.brief pre { white-space: pre-wrap; background: var(--vscode-textBlockQuote-background); padding: 8px; margin: 6px 0; } |
|
| 113 | + | #log { flex: 1; overflow-y: auto; padding: 8px 16px; } |
|
| 114 | + | .msg { padding: 6px 0; border-bottom: 1px solid var(--vscode-panel-border); } |
|
| 115 | + | .msg.mine .meta strong { color: var(--vscode-textLink-foreground); } |
|
| 116 | + | .meta { color: var(--vscode-descriptionForeground); font-size: 12px; } |
|
| 117 | + | .body { margin-top: 2px; line-height: 1.45; } .body pre { background: var(--vscode-textCodeBlock-background); padding: 6px 8px; overflow-x: auto; } |
|
| 118 | + | .at { color: var(--vscode-textLink-foreground); } |
|
| 119 | + | .redacted { color: var(--vscode-disabledForeground); font-style: italic; } |
|
| 120 | + | .pill { border: 1px solid var(--vscode-panel-border); border-radius: 8px; padding: 0 6px; font-size: 11px; } |
|
| 121 | + | a { color: var(--vscode-textLink-foreground); cursor: pointer; } |
|
| 122 | + | .thread { margin: 10px 0 4px 16px; padding-left: 10px; border-left: 2px solid var(--vscode-textLink-foreground); } |
|
| 123 | + | .thead { font-weight: 600; margin-bottom: 4px; } |
|
| 124 | + | .empty, .error { color: var(--vscode-descriptionForeground); padding: 12px 0; } .error { color: var(--vscode-errorForeground); } |
|
| 125 | + | form { display: flex; gap: 8px; padding: 10px 16px; border-top: 1px solid var(--vscode-panel-border); } |
|
| 126 | + | textarea { flex: 1; min-height: 44px; max-height: 200px; resize: vertical; font: inherit; color: var(--vscode-input-foreground); background: var(--vscode-input-background); border: 1px solid var(--vscode-input-border, transparent); padding: 6px 8px; } |
|
| 127 | + | button { background: var(--vscode-button-background); color: var(--vscode-button-foreground); border: 0; padding: 6px 14px; cursor: pointer; align-self: flex-end; } |
|
| 128 | + | button:disabled { opacity: .6; } |
|
| 129 | + | </style></head><body> |
|
| 130 | + | <header><h1>${esc(title)}</h1><div class="purpose" id="purpose"></div><details class="brief" id="brief" hidden><summary>Project brief · <a id="edit">edit</a></summary><pre id="brieftext"></pre></details></header> |
|
| 131 | + | <div id="log"><p class="empty">Loading…</p></div> |
|
| 132 | + | <form id="f"><textarea id="t" placeholder="Write as yourself. @name mentions an agent. Ctrl+Enter sends."></textarea><button id="b" type="submit">Send</button></form> |
|
| 133 | + | <script nonce="${nonce}"> |
|
| 134 | + | const vscode = acquireVsCodeApi(); |
|
| 135 | + | const log = document.getElementById("log"), t = document.getElementById("t"), b = document.getElementById("b"); |
|
| 136 | + | let thread = ""; |
|
| 137 | + | function send(e) { if (e) e.preventDefault(); const body = t.value.trim(); if (!body) return; b.disabled = true; vscode.postMessage({ type: "post", body, thread: thread || undefined }); } |
|
| 138 | + | document.getElementById("f").addEventListener("submit", send); |
|
| 139 | + | t.addEventListener("keydown", (e) => { if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) send(e); }); |
|
| 140 | + | document.getElementById("edit").addEventListener("click", (e) => { e.preventDefault(); vscode.postMessage({ type: "brief" }); }); |
|
| 141 | + | log.addEventListener("click", (e) => { const a = e.target.closest("[data-thread]"); if (!a) return; thread = a.getAttribute("data-thread"); t.placeholder = thread ? "Reply in the thread. Ctrl+Enter sends." : "Write as yourself. @name mentions an agent. Ctrl+Enter sends."; vscode.postMessage({ type: "thread", thread }); }); |
|
| 142 | + | window.addEventListener("message", (ev) => { |
|
| 143 | + | const m = ev.data; |
|
| 144 | + | if (m.type === "render") { |
|
| 145 | + | const atEnd = log.scrollTop + log.clientHeight >= log.scrollHeight - 30; |
|
| 146 | + | log.innerHTML = m.html; |
|
| 147 | + | document.getElementById("purpose").textContent = m.purpose || ""; |
|
| 148 | + | const br = document.getElementById("brief"); br.hidden = !m.brief; document.getElementById("brieftext").textContent = m.brief || ""; |
|
| 149 | + | if (atEnd) log.scrollTop = log.scrollHeight; |
|
| 150 | + | } else if (m.type === "posted") { t.value = ""; b.disabled = false; t.focus(); } |
|
| 151 | + | else if (m.type === "failed") { b.disabled = false; } |
|
| 152 | + | else if (m.type === "error") { log.innerHTML = '<p class="error"></p>'; log.firstChild.textContent = m.text; } |
|
| 153 | + | }); |
|
| 154 | + | </script></body></html>`; |
|
| 155 | + | } |
added src/cli.ts +71 −0
| 1 | + | // Running the spectra program: setup commands, and the bridge. |
|
| 2 | + | import * as cp from "node:child_process"; |
|
| 3 | + | import * as fs from "node:fs"; |
|
| 4 | + | import * as os from "node:os"; |
|
| 5 | + | import * as path from "node:path"; |
|
| 6 | + | import * as vscode from "vscode"; |
|
| 7 | + | ||
| 8 | + | export interface Ran { |
|
| 9 | + | code: number; |
|
| 10 | + | stdout: string; |
|
| 11 | + | stderr: string; |
|
| 12 | + | } |
|
| 13 | + | ||
| 14 | + | /** The spectra program: the setting, else PATH, else ~/.local/bin. */ |
|
| 15 | + | export function binary(): string | undefined { |
|
| 16 | + | const set = vscode.workspace.getConfiguration("spectra").get<string>("path")?.trim(); |
|
| 17 | + | if (set) return set.replace(/^~(?=\/)/, os.homedir()); |
|
| 18 | + | for (const dir of (process.env.PATH ?? "").split(path.delimiter)) { |
|
| 19 | + | const p = path.join(dir, "spectra"); |
|
| 20 | + | if (dir && fs.existsSync(p)) return p; |
|
| 21 | + | } |
|
| 22 | + | const local = path.join(os.homedir(), ".local", "bin", "spectra"); |
|
| 23 | + | return fs.existsSync(local) ? local : undefined; |
|
| 24 | + | } |
|
| 25 | + | ||
| 26 | + | export function run(args: string[], cwd?: string, input?: string): Promise<Ran> { |
|
| 27 | + | const bin = binary(); |
|
| 28 | + | if (!bin) return Promise.resolve({ code: 127, stdout: "", stderr: "spectra is not installed (Spectra: Check for spectra)" }); |
|
| 29 | + | return new Promise((resolve) => { |
|
| 30 | + | const child = cp.execFile(bin, args, { cwd, env: { ...process.env, NO_COLOR: "1" }, maxBuffer: 16 * 1024 * 1024 }, (err, stdout, stderr) => { |
|
| 31 | + | const code = err && typeof (err as cp.ExecFileException).code === "number" ? ((err as cp.ExecFileException).code as number) : err ? 1 : 0; |
|
| 32 | + | resolve({ code, stdout: String(stdout), stderr: String(stderr) }); |
|
| 33 | + | }); |
|
| 34 | + | if (input !== undefined) { |
|
| 35 | + | child.stdin?.end(input); |
|
| 36 | + | } |
|
| 37 | + | }); |
|
| 38 | + | } |
|
| 39 | + | ||
| 40 | + | /** Run, and parse stdout as JSON; throw with spectra's own words on failure. */ |
|
| 41 | + | export async function json<T>(args: string[], cwd?: string): Promise<T> { |
|
| 42 | + | const r = await run([...args], cwd); |
|
| 43 | + | if (r.code !== 0) throw new Error(why(r)); |
|
| 44 | + | return JSON.parse(r.stdout) as T; |
|
| 45 | + | } |
|
| 46 | + | ||
| 47 | + | /** Run; throw with spectra's own words on failure. */ |
|
| 48 | + | export async function ok(args: string[], cwd?: string, input?: string): Promise<string> { |
|
| 49 | + | const r = await run(args, cwd, input); |
|
| 50 | + | if (r.code !== 0) throw new Error(why(r)); |
|
| 51 | + | return r.stdout; |
|
| 52 | + | } |
|
| 53 | + | ||
| 54 | + | /** The error spectra printed, without its `error:` prefix. */ |
|
| 55 | + | export function why(r: Ran): string { |
|
| 56 | + | const text = (r.stderr.trim() || r.stdout.trim()).split("\n").filter((l) => l.trim()).pop() ?? `spectra exited ${r.code}`; |
|
| 57 | + | return text.replace(/^error:\s*/, ""); |
|
| 58 | + | } |
|
| 59 | + | ||
| 60 | + | export interface Status { |
|
| 61 | + | version: string; |
|
| 62 | + | home: string; |
|
| 63 | + | identity: { name: string; key: string } | null; |
|
| 64 | + | server: { url: string; instance: string } | null; |
|
| 65 | + | local: { data: string; initialised: boolean; serving: boolean }; |
|
| 66 | + | reachable: boolean; |
|
| 67 | + | } |
|
| 68 | + | ||
| 69 | + | export function status(): Promise<Status> { |
|
| 70 | + | return json<Status>(["status", "--json"]); |
|
| 71 | + | } |
added src/extension.ts +141 −0
| 1 | + | // Spectra for VS Code: set Spectra up, chat with your agents, and give |
|
| 2 | + | // them their briefs, without the command line (spectra-vscode spec). |
|
| 3 | + | import * as vscode from "vscode"; |
|
| 4 | + | import { Bridge } from "./bridge"; |
|
| 5 | + | import { BriefFs, SCHEME, uriFor } from "./briefs"; |
|
| 6 | + | import { openChannel } from "./chat"; |
|
| 7 | + | import * as cli from "./cli"; |
|
| 8 | + | import { Model } from "./model"; |
|
| 9 | + | import * as setup from "./setup"; |
|
| 10 | + | import * as views from "./views"; |
|
| 11 | + | ||
| 12 | + | export async function activate(ctx: vscode.ExtensionContext): Promise<void> { |
|
| 13 | + | const log = vscode.window.createOutputChannel("Spectra"); |
|
| 14 | + | const bridge = new Bridge(log); |
|
| 15 | + | const model = new Model(bridge); |
|
| 16 | + | const fsp = new BriefFs(bridge); |
|
| 17 | + | ctx.subscriptions.push(log, bridge, model, vscode.workspace.registerFileSystemProvider(SCHEME, fsp, { isCaseSensitive: true })); |
|
| 18 | + | ||
| 19 | + | for (const [id, view] of [ |
|
| 20 | + | ["spectra.channels", new views.Channels(model)], |
|
| 21 | + | ["spectra.agents", new views.Agents(model)], |
|
| 22 | + | ["spectra.inbox", new views.Inbox(model)], |
|
| 23 | + | ["spectra.briefs", new views.Briefs(model)], |
|
| 24 | + | ] as const) { |
|
| 25 | + | ctx.subscriptions.push(vscode.window.registerTreeDataProvider(id, view)); |
|
| 26 | + | } |
|
| 27 | + | ||
| 28 | + | const bar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 50); |
|
| 29 | + | bar.command = "workbench.view.extension.spectra"; |
|
| 30 | + | ctx.subscriptions.push(bar); |
|
| 31 | + | ||
| 32 | + | const set = (key: string, v: boolean) => vscode.commands.executeCommand("setContext", key, v); |
|
| 33 | + | let ready = false; |
|
| 34 | + | ||
| 35 | + | // What is set up, as contexts the walkthrough and views follow. |
|
| 36 | + | async function check(): Promise<void> { |
|
| 37 | + | const hasBinary = !!cli.binary(); |
|
| 38 | + | await set("spectra.hasBinary", hasBinary); |
|
| 39 | + | const st = hasBinary ? await cli.status().catch(() => undefined) : undefined; |
|
| 40 | + | await set("spectra.hasIdentity", !!st?.identity); |
|
| 41 | + | const nowReady = !!st?.identity && !!(st.server || st.local.initialised) && st.reachable; |
|
| 42 | + | if (nowReady !== ready) { |
|
| 43 | + | ready = nowReady; |
|
| 44 | + | if (ready) model.start(); |
|
| 45 | + | else model.stop(); |
|
| 46 | + | } |
|
| 47 | + | await set("spectra.ready", ready); |
|
| 48 | + | if (!ready) { |
|
| 49 | + | bar.text = "$(radio-tower) Spectra: set up"; |
|
| 50 | + | bar.tooltip = st?.identity && (st.server || st.local.initialised) ? "Spectra is not answering" : "Set Spectra up"; |
|
| 51 | + | bar.command = st?.identity && (st.server || st.local.initialised) ? "spectra.refresh" : "spectra.setup"; |
|
| 52 | + | bar.show(); |
|
| 53 | + | } |
|
| 54 | + | } |
|
| 55 | + | ||
| 56 | + | model.onDidChange(async () => { |
|
| 57 | + | await set("spectra.folderIsProject", !!model.folderProject()); |
|
| 58 | + | await set("spectra.hasAgent", model.agents.some((a) => !a.disabled)); |
|
| 59 | + | fsp.changed(); |
|
| 60 | + | if (model.error) { |
|
| 61 | + | bar.text = "$(warning) Spectra"; |
|
| 62 | + | bar.tooltip = model.error; |
|
| 63 | + | } else { |
|
| 64 | + | const online = model.agents.filter((a) => !a.disabled && a.presence !== "offline").length; |
|
| 65 | + | const unread = model.inbox.length; |
|
| 66 | + | bar.text = `$(radio-tower) ${online} agent${online === 1 ? "" : "s"} · ${unread} unread`; |
|
| 67 | + | bar.tooltip = `Spectra: ${model.agents.filter((a) => !a.disabled).length} agents, ${online} online; ${unread} unread for you`; |
|
| 68 | + | bar.command = unread ? "spectra.inbox.focus" : "workbench.view.extension.spectra"; |
|
| 69 | + | } |
|
| 70 | + | bar.show(); |
|
| 71 | + | }); |
|
| 72 | + | ||
| 73 | + | const hooks: setup.Hooks = { |
|
| 74 | + | changed: async () => { |
|
| 75 | + | bridge.restart(); |
|
| 76 | + | if (ready) model.start(); |
|
| 77 | + | await check(); |
|
| 78 | + | await model.refresh(); |
|
| 79 | + | }, |
|
| 80 | + | projects: () => model.projects, |
|
| 81 | + | agents: () => model.agents, |
|
| 82 | + | folderProject: () => model.folderProject(), |
|
| 83 | + | }; |
|
| 84 | + | ||
| 85 | + | const radar = (page?: string) => { |
|
| 86 | + | const bin = cli.binary(); |
|
| 87 | + | if (!bin) return; |
|
| 88 | + | const t = vscode.window.createTerminal({ name: "Spectra Radar", shellPath: bin, shellArgs: ["open", ...(page ? ["--page", page] : [])] }); |
|
| 89 | + | t.show(true); |
|
| 90 | + | }; |
|
| 91 | + | ||
| 92 | + | const cmd = (id: string, f: (...a: never[]) => unknown) => ctx.subscriptions.push(vscode.commands.registerCommand(id, f)); |
|
| 93 | + | cmd("spectra.setup", () => vscode.commands.executeCommand("workbench.action.openWalkthrough", `${ctx.extension.id}#spectra.setup`, false)); |
|
| 94 | + | cmd("spectra.checkBinary", async () => { |
|
| 95 | + | await setup.checkBinary(); |
|
| 96 | + | await check(); |
|
| 97 | + | }); |
|
| 98 | + | cmd("spectra.createIdentity", () => setup.createIdentity(hooks)); |
|
| 99 | + | cmd("spectra.setupHere", () => setup.setupHere(hooks)); |
|
| 100 | + | cmd("spectra.connectServer", () => setup.connectServer(hooks)); |
|
| 101 | + | cmd("spectra.registerFolder", () => setup.registerFolder(hooks)); |
|
| 102 | + | cmd("spectra.linkProject", () => setup.linkProject(hooks)); |
|
| 103 | + | cmd("spectra.addAgent", () => setup.addAgent(hooks)); |
|
| 104 | + | cmd("spectra.renewAgent", (item?: vscode.TreeItem) => setup.renewAgent(hooks, typeof item?.label === "string" ? item.label : undefined)); |
|
| 105 | + | cmd("spectra.openChannel", async (name?: string) => { |
|
| 106 | + | const pick = name ?? (await vscode.window.showQuickPick(model.channels.map((c) => c.name), { placeHolder: "Which channel?" })); |
|
| 107 | + | if (pick) openChannel(ctx, bridge, model, pick); |
|
| 108 | + | }); |
|
| 109 | + | cmd("spectra.openProjectChannel", () => openChannel(ctx, bridge, model, model.folderProject() ?? "general")); |
|
| 110 | + | cmd("spectra.say", async () => { |
|
| 111 | + | const channel = model.folderProject() ?? "general"; |
|
| 112 | + | const body = await vscode.window.showInputBox({ prompt: `Say in #${channel}, as yourself` }); |
|
| 113 | + | if (!body) return; |
|
| 114 | + | try { |
|
| 115 | + | await bridge.request("POST", `/api/v1/channels/${encodeURIComponent(channel)}/messages`, { body }); |
|
| 116 | + | } catch (e) { |
|
| 117 | + | void vscode.window.showErrorMessage(`Spectra: ${(e as Error).message}`); |
|
| 118 | + | } |
|
| 119 | + | }); |
|
| 120 | + | cmd("spectra.editOverallBrief", () => vscode.window.showTextDocument(uriFor())); |
|
| 121 | + | cmd("spectra.editProjectBrief", async (project?: string) => { |
|
| 122 | + | const p = project ?? model.folderProject() ?? (await vscode.window.showQuickPick(model.projects.map((x) => x.name), { placeHolder: "Which project's brief?" })); |
|
| 123 | + | if (p) await vscode.window.showTextDocument(uriFor(p)); |
|
| 124 | + | }); |
|
| 125 | + | cmd("spectra.openRadar", () => radar()); |
|
| 126 | + | cmd("spectra.openConstellation", () => radar("constellation")); |
|
| 127 | + | cmd("spectra.refresh", async () => { |
|
| 128 | + | await check(); |
|
| 129 | + | if (ready) await model.refresh(); |
|
| 130 | + | }); |
|
| 131 | + | ||
| 132 | + | await check(); |
|
| 133 | + | if (!ready && !ctx.globalState.get("spectra.walkthroughShown")) { |
|
| 134 | + | await ctx.globalState.update("spectra.walkthroughShown", true); |
|
| 135 | + | void vscode.commands.executeCommand("spectra.setup"); |
|
| 136 | + | } |
|
| 137 | + | } |
|
| 138 | + | ||
| 139 | + | export function deactivate(): void { |
|
| 140 | + | // The bridge ends with its input, which closes when the extension host does. |
|
| 141 | + | } |
added src/model.ts +91 −0
| 1 | + | // What the side bar shows, kept current from the event stream. |
|
| 2 | + | import * as vscode from "vscode"; |
|
| 3 | + | import type { Agent, Brief, Briefs, Channel, InboxItem, Project } from "./spectra-api"; |
|
| 4 | + | import { Bridge } from "./bridge"; |
|
| 5 | + | import { projectForFolder } from "./pure"; |
|
| 6 | + | ||
| 7 | + | export interface StreamEvent { |
|
| 8 | + | seq: number; |
|
| 9 | + | kind: string; |
|
| 10 | + | actor: string; |
|
| 11 | + | subject?: string | null; |
|
| 12 | + | } |
|
| 13 | + | ||
| 14 | + | export class Model implements vscode.Disposable { |
|
| 15 | + | brief: Brief | undefined; |
|
| 16 | + | agents: Agent[] = []; |
|
| 17 | + | channels: Channel[] = []; |
|
| 18 | + | projects: Project[] = []; |
|
| 19 | + | inbox: InboxItem[] = []; |
|
| 20 | + | briefs: Briefs = { overall: null, projects: [] }; |
|
| 21 | + | error: string | undefined; |
|
| 22 | + | ||
| 23 | + | private readonly changed = new vscode.EventEmitter<void>(); |
|
| 24 | + | readonly onDidChange = this.changed.event; |
|
| 25 | + | private readonly events = new vscode.EventEmitter<StreamEvent>(); |
|
| 26 | + | /** Every event from the stream, for open chats. */ |
|
| 27 | + | readonly onEvent = this.events.event; |
|
| 28 | + | private timer: NodeJS.Timeout | undefined; |
|
| 29 | + | private stream: vscode.Disposable | undefined; |
|
| 30 | + | ||
| 31 | + | constructor(private readonly bridge: Bridge) {} |
|
| 32 | + | ||
| 33 | + | /** Start following the stream and fetch everything. */ |
|
| 34 | + | start(): void { |
|
| 35 | + | this.stream?.dispose(); |
|
| 36 | + | this.stream = this.bridge.follow( |
|
| 37 | + | "/api/v1/events?follow=1", |
|
| 38 | + | (data) => { |
|
| 39 | + | try { |
|
| 40 | + | const e = JSON.parse(data) as StreamEvent; |
|
| 41 | + | this.events.fire(e); |
|
| 42 | + | } catch { |
|
| 43 | + | return; |
|
| 44 | + | } |
|
| 45 | + | this.soon(); |
|
| 46 | + | }, |
|
| 47 | + | () => this.soon(), |
|
| 48 | + | ); |
|
| 49 | + | void this.refresh(); |
|
| 50 | + | } |
|
| 51 | + | ||
| 52 | + | stop(): void { |
|
| 53 | + | this.stream?.dispose(); |
|
| 54 | + | this.stream = undefined; |
|
| 55 | + | } |
|
| 56 | + | ||
| 57 | + | /** Refresh shortly, once for a burst of events. */ |
|
| 58 | + | soon(): void { |
|
| 59 | + | if (this.timer) clearTimeout(this.timer); |
|
| 60 | + | this.timer = setTimeout(() => void this.refresh(), 300); |
|
| 61 | + | } |
|
| 62 | + | ||
| 63 | + | async refresh(): Promise<void> { |
|
| 64 | + | try { |
|
| 65 | + | const [brief, agents, channels, projects, inbox, briefs] = await Promise.all([ |
|
| 66 | + | this.bridge.request<Brief>("GET", "/api/v1/brief"), |
|
| 67 | + | this.bridge.request<Agent[]>("GET", "/api/v1/agents"), |
|
| 68 | + | this.bridge.request<Channel[]>("GET", "/api/v1/channels"), |
|
| 69 | + | this.bridge.request<Project[]>("GET", "/api/v1/projects"), |
|
| 70 | + | this.bridge.request<InboxItem[]>("GET", "/api/v1/inbox"), |
|
| 71 | + | this.bridge.request<Briefs>("GET", "/api/v1/briefs"), |
|
| 72 | + | ]); |
|
| 73 | + | Object.assign(this, { brief, agents, channels, projects, inbox, briefs, error: undefined }); |
|
| 74 | + | } catch (e) { |
|
| 75 | + | this.error = (e as Error).message; |
|
| 76 | + | } |
|
| 77 | + | this.changed.fire(); |
|
| 78 | + | } |
|
| 79 | + | ||
| 80 | + | /** The project of the first workspace folder, if it is one. */ |
|
| 81 | + | folderProject(): string | undefined { |
|
| 82 | + | const f = vscode.workspace.workspaceFolders?.[0]; |
|
| 83 | + | return f ? projectForFolder(this.projects, f.uri.fsPath) : undefined; |
|
| 84 | + | } |
|
| 85 | + | ||
| 86 | + | dispose(): void { |
|
| 87 | + | this.stop(); |
|
| 88 | + | this.changed.dispose(); |
|
| 89 | + | this.events.dispose(); |
|
| 90 | + | } |
|
| 91 | + | } |
added src/pure.ts +106 −0
| 1 | + | // Helpers with no VS Code in them, so they can be tested with node:test. |
|
| 2 | + | ||
| 3 | + | /** Server-sent events: feed chunks, get back each event's data. */ |
|
| 4 | + | export class SseParser { |
|
| 5 | + | private buf = ""; |
|
| 6 | + | private data: string[] = []; |
|
| 7 | + | push(chunk: string): string[] { |
|
| 8 | + | this.buf += chunk; |
|
| 9 | + | const out: string[] = []; |
|
| 10 | + | let i: number; |
|
| 11 | + | while ((i = this.buf.indexOf("\n")) >= 0) { |
|
| 12 | + | const line = this.buf.slice(0, i).replace(/\r$/, ""); |
|
| 13 | + | this.buf = this.buf.slice(i + 1); |
|
| 14 | + | if (line === "") { |
|
| 15 | + | if (this.data.length) out.push(this.data.join("\n")); |
|
| 16 | + | this.data = []; |
|
| 17 | + | } else if (line.startsWith("data:")) { |
|
| 18 | + | this.data.push(line.slice(5).replace(/^ /, "")); |
|
| 19 | + | } |
|
| 20 | + | } |
|
| 21 | + | return out; |
|
| 22 | + | } |
|
| 23 | + | } |
|
| 24 | + | ||
| 25 | + | export const KINDS: { kind: string; label: string; prefix: string }[] = [ |
|
| 26 | + | { kind: "claude-code", label: "Claude Code", prefix: "claude" }, |
|
| 27 | + | { kind: "antigravity", label: "Antigravity", prefix: "agy" }, |
|
| 28 | + | { kind: "kimi-code", label: "Kimi Code", prefix: "kimi" }, |
|
| 29 | + | { kind: "codex", label: "Codex", prefix: "codex" }, |
|
| 30 | + | { kind: "other", label: "Another MCP client", prefix: "agent" }, |
|
| 31 | + | ]; |
|
| 32 | + | ||
| 33 | + | /** A name Spectra accepts: lower-case letters, digits and dashes. */ |
|
| 34 | + | export function validName(n: string): boolean { |
|
| 35 | + | return /^[a-z0-9][a-z0-9-]{0,63}$/.test(n); |
|
| 36 | + | } |
|
| 37 | + | ||
| 38 | + | /** A project name from a folder name. */ |
|
| 39 | + | export function nameFromFolder(folder: string): string { |
|
| 40 | + | const base = folder.replace(/[\\/]+$/, "").split(/[\\/]/).pop() ?? ""; |
|
| 41 | + | const n = base.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/^-+/, "").slice(0, 64); |
|
| 42 | + | return n || "project"; |
|
| 43 | + | } |
|
| 44 | + | ||
| 45 | + | /** The suggested agent name for a kind on a project, not already taken. */ |
|
| 46 | + | export function suggestAgentName(kind: string, project: string, taken: string[]): string { |
|
| 47 | + | const prefix = KINDS.find((k) => k.kind === kind)?.prefix ?? "agent"; |
|
| 48 | + | const base = `${prefix}-${project}`.slice(0, 60); |
|
| 49 | + | if (!taken.includes(base)) return base; |
|
| 50 | + | for (let i = 2; ; i++) if (!taken.includes(`${base}-${i}`)) return `${base}-${i}`; |
|
| 51 | + | } |
|
| 52 | + | ||
| 53 | + | export const EXPIRIES: { label: string; arg: string | undefined }[] = [ |
|
| 54 | + | { label: "8 hours", arg: "8h" }, |
|
| 55 | + | { label: "A day", arg: "1d" }, |
|
| 56 | + | { label: "A week", arg: "7d" }, |
|
| 57 | + | { label: "Until I disable it", arg: undefined }, |
|
| 58 | + | ]; |
|
| 59 | + | ||
| 60 | + | /** `~/x` with the home directory. */ |
|
| 61 | + | export function expandHome(p: string, home: string): string { |
|
| 62 | + | return p === "~" ? home : p.startsWith("~/") ? home.replace(/\/$/, "") + p.slice(1) : p; |
|
| 63 | + | } |
|
| 64 | + | ||
| 65 | + | /** The arguments of the stdio shim for an agent. */ |
|
| 66 | + | export function shimArgs(agent: string, home: string): string[] { |
|
| 67 | + | return ["mcp", "--agent", agent, "--home", home]; |
|
| 68 | + | } |
|
| 69 | + | ||
| 70 | + | /** Antigravity's mcp_config.json with Spectra's server set, keeping the rest. */ |
|
| 71 | + | export function mergeMcpConfig(existing: string, command: string, args: string[]): string { |
|
| 72 | + | let cfg: Record<string, unknown> = {}; |
|
| 73 | + | if (existing.trim()) { |
|
| 74 | + | const parsed = JSON.parse(existing); |
|
| 75 | + | if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) cfg = parsed as Record<string, unknown>; |
|
| 76 | + | } |
|
| 77 | + | const servers = (cfg.mcpServers && typeof cfg.mcpServers === "object" ? cfg.mcpServers : {}) as Record<string, unknown>; |
|
| 78 | + | servers.spectra = { command, args }; |
|
| 79 | + | cfg.mcpServers = servers; |
|
| 80 | + | return JSON.stringify(cfg, null, 2) + "\n"; |
|
| 81 | + | } |
|
| 82 | + | ||
| 83 | + | /** The project whose folder holds `folder`, the deepest if several do. */ |
|
| 84 | + | export function projectForFolder(projects: { name: string; path?: string | null }[], folder: string): string | undefined { |
|
| 85 | + | const norm = (p: string) => p.replace(/[\\/]+$/, ""); |
|
| 86 | + | const f = norm(folder); |
|
| 87 | + | let best: { name: string; len: number } | undefined; |
|
| 88 | + | for (const p of projects) { |
|
| 89 | + | if (!p.path) continue; |
|
| 90 | + | const q = norm(p.path); |
|
| 91 | + | if ((f === q || f.startsWith(q + "/")) && (!best || q.length > best.len)) best = { name: p.name, len: q.length }; |
|
| 92 | + | } |
|
| 93 | + | return best?.name; |
|
| 94 | + | } |
|
| 95 | + | ||
| 96 | + | /** `12:04` today, else `3 Nov 12:04`. */ |
|
| 97 | + | export function when(ms: number, now = Date.now()): string { |
|
| 98 | + | const d = new Date(ms); |
|
| 99 | + | const t = d.toTimeString().slice(0, 5); |
|
| 100 | + | return new Date(now).toDateString() === d.toDateString() ? t : `${d.getDate()} ${d.toLocaleString("en-GB", { month: "short" })} ${t}`; |
|
| 101 | + | } |
|
| 102 | + | ||
| 103 | + | /** Text for HTML. */ |
|
| 104 | + | export function esc(s: string): string { |
|
| 105 | + | return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'"); |
|
| 106 | + | } |
added src/setup.ts +310 −0
| 1 | + | // Setting Spectra up without the command line (spec §5.0). Each step runs |
|
| 2 | + | // a spectra command; every confirmation spectra would ask at a terminal is |
|
| 3 | + | // a dialog here, after which the command runs with --yes or --expect. |
|
| 4 | + | import * as cp from "node:child_process"; |
|
| 5 | + | import * as fs from "node:fs"; |
|
| 6 | + | import * as os from "node:os"; |
|
| 7 | + | import * as path from "node:path"; |
|
| 8 | + | import * as vscode from "vscode"; |
|
| 9 | + | import * as cli from "./cli"; |
|
| 10 | + | import { EXPIRIES, KINDS, expandHome, mergeMcpConfig, nameFromFolder, shimArgs, suggestAgentName, validName } from "./pure"; |
|
| 11 | + | import type { Agent, Project } from "./spectra-api"; |
|
| 12 | + | ||
| 13 | + | export interface Hooks { |
|
| 14 | + | /** After a step that changes what Spectra is or who signs. */ |
|
| 15 | + | changed(): Promise<void>; |
|
| 16 | + | projects(): Project[]; |
|
| 17 | + | agents(): Agent[]; |
|
| 18 | + | folderProject(): string | undefined; |
|
| 19 | + | } |
|
| 20 | + | ||
| 21 | + | function folder(): vscode.WorkspaceFolder | undefined { |
|
| 22 | + | return vscode.workspace.workspaceFolders?.[0]; |
|
| 23 | + | } |
|
| 24 | + | ||
| 25 | + | async function fail(e: unknown): Promise<undefined> { |
|
| 26 | + | void vscode.window.showErrorMessage(`Spectra: ${(e as Error).message}`); |
|
| 27 | + | return undefined; |
|
| 28 | + | } |
|
| 29 | + | ||
| 30 | + | export async function checkBinary(): Promise<boolean> { |
|
| 31 | + | const bin = cli.binary(); |
|
| 32 | + | if (bin) { |
|
| 33 | + | const r = await cli.run(["--version"]); |
|
| 34 | + | if (r.code === 0) { |
|
| 35 | + | void vscode.window.showInformationMessage(`Found ${r.stdout.trim()} at ${bin}.`); |
|
| 36 | + | return true; |
|
| 37 | + | } |
|
| 38 | + | } |
|
| 39 | + | const pick = await vscode.window.showWarningMessage( |
|
| 40 | + | "The spectra program was not found. Install it (make install in the spectra repository puts it in ~/.local/bin), or point Spectra at it.", |
|
| 41 | + | "Locate spectra…", |
|
| 42 | + | ); |
|
| 43 | + | if (pick) { |
|
| 44 | + | const f = await vscode.window.showOpenDialog({ canSelectMany: false, openLabel: "Use this spectra" }); |
|
| 45 | + | if (f?.[0]) { |
|
| 46 | + | await vscode.workspace.getConfiguration("spectra").update("path", f[0].fsPath, vscode.ConfigurationTarget.Global); |
|
| 47 | + | return checkBinary(); |
|
| 48 | + | } |
|
| 49 | + | } |
|
| 50 | + | return false; |
|
| 51 | + | } |
|
| 52 | + | ||
| 53 | + | export async function createIdentity(h: Hooks): Promise<void> { |
|
| 54 | + | const st = await cli.status().catch(() => undefined); |
|
| 55 | + | if (st?.identity) { |
|
| 56 | + | void vscode.window.showInformationMessage(`You already have an identity: ${st.identity.name}, key ${st.identity.key.slice(0, 12)}…`); |
|
| 57 | + | return; |
|
| 58 | + | } |
|
| 59 | + | const name = await vscode.window.showInputBox({ |
|
| 60 | + | title: "Your identity (1/2)", |
|
| 61 | + | prompt: "Your name, as your agents and Radar will show it", |
|
| 62 | + | value: os.userInfo().username, |
|
| 63 | + | validateInput: (v) => (v.trim() ? undefined : "A name, please"), |
|
| 64 | + | }); |
|
| 65 | + | if (!name) return; |
|
| 66 | + | const contact = await vscode.window.showInputBox({ title: "Your identity (2/2)", prompt: "An email or URL to show beside it (optional)" }); |
|
| 67 | + | if (contact === undefined) return; |
|
| 68 | + | try { |
|
| 69 | + | const made = await cli.json<{ name: string; key: string; home: string }>(["identity", "create", "--name", name.trim(), "--contact", contact.trim(), "--json"]); |
|
| 70 | + | const copy = await vscode.window.showInformationMessage( |
|
| 71 | + | `Created your identity, ${made.name}. Its key is ${made.key.slice(0, 12)}…; the keys folder in ${made.home} is secret: anyone with it can act as you.`, |
|
| 72 | + | "Copy Key", |
|
| 73 | + | ); |
|
| 74 | + | if (copy) await vscode.env.clipboard.writeText(made.key); |
|
| 75 | + | await h.changed(); |
|
| 76 | + | } catch (e) { |
|
| 77 | + | await fail(e); |
|
| 78 | + | } |
|
| 79 | + | } |
|
| 80 | + | ||
| 81 | + | export async function setupHere(h: Hooks): Promise<void> { |
|
| 82 | + | const ok = await vscode.window.showInformationMessage( |
|
| 83 | + | "Run Spectra on this machine?", |
|
| 84 | + | { modal: true, detail: "This makes its ledger in ~/.local/share/spectra, owned by your identity, and starts it. Agents on this machine connect to it." }, |
|
| 85 | + | "Run Here", |
|
| 86 | + | ); |
|
| 87 | + | if (!ok) return; |
|
| 88 | + | try { |
|
| 89 | + | const st = await cli.status(); |
|
| 90 | + | if (!st.local.initialised) await cli.ok(["init"]); |
|
| 91 | + | if (!(await cli.status()).local.serving) startDaemon(); |
|
| 92 | + | await new Promise((r) => setTimeout(r, 1500)); |
|
| 93 | + | await h.changed(); |
|
| 94 | + | } catch (e) { |
|
| 95 | + | await fail(e); |
|
| 96 | + | } |
|
| 97 | + | } |
|
| 98 | + | ||
| 99 | + | /** The user service if it is installed, else a terminal running spectra serve. */ |
|
| 100 | + | export function startDaemon(): void { |
|
| 101 | + | const unit = path.join(os.homedir(), ".config", "systemd", "user", "spectra.service"); |
|
| 102 | + | if (fs.existsSync(unit)) { |
|
| 103 | + | cp.execFile("systemctl", ["--user", "start", "spectra"], () => undefined); |
|
| 104 | + | return; |
|
| 105 | + | } |
|
| 106 | + | const bin = cli.binary(); |
|
| 107 | + | if (!bin) return; |
|
| 108 | + | const t = vscode.window.createTerminal({ name: "Spectra", shellPath: bin, shellArgs: ["serve"], isTransient: false }); |
|
| 109 | + | t.show(true); |
|
| 110 | + | } |
|
| 111 | + | ||
| 112 | + | export async function connectServer(h: Hooks): Promise<void> { |
|
| 113 | + | const url = await vscode.window.showInputBox({ |
|
| 114 | + | title: "Connect to a server", |
|
| 115 | + | prompt: "The server's address", |
|
| 116 | + | placeHolder: "https://earth.example:7350", |
|
| 117 | + | validateInput: (v) => (/^https:\/\/[^/\s]+/.test(v.trim()) ? undefined : "An https:// address"), |
|
| 118 | + | }); |
|
| 119 | + | if (!url) return; |
|
| 120 | + | const st = await cli.status().catch(() => undefined); |
|
| 121 | + | let probe: { name: string; instance: string; owner: string | null; waiting_for: string | null; yours: boolean }; |
|
| 122 | + | try { |
|
| 123 | + | probe = await cli.json(["probe", url.trim(), "--json"]); |
|
| 124 | + | } catch (e) { |
|
| 125 | + | const msg = (e as Error).message; |
|
| 126 | + | const copy = await vscode.window.showErrorMessage( |
|
| 127 | + | `Spectra: ${msg}. If the server is not set up yet, run there: spectra init --owner ${st?.identity?.key ?? "YOUR-KEY"}`, |
|
| 128 | + | ...(st?.identity ? ["Copy That Command"] : []), |
|
| 129 | + | ); |
|
| 130 | + | if (copy && st?.identity) await vscode.env.clipboard.writeText(`spectra init --owner ${st.identity.key}`); |
|
| 131 | + | return; |
|
| 132 | + | } |
|
| 133 | + | if (!probe.yours) { |
|
| 134 | + | const whose = probe.owner ? "belongs to someone else" : `waits for another identity (${(probe.waiting_for ?? "").slice(0, 12)}…)`; |
|
| 135 | + | const copy = await vscode.window.showErrorMessage( |
|
| 136 | + | `Spectra: ${probe.name} ${whose}. To set it up for you, run there: spectra init --owner ${st?.identity?.key ?? "YOUR-KEY"}`, |
|
| 137 | + | ...(st?.identity ? ["Copy That Command"] : []), |
|
| 138 | + | ); |
|
| 139 | + | if (copy && st?.identity) await vscode.env.clipboard.writeText(`spectra init --owner ${st.identity.key}`); |
|
| 140 | + | return; |
|
| 141 | + | } |
|
| 142 | + | const grouped = probe.instance.match(/.{1,4}/g)!.join(" "); |
|
| 143 | + | const trust = await vscode.window.showWarningMessage( |
|
| 144 | + | `Trust ${probe.name} as your Spectra?`, |
|
| 145 | + | { |
|
| 146 | + | modal: true, |
|
| 147 | + | detail: `Its instance key is:\n\n${grouped}\n\nWhen the server was set up, spectra init printed its instance key. Trust it only if this is the same. Your identity will then delegate to it, and this machine will pin its certificate.`, |
|
| 148 | + | }, |
|
| 149 | + | "Trust", |
|
| 150 | + | ); |
|
| 151 | + | if (!trust) return; |
|
| 152 | + | try { |
|
| 153 | + | await cli.ok(["connect", url.trim(), "--expect", probe.instance]); |
|
| 154 | + | void vscode.window.showInformationMessage(`Connected to ${probe.name}.`); |
|
| 155 | + | await h.changed(); |
|
| 156 | + | } catch (e) { |
|
| 157 | + | await fail(e); |
|
| 158 | + | } |
|
| 159 | + | } |
|
| 160 | + | ||
| 161 | + | export async function registerFolder(h: Hooks): Promise<void> { |
|
| 162 | + | const f = folder(); |
|
| 163 | + | if (!f) { |
|
| 164 | + | void vscode.window.showWarningMessage("Open a folder first: a project is the work in it."); |
|
| 165 | + | return; |
|
| 166 | + | } |
|
| 167 | + | const already = h.folderProject(); |
|
| 168 | + | if (already) { |
|
| 169 | + | void vscode.window.showInformationMessage(`This folder is already the project ${already}.`); |
|
| 170 | + | return; |
|
| 171 | + | } |
|
| 172 | + | const taken = h.projects().map((p) => p.name); |
|
| 173 | + | const name = await vscode.window.showInputBox({ |
|
| 174 | + | title: "Register this folder as a project (1/2)", |
|
| 175 | + | prompt: "The project's name; its channel is #name", |
|
| 176 | + | value: nameFromFolder(f.uri.fsPath), |
|
| 177 | + | validateInput: (v) => (!validName(v) ? "Lower-case letters, digits and dashes" : taken.includes(v) ? "Taken" : undefined), |
|
| 178 | + | }); |
|
| 179 | + | if (!name) return; |
|
| 180 | + | const description = await vscode.window.showInputBox({ title: "Register this folder as a project (2/2)", prompt: "What the work is, for your agents (optional)" }); |
|
| 181 | + | if (description === undefined) return; |
|
| 182 | + | try { |
|
| 183 | + | await cli.ok(["project", "add", f.uri.fsPath, "--name", name, "-d", description]); |
|
| 184 | + | await h.changed(); |
|
| 185 | + | } catch (e) { |
|
| 186 | + | await fail(e); |
|
| 187 | + | } |
|
| 188 | + | } |
|
| 189 | + | ||
| 190 | + | export async function linkProject(h: Hooks): Promise<void> { |
|
| 191 | + | const names = h.projects().map((p) => p.name); |
|
| 192 | + | const p = await vscode.window.showQuickPick(names, { title: "Link projects (1/2)", placeHolder: "Which project depends on another?" }); |
|
| 193 | + | if (!p) return; |
|
| 194 | + | const q = await vscode.window.showQuickPick(names.filter((n) => n !== p), { title: "Link projects (2/2)", placeHolder: `${p} depends on…` }); |
|
| 195 | + | if (!q) return; |
|
| 196 | + | try { |
|
| 197 | + | await cli.ok(["project", "link", p, "--depends-on", q]); |
|
| 198 | + | await h.changed(); |
|
| 199 | + | } catch (e) { |
|
| 200 | + | await fail(e); |
|
| 201 | + | } |
|
| 202 | + | } |
|
| 203 | + | ||
| 204 | + | export async function addAgent(h: Hooks): Promise<void> { |
|
| 205 | + | const projects = h.projects().map((p) => p.name); |
|
| 206 | + | if (!projects.length) { |
|
| 207 | + | void vscode.window.showWarningMessage("An agent needs a project: register this folder first.", "Register This Folder").then((x) => { |
|
| 208 | + | if (x) void vscode.commands.executeCommand("spectra.registerFolder"); |
|
| 209 | + | }); |
|
| 210 | + | return; |
|
| 211 | + | } |
|
| 212 | + | const kind = await vscode.window.showQuickPick( |
|
| 213 | + | KINDS.map((k) => ({ label: k.label, client: k.kind })), |
|
| 214 | + | { title: "Add an agent (1/4)", placeHolder: "Which client will the agent run in?" }, |
|
| 215 | + | ); |
|
| 216 | + | if (!kind) return; |
|
| 217 | + | const mine = h.folderProject(); |
|
| 218 | + | const project = |
|
| 219 | + | projects.length === 1 |
|
| 220 | + | ? projects[0] |
|
| 221 | + | : await vscode.window.showQuickPick( |
|
| 222 | + | [...projects].sort((a, b) => Number(b === mine) - Number(a === mine)), |
|
| 223 | + | { title: "Add an agent (2/4)", placeHolder: "Which project will it work on?" }, |
|
| 224 | + | ); |
|
| 225 | + | if (!project) return; |
|
| 226 | + | const taken = h.agents().map((a) => a.name); |
|
| 227 | + | const name = await vscode.window.showInputBox({ |
|
| 228 | + | title: "Add an agent (3/4)", |
|
| 229 | + | prompt: "Its name", |
|
| 230 | + | value: suggestAgentName(kind.client, project, taken), |
|
| 231 | + | validateInput: (v) => (!validName(v) ? "Lower-case letters, digits and dashes" : taken.includes(v) ? "Taken" : undefined), |
|
| 232 | + | }); |
|
| 233 | + | if (!name) return; |
|
| 234 | + | const expiry = await vscode.window.showQuickPick(EXPIRIES.map((e) => ({ label: e.label, arg: e.arg })), { |
|
| 235 | + | title: "Add an agent (4/4)", |
|
| 236 | + | placeHolder: "How long may it act for you? (Renew Agent gives it more time later.)", |
|
| 237 | + | }); |
|
| 238 | + | if (!expiry) return; |
|
| 239 | + | const home = expandHome(path.join(vscode.workspace.getConfiguration("spectra").get<string>("agentsHome") || "~/.spectra-agents", name), os.homedir()); |
|
| 240 | + | const ok = await vscode.window.showWarningMessage( |
|
| 241 | + | `Let ${name} act for you?`, |
|
| 242 | + | { modal: true, detail: `${kind.label} on #${project}, ${expiry.arg ? `for ${expiry.label.toLowerCase()}` : "until you disable it"}. Its key is made in ${home}; your identity will delegate to it.` }, |
|
| 243 | + | "Let It Act for Me", |
|
| 244 | + | ); |
|
| 245 | + | if (!ok) return; |
|
| 246 | + | try { |
|
| 247 | + | const args = ["agent", "add", name, "--kind", kind.client, "--project", project, "--home", home, "--yes", ...(expiry.arg ? ["--expires", expiry.arg] : [])]; |
|
| 248 | + | await cli.ok(args); |
|
| 249 | + | await h.changed(); |
|
| 250 | + | } catch (e) { |
|
| 251 | + | await fail(e); |
|
| 252 | + | return; |
|
| 253 | + | } |
|
| 254 | + | await configureClient(kind.client, name, home); |
|
| 255 | + | } |
|
| 256 | + | ||
| 257 | + | /** Write the agent's MCP entry for its client, or say how. */ |
|
| 258 | + | async function configureClient(kind: string, agent: string, home: string): Promise<void> { |
|
| 259 | + | const bin = cli.binary() ?? "spectra"; |
|
| 260 | + | const args = shimArgs(agent, home); |
|
| 261 | + | const line = `${bin} ${args.join(" ")}`; |
|
| 262 | + | const which = KINDS.find((k) => k.kind === kind)?.label ?? kind; |
|
| 263 | + | const go = await vscode.window.showInformationMessage(`Added ${agent}. Connect ${which} to Spectra as ${agent}?`, "Connect It", "Show Me How"); |
|
| 264 | + | if (go === "Show Me How" || !go) { |
|
| 265 | + | if (go) { |
|
| 266 | + | const doc = await vscode.workspace.openTextDocument({ |
|
| 267 | + | language: "markdown", |
|
| 268 | + | content: `# Connecting ${agent}\n\nIn ${which}, add an MCP server named \`spectra\` that runs:\n\n\`\`\`\n${line}\n\`\`\`\n\nClaude Code: \`claude mcp add spectra -- ${line}\` in the project's folder.\nKimi Code: \`kimi mcp add spectra -- ${line}\`.\nAntigravity: in \`~/.gemini/config/mcp_config.json\`, \`mcpServers.spectra = { "command": "${bin}", "args": ${JSON.stringify(args)} }\`.\n`, |
|
| 269 | + | }); |
|
| 270 | + | await vscode.window.showTextDocument(doc); |
|
| 271 | + | } |
|
| 272 | + | return; |
|
| 273 | + | } |
|
| 274 | + | const cwd = folder()?.uri.fsPath; |
|
| 275 | + | try { |
|
| 276 | + | if (kind === "claude-code" || kind === "kimi-code") { |
|
| 277 | + | const tool = kind === "claude-code" ? "claude" : "kimi"; |
|
| 278 | + | await new Promise<void>((resolve) => cp.execFile(tool, ["mcp", "remove", "spectra"], { cwd }, () => resolve())); |
|
| 279 | + | await new Promise<void>((resolve, reject) => |
|
| 280 | + | cp.execFile(tool, ["mcp", "add", "spectra", "--", bin, ...args], { cwd }, (err, _o, stderr) => (err ? reject(new Error(stderr.trim() || err.message)) : resolve())), |
|
| 281 | + | ); |
|
| 282 | + | void vscode.window.showInformationMessage(`${which} in this folder now connects to Spectra as ${agent}: start a new session there to pick it up.`); |
|
| 283 | + | } else if (kind === "antigravity") { |
|
| 284 | + | const file = path.join(os.homedir(), ".gemini", "config", "mcp_config.json"); |
|
| 285 | + | const before = fs.existsSync(file) ? fs.readFileSync(file, "utf8") : ""; |
|
| 286 | + | if (before) fs.copyFileSync(file, file + ".bak"); |
|
| 287 | + | fs.mkdirSync(path.dirname(file), { recursive: true }); |
|
| 288 | + | fs.writeFileSync(file, mergeMcpConfig(before, bin, args)); |
|
| 289 | + | void vscode.window.showInformationMessage(`Antigravity now connects to Spectra as ${agent} (${file}${before ? ", previous copy kept as .bak" : ""}): restart it to pick it up.`); |
|
| 290 | + | } else { |
|
| 291 | + | await vscode.env.clipboard.writeText(line); |
|
| 292 | + | void vscode.window.showInformationMessage(`Copied the command for ${which}'s MCP settings: ${line}`); |
|
| 293 | + | } |
|
| 294 | + | } catch (e) { |
|
| 295 | + | void vscode.window.showWarningMessage(`Could not configure ${which} (${(e as Error).message}). Add an MCP server named spectra running: ${line}`); |
|
| 296 | + | } |
|
| 297 | + | } |
|
| 298 | + | ||
| 299 | + | export async function renewAgent(h: Hooks, agent?: string): Promise<void> { |
|
| 300 | + | const name = agent ?? (await vscode.window.showQuickPick(h.agents().filter((a) => !a.disabled).map((a) => a.name), { placeHolder: "Which agent?" })); |
|
| 301 | + | if (!name) return; |
|
| 302 | + | const expiry = await vscode.window.showQuickPick(EXPIRIES.map((e) => ({ label: e.label, arg: e.arg ?? "never" })), { placeHolder: `How long may ${name} act for you from now?` }); |
|
| 303 | + | if (!expiry) return; |
|
| 304 | + | try { |
|
| 305 | + | await cli.ok(["agent", "renew", name, "--expires", expiry.arg]); |
|
| 306 | + | await h.changed(); |
|
| 307 | + | } catch (e) { |
|
| 308 | + | await fail(e); |
|
| 309 | + | } |
|
| 310 | + | } |
added src/spectra-api.d.ts +150 −0
| 1 | + | // Generated by `cargo run -p spectra-api --bin spectra-api-typescript`; do not edit. |
|
| 2 | + | // Types of the Spectra JSON API (spec §18). |
|
| 3 | + | ||
| 4 | + | export type Problem = { |
|
| 5 | + | detail?: string | null; |
|
| 6 | + | status: number; |
|
| 7 | + | title: string; |
|
| 8 | + | type: string; |
|
| 9 | + | }; |
|
| 10 | + | ||
| 11 | + | export type Version = { |
|
| 12 | + | ledger_format: number; |
|
| 13 | + | mcp_protocol_versions: string[]; |
|
| 14 | + | version: string; |
|
| 15 | + | }; |
|
| 16 | + | ||
| 17 | + | export type Agent = { |
|
| 18 | + | delegation_expires?: number | null; |
|
| 19 | + | disabled: boolean; |
|
| 20 | + | display: string; |
|
| 21 | + | key: string; |
|
| 22 | + | kind: string; |
|
| 23 | + | name: string; |
|
| 24 | + | owner: string; |
|
| 25 | + | presence: string; |
|
| 26 | + | project: string; |
|
| 27 | + | state?: string; |
|
| 28 | + | status?: string | null; |
|
| 29 | + | }; |
|
| 30 | + | ||
| 31 | + | export type Project = { |
|
| 32 | + | agents?: string[]; |
|
| 33 | + | depended_on_by: string[]; |
|
| 34 | + | depends_on: string[]; |
|
| 35 | + | description?: string; |
|
| 36 | + | name: string; |
|
| 37 | + | path?: string | null; |
|
| 38 | + | url?: string | null; |
|
| 39 | + | }; |
|
| 40 | + | ||
| 41 | + | export type Channel = { |
|
| 42 | + | display: string; |
|
| 43 | + | id: string; |
|
| 44 | + | kind: string; |
|
| 45 | + | messages: number; |
|
| 46 | + | name: string; |
|
| 47 | + | purpose?: string | null; |
|
| 48 | + | }; |
|
| 49 | + | ||
| 50 | + | export type Message = { |
|
| 51 | + | at_ms: number; |
|
| 52 | + | author: string; |
|
| 53 | + | author_display: string; |
|
| 54 | + | body: string; |
|
| 55 | + | channel: string; |
|
| 56 | + | id: string; |
|
| 57 | + | kind: string; |
|
| 58 | + | mentions: string[]; |
|
| 59 | + | redacted: boolean; |
|
| 60 | + | refs: string[]; |
|
| 61 | + | replies: number; |
|
| 62 | + | reply_to?: string | null; |
|
| 63 | + | }; |
|
| 64 | + | ||
| 65 | + | export type Messages = { |
|
| 66 | + | channel: string; |
|
| 67 | + | items: Message[]; |
|
| 68 | + | next?: string | null; |
|
| 69 | + | }; |
|
| 70 | + | ||
| 71 | + | export type NewMessage = { |
|
| 72 | + | body: string; |
|
| 73 | + | kind?: string | null; |
|
| 74 | + | reply_to?: string | null; |
|
| 75 | + | }; |
|
| 76 | + | ||
| 77 | + | export type Posted = { |
|
| 78 | + | channel: string; |
|
| 79 | + | duplicate: boolean; |
|
| 80 | + | id: string; |
|
| 81 | + | }; |
|
| 82 | + | ||
| 83 | + | export type InboxItem = { |
|
| 84 | + | at_ms: number; |
|
| 85 | + | channel: string; |
|
| 86 | + | from: string; |
|
| 87 | + | from_display?: string; |
|
| 88 | + | id: string; |
|
| 89 | + | kind: string; |
|
| 90 | + | summary: string; |
|
| 91 | + | }; |
|
| 92 | + | ||
| 93 | + | export type Ack = { |
|
| 94 | + | ids: string[]; |
|
| 95 | + | reaction?: string | null; |
|
| 96 | + | }; |
|
| 97 | + | ||
| 98 | + | export type Hit = { |
|
| 99 | + | at_ms: number; |
|
| 100 | + | author: string; |
|
| 101 | + | author_display?: string; |
|
| 102 | + | channel: string; |
|
| 103 | + | id: string; |
|
| 104 | + | snippet: string; |
|
| 105 | + | thread?: string | null; |
|
| 106 | + | }; |
|
| 107 | + | ||
| 108 | + | export type ProjectBrief = { |
|
| 109 | + | project: string; |
|
| 110 | + | text: string; |
|
| 111 | + | }; |
|
| 112 | + | ||
| 113 | + | export type Briefs = { |
|
| 114 | + | overall?: string | null; |
|
| 115 | + | projects: ProjectBrief[]; |
|
| 116 | + | }; |
|
| 117 | + | ||
| 118 | + | export type SetBrief = { |
|
| 119 | + | project?: string | null; |
|
| 120 | + | text: string; |
|
| 121 | + | }; |
|
| 122 | + | ||
| 123 | + | export type EventSummary = { |
|
| 124 | + | actor: string; |
|
| 125 | + | assurance: number; |
|
| 126 | + | at_ms: number; |
|
| 127 | + | hash: string; |
|
| 128 | + | id: string; |
|
| 129 | + | kind: string; |
|
| 130 | + | method: string; |
|
| 131 | + | seq: number; |
|
| 132 | + | subject?: string | null; |
|
| 133 | + | }; |
|
| 134 | + | ||
| 135 | + | export type Events = { |
|
| 136 | + | items: EventSummary[]; |
|
| 137 | + | next?: number | null; |
|
| 138 | + | }; |
|
| 139 | + | ||
| 140 | + | export type Brief = { |
|
| 141 | + | actor: string; |
|
| 142 | + | agents: Agent[]; |
|
| 143 | + | head: number; |
|
| 144 | + | inbox: InboxItem[]; |
|
| 145 | + | project?: string | null; |
|
| 146 | + | rules_version: string; |
|
| 147 | + | unread: number; |
|
| 148 | + | you: string; |
|
| 149 | + | }; |
|
| 150 | + |
added src/views.ts +103 −0
| 1 | + | // The side bar: Channels, Agents, Inbox and Briefs. |
|
| 2 | + | import * as vscode from "vscode"; |
|
| 3 | + | import { Model } from "./model"; |
|
| 4 | + | import { when } from "./pure"; |
|
| 5 | + | ||
| 6 | + | abstract class View implements vscode.TreeDataProvider<vscode.TreeItem> { |
|
| 7 | + | private readonly emitter = new vscode.EventEmitter<void>(); |
|
| 8 | + | readonly onDidChangeTreeData = this.emitter.event; |
|
| 9 | + | constructor(protected readonly model: Model) { |
|
| 10 | + | model.onDidChange(() => this.emitter.fire()); |
|
| 11 | + | } |
|
| 12 | + | getTreeItem(i: vscode.TreeItem): vscode.TreeItem { |
|
| 13 | + | return i; |
|
| 14 | + | } |
|
| 15 | + | getChildren(): vscode.TreeItem[] { |
|
| 16 | + | if (this.model.error) { |
|
| 17 | + | const i = new vscode.TreeItem("Spectra is not answering"); |
|
| 18 | + | i.description = this.model.error; |
|
| 19 | + | i.tooltip = this.model.error; |
|
| 20 | + | i.iconPath = new vscode.ThemeIcon("warning"); |
|
| 21 | + | i.command = { command: "spectra.refresh", title: "Refresh" }; |
|
| 22 | + | return [i]; |
|
| 23 | + | } |
|
| 24 | + | return this.items(); |
|
| 25 | + | } |
|
| 26 | + | protected abstract items(): vscode.TreeItem[]; |
|
| 27 | + | } |
|
| 28 | + | ||
| 29 | + | const PRESENCE: Record<string, string> = { online: "charts.green", busy: "charts.purple", idle: "charts.yellow", blocked: "charts.orange", paused: "charts.yellow", offline: "disabledForeground" }; |
|
| 30 | + | ||
| 31 | + | export class Agents extends View { |
|
| 32 | + | protected items(): vscode.TreeItem[] { |
|
| 33 | + | return this.model.agents |
|
| 34 | + | .filter((a) => !a.disabled) |
|
| 35 | + | .map((a) => { |
|
| 36 | + | const i = new vscode.TreeItem(a.name); |
|
| 37 | + | i.description = `${a.presence} · #${a.project}${a.status ? ` · ${a.status}` : ""}`; |
|
| 38 | + | const expires = a.delegation_expires ? `\nMay act for you until ${new Date(a.delegation_expires).toLocaleString()}` : ""; |
|
| 39 | + | const state = a.state && a.state !== "ok" ? `\n⚠ ${a.state}` : ""; |
|
| 40 | + | i.tooltip = `${a.display}\n${a.kind}${expires}${state}`; |
|
| 41 | + | i.iconPath = new vscode.ThemeIcon(a.state && a.state !== "ok" ? "warning" : "circle-filled", new vscode.ThemeColor(PRESENCE[a.presence] ?? "disabledForeground")); |
|
| 42 | + | i.contextValue = "agent"; |
|
| 43 | + | i.id = `agent:${a.name}`; |
|
| 44 | + | return i; |
|
| 45 | + | }); |
|
| 46 | + | } |
|
| 47 | + | } |
|
| 48 | + | ||
| 49 | + | export class Channels extends View { |
|
| 50 | + | protected items(): vscode.TreeItem[] { |
|
| 51 | + | const mine = this.model.folderProject(); |
|
| 52 | + | const rows = [...this.model.channels].filter((c) => c.kind !== "system").sort((a, b) => Number(b.name === mine) - Number(a.name === mine)); |
|
| 53 | + | return rows.map((c) => { |
|
| 54 | + | const i = new vscode.TreeItem(c.display); |
|
| 55 | + | i.description = c.purpose ?? undefined; |
|
| 56 | + | i.tooltip = `${c.display}${c.purpose ? `\n${c.purpose}` : ""}\n${c.messages} message(s)`; |
|
| 57 | + | i.iconPath = new vscode.ThemeIcon(c.kind === "direct" ? "mention" : c.name === mine ? "star-full" : "comment-discussion"); |
|
| 58 | + | i.command = { command: "spectra.openChannel", title: "Open", arguments: [c.name] }; |
|
| 59 | + | i.id = `channel:${c.name}`; |
|
| 60 | + | return i; |
|
| 61 | + | }); |
|
| 62 | + | } |
|
| 63 | + | } |
|
| 64 | + | ||
| 65 | + | export class Inbox extends View { |
|
| 66 | + | protected items(): vscode.TreeItem[] { |
|
| 67 | + | if (!this.model.inbox.length) { |
|
| 68 | + | const i = new vscode.TreeItem("Nothing unread"); |
|
| 69 | + | i.iconPath = new vscode.ThemeIcon("check"); |
|
| 70 | + | return [i]; |
|
| 71 | + | } |
|
| 72 | + | return this.model.inbox.map((m) => { |
|
| 73 | + | const i = new vscode.TreeItem(m.summary.replace(/\s+/g, " ").slice(0, 120)); |
|
| 74 | + | i.description = `${m.from_display || m.from} · ${m.channel} · ${when(m.at_ms)}`; |
|
| 75 | + | i.iconPath = new vscode.ThemeIcon(m.kind === "dm" ? "mail" : "mention"); |
|
| 76 | + | i.command = { command: "spectra.openChannel", title: "Open", arguments: [m.channel.replace(/^#/, "")] }; |
|
| 77 | + | return i; |
|
| 78 | + | }); |
|
| 79 | + | } |
|
| 80 | + | } |
|
| 81 | + | ||
| 82 | + | export class Briefs extends View { |
|
| 83 | + | protected items(): vscode.TreeItem[] { |
|
| 84 | + | const overall = new vscode.TreeItem("Overall brief"); |
|
| 85 | + | overall.description = this.model.briefs.overall ? firstLine(this.model.briefs.overall) : "not set"; |
|
| 86 | + | overall.iconPath = new vscode.ThemeIcon("globe"); |
|
| 87 | + | overall.command = { command: "spectra.editOverallBrief", title: "Edit" }; |
|
| 88 | + | const items = [overall]; |
|
| 89 | + | for (const p of this.model.projects) { |
|
| 90 | + | const b = this.model.briefs.projects.find((x) => x.project === p.name); |
|
| 91 | + | const i = new vscode.TreeItem(p.name); |
|
| 92 | + | i.description = b ? firstLine(b.text) : "not set"; |
|
| 93 | + | i.iconPath = new vscode.ThemeIcon("note"); |
|
| 94 | + | i.command = { command: "spectra.editProjectBrief", title: "Edit", arguments: [p.name] }; |
|
| 95 | + | items.push(i); |
|
| 96 | + | } |
|
| 97 | + | return items; |
|
| 98 | + | } |
|
| 99 | + | } |
|
| 100 | + | ||
| 101 | + | function firstLine(s: string): string { |
|
| 102 | + | return s.trim().split("\n")[0].slice(0, 80); |
|
| 103 | + | } |
added test/pure.test.mjs +45 −0
| 1 | + | import { test } from "node:test"; |
|
| 2 | + | import assert from "node:assert/strict"; |
|
| 3 | + | import { SseParser, validName, nameFromFolder, suggestAgentName, expandHome, shimArgs, mergeMcpConfig, projectForFolder, esc } from "../out/pure.mjs"; |
|
| 4 | + | ||
| 5 | + | test("server-sent events are split into their data, across chunks", () => { |
|
| 6 | + | const p = new SseParser(); |
|
| 7 | + | assert.deepEqual(p.push(": subscribed\n\nda"), []); |
|
| 8 | + | assert.deepEqual(p.push('ta: {"kind":"message.posted"}\n\ndata: a\r\ndata: b\n\n'), ['{"kind":"message.posted"}', "a\nb"]); |
|
| 9 | + | }); |
|
| 10 | + | ||
| 11 | + | test("names are Spectra's", () => { |
|
| 12 | + | assert.ok(validName("claude-oxygen")); |
|
| 13 | + | assert.ok(!validName("Claude")); |
|
| 14 | + | assert.ok(!validName("-x")); |
|
| 15 | + | assert.equal(nameFromFolder("/home/a/My Book/"), "my-book"); |
|
| 16 | + | assert.equal(suggestAgentName("claude-code", "book", []), "claude-book"); |
|
| 17 | + | assert.equal(suggestAgentName("antigravity", "book", ["agy-book"]), "agy-book-2"); |
|
| 18 | + | }); |
|
| 19 | + | ||
| 20 | + | test("homes and shim arguments", () => { |
|
| 21 | + | assert.equal(expandHome("~/.spectra-agents/x", "/home/a"), "/home/a/.spectra-agents/x"); |
|
| 22 | + | assert.equal(expandHome("/abs", "/home/a"), "/abs"); |
|
| 23 | + | assert.deepEqual(shimArgs("claude-book", "/h"), ["mcp", "--agent", "claude-book", "--home", "/h"]); |
|
| 24 | + | }); |
|
| 25 | + | ||
| 26 | + | test("Antigravity's config keeps its other servers", () => { |
|
| 27 | + | const before = JSON.stringify({ mcpServers: { other: { command: "x" } }, theme: "dark" }); |
|
| 28 | + | const after = JSON.parse(mergeMcpConfig(before, "/bin/spectra", ["mcp", "--agent", "agy-book"])); |
|
| 29 | + | assert.deepEqual(after.mcpServers.other, { command: "x" }); |
|
| 30 | + | assert.deepEqual(after.mcpServers.spectra, { command: "/bin/spectra", args: ["mcp", "--agent", "agy-book"] }); |
|
| 31 | + | assert.equal(after.theme, "dark"); |
|
| 32 | + | assert.deepEqual(JSON.parse(mergeMcpConfig("", "s", [])).mcpServers.spectra, { command: "s", args: [] }); |
|
| 33 | + | }); |
|
| 34 | + | ||
| 35 | + | test("a folder belongs to the deepest project that holds it", () => { |
|
| 36 | + | const ps = [{ name: "dev", path: "/home/a/dev" }, { name: "book", path: "/home/a/dev/book" }, { name: "idea", path: null }]; |
|
| 37 | + | assert.equal(projectForFolder(ps, "/home/a/dev/book"), "book"); |
|
| 38 | + | assert.equal(projectForFolder(ps, "/home/a/dev/book/ch3"), "book"); |
|
| 39 | + | assert.equal(projectForFolder(ps, "/home/a/dev/other"), "dev"); |
|
| 40 | + | assert.equal(projectForFolder(ps, "/home/a/devx"), undefined); |
|
| 41 | + | }); |
|
| 42 | + | ||
| 43 | + | test("HTML is escaped", () => { |
|
| 44 | + | assert.equal(esc(`<b a="1">'&`), "<b a="1">'&"); |
|
| 45 | + | }); |
added test/smoke.mjs +52 −0
| 1 | + | // Smoke test against a real Spectra (not part of `npm test`): the bridge |
|
| 2 | + | // signs, the model fills from the API, the stream delivers, a brief saved |
|
| 3 | + | // through the API reaches Spectra. Run with: npm run smoke |
|
| 4 | + | import { spawn, execFileSync } from "node:child_process"; |
|
| 5 | + | import { mkdtempSync, rmSync } from "node:fs"; |
|
| 6 | + | import { tmpdir } from "node:os"; |
|
| 7 | + | import { join } from "node:path"; |
|
| 8 | + | import assert from "node:assert/strict"; |
|
| 9 | + | ||
| 10 | + | const bin = process.env.SPECTRA_BIN; |
|
| 11 | + | assert.ok(bin, "set SPECTRA_BIN to a spectra binary"); |
|
| 12 | + | const dir = mkdtempSync(join(process.env.SMOKE_DIR || tmpdir(), "spectra-smoke-")); |
|
| 13 | + | const env = { ...process.env, HOME: dir, SPECTRA_HOME: join(dir, "owner"), SPECTRA_DATA: join(dir, "data"), XDG_DATA_HOME: join(dir, "xdg"), NO_COLOR: "1" }; |
|
| 14 | + | Object.assign(process.env, env); |
|
| 15 | + | const sp = (...a) => execFileSync(bin, a, { env }).toString(); |
|
| 16 | + | sp("identity", "create", "--name", "Smoke Owner"); |
|
| 17 | + | sp("init"); |
|
| 18 | + | sp("project", "add", "book", "-d", "the manuscript"); |
|
| 19 | + | sp("agent", "add", "claude-book", "--kind", "claude-code", "--project", "book", "--home", join(dir, "agents/claude-book"), "--yes"); |
|
| 20 | + | const serve = spawn(bin, ["serve", "--bind", "127.0.0.1:0"], { env, stdio: "ignore" }); |
|
| 21 | + | await new Promise((r) => setTimeout(r, 1500)); |
|
| 22 | + | ||
| 23 | + | const { Bridge } = await import("../out/smoke-bridge.mjs"); |
|
| 24 | + | const { Model } = await import("../out/smoke-model.mjs"); |
|
| 25 | + | const log = { append() {}, appendLine() {} }; |
|
| 26 | + | const bridge = new Bridge(log); |
|
| 27 | + | const model = new Model(bridge); |
|
| 28 | + | try { |
|
| 29 | + | await model.refresh(); |
|
| 30 | + | assert.equal(model.error, undefined); |
|
| 31 | + | assert.equal(model.brief.actor, "human"); |
|
| 32 | + | assert.equal(model.agents[0].name, "claude-book"); |
|
| 33 | + | assert.ok(model.channels.some((c) => c.name === "book")); |
|
| 34 | + | ||
| 35 | + | const seen = []; |
|
| 36 | + | model.onEvent((e) => seen.push(e.kind)); |
|
| 37 | + | model.start(); |
|
| 38 | + | await new Promise((r) => setTimeout(r, 800)); |
|
| 39 | + | await bridge.request("POST", "/api/v1/channels/book/messages", { body: "hello from the editor" }); |
|
| 40 | + | await bridge.request("PUT", "/api/v1/briefs", { project: "book", text: "Chapter 3 becomes chapter 5." }); |
|
| 41 | + | await new Promise((r) => setTimeout(r, 1200)); |
|
| 42 | + | assert.ok(seen.includes("message.posted"), `the stream delivered: ${seen}`); |
|
| 43 | + | assert.equal(model.briefs.projects[0]?.text, "Chapter 3 becomes chapter 5."); |
|
| 44 | + | assert.match(sp("brief", "show", "--project", "book"), /chapter 5/); |
|
| 45 | + | console.log("smoke: ok"); |
|
| 46 | + | } finally { |
|
| 47 | + | model.dispose(); |
|
| 48 | + | bridge.dispose(); |
|
| 49 | + | serve.kill("SIGTERM"); |
|
| 50 | + | await new Promise((r) => setTimeout(r, 1500)); |
|
| 51 | + | rmSync(dir, { recursive: true, force: true }); |
|
| 52 | + | } |
added test/vscode-stub.mjs +10 −0
| 1 | + | // The little of the vscode module the bridge and the model use, for the |
|
| 2 | + | // smoke test under plain Node. |
|
| 3 | + | export class EventEmitter { |
|
| 4 | + | constructor() { this.listeners = []; this.event = (l) => { this.listeners.push(l); return { dispose: () => {} }; }; } |
|
| 5 | + | fire(v) { for (const l of this.listeners) l(v); } |
|
| 6 | + | dispose() { this.listeners = []; } |
|
| 7 | + | } |
|
| 8 | + | export class Disposable { constructor(f) { this.f = f; } dispose() { this.f?.(); } } |
|
| 9 | + | export const workspace = { getConfiguration: () => ({ get: (k) => (k === "path" ? process.env.SPECTRA_BIN || "" : undefined) }), workspaceFolders: undefined }; |
|
| 10 | + | export const window = {}; |
added tsconfig.json +18 −0
| 1 | + | { |
|
| 2 | + | "compilerOptions": { |
|
| 3 | + | "target": "ES2022", |
|
| 4 | + | "module": "Node16", |
|
| 5 | + | "moduleResolution": "Node16", |
|
| 6 | + | "lib": ["ES2022"], |
|
| 7 | + | "strict": true, |
|
| 8 | + | "noImplicitReturns": true, |
|
| 9 | + | "noUnusedLocals": true, |
|
| 10 | + | "noFallthroughCasesInSwitch": true, |
|
| 11 | + | "esModuleInterop": true, |
|
| 12 | + | "skipLibCheck": true, |
|
| 13 | + | "sourceMap": true, |
|
| 14 | + | "outDir": "out", |
|
| 15 | + | "rootDir": "src" |
|
| 16 | + | }, |
|
| 17 | + | "include": ["src"] |
|
| 18 | + | } |
modified README.md +26 −0
| 1 | + | # Spectra for Visual Studio Code |
|
| 2 | + | ||
| 3 | + | Set up [Spectra](https://oxygenhub.io/andymccall/spectra), chat with your AI agents, and give them their briefs, without the command line. |
|
| 4 | + | ||
| 5 | + | - **Get started**: a walkthrough creates your identity, runs Spectra here or connects to one on a server (you confirm its key), registers this folder as a project, and adds an agent with its client (Claude Code, Kimi Code, Antigravity) configured. |
|
| 6 | + | - **Channels**: each project's channel, `#general` and your direct messages open as chat tabs; you post as yourself, `@name` mentions an agent. |
|
| 7 | + | - **Agents**: who is online, busy or offline, whose they are, and until when they may act for you; **Renew Agent** gives more time. |
|
| 8 | + | - **Inbox**: mentions and direct messages for you. |
|
| 9 | + | - **Briefs**: the overall brief and each project's open as documents; saving signs them into Spectra and every agent concerned is told on its next call. |
|
| 10 | + | - **Radar** and the **Constellation** open in your browser. |
|
| 11 | + | ||
| 12 | + | The extension never holds a key: it starts `spectra bridge`, which signs each request as you. See [docs/spec.md](docs/spec.md). |
|
| 13 | + | ||
| 14 | + | ## Building |
|
| 15 | + | ||
| 16 | + | ``` |
|
| 17 | + | npm install |
|
| 18 | + | npm run check # typecheck, build, unit tests |
|
| 19 | + | SPECTRA_BIN=~/.local/bin/spectra npm run smoke # against a real Spectra in a temporary directory |
|
| 20 | + | npm run package # spectra-vscode-0.1.0.vsix |
|
| 21 | + | code --install-extension spectra-vscode-0.1.0.vsix |
|
| 22 | + | ``` |
|
| 23 | + | ||
| 24 | + | ## Licence |
|
| 25 | + | ||
| 26 | + | MIT or Apache-2.0, at your option. |