For a final version the publish job now pushes branch release/qqq-<version>
to QRun-IO/homebrew-tap and QRun-IO/scoop-bucket with PACKAGES_TOKEN and
opens a pull request "qqq <version>" in each; a human merges. A prerelease,
or a refused push or pull request, attaches qqq.rb / qqq.json to the release
as before, and never fails the release. The logic moves to
scripts/publish-packaging.sh, with a DRY_RUN=1 path that clones and commits
locally but writes nothing remote.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Started from pwsh 7, the child Windows PowerShell 5.1 inherits PowerShell 7's
PSModulePath and cannot load Microsoft.PowerShell.Security, so
ConvertTo-SecureString fails and every DPAPI set/get reports "Windows
credential protection failed". The release workflow's Windows lane (steps in
pwsh) hit it in the existing DPAPI test. The keyring now spawns PowerShell
without PSModulePath; each shell computes its own.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v1.0.1-rc.1 passed scripts/release.mjs in CI, then build-binaries stopped:
Bun on the glibc container installs no OpenTUI musl native package
(@opentui/core-linux-x64-musl), and the script calls musl best effort. The
release ships linux x64/arm64, darwin x64/arm64 and windows x64; install.sh
says so on musl hosts and points at the npm package.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both assertions only matched X.Y.Z, so the release gate (which now runs the
TUI tests) failed on 1.0.1-rc.1. They accept a semver prerelease suffix.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The footer assertion only accepted X.Y.Z, so any -rc.N version failed it.
It now compares against CLI_VERSION exactly (port masked), which is stricter.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
.gitea/workflows/release.yaml replaces the orb publish and the GitHub release:
the scripts/release.mjs gate, Linux + darwin-x64 binaries in the container,
darwin-arm64 and windows-x64 built and smoke-tested on the native runners, a
draft Gitea release, SHA256SUMS checked against the published downloads, the
four packages to the QRun-IO Gitea npm registry (an existing version is
skipped, each package's own version picks next/latest), and for final versions
the Homebrew tap and Scoop bucket (pushed when PACKAGES_TOKEN can write them,
else the filled files are attached to the release).
release.mjs accepts its tag when it points at HEAD and prints the new path;
install.sh, update-packaging, the formula and the manifest use Gitea
downloads under v<version>; publishConfig points at the Gitea registry;
build.yaml now runs on branches only; the runbook describes the new path.
Versions: cli and core 1.0.1-rc.1, client 1.8.1-rc.1 (content changed since
1.0.0), conformance 1.6.0 unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Gitea CI runs npm ci, the generated-code drift check, build, test and lint
through KofTwentyTwo/ci-workflows node.yaml. Removes the retired qqq-orb
files (.circleci, .qqq-ci.yml and the copies seeded into the Homebrew tap)
and updates the docs that described them. Publishing is not ported.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The record screen opened the TagOverlay for any record whenever the
collaboration extension was present. On a table without the TAGS
feature, or for a role without the `tag` capability, the editor opened
and the save failed only when the server refused it.
openTags now calls getRecordFeatures for the record and opens the
overlay only when `features` contains TAGS and `capabilities.tag` is
true; otherwise it flashes "Tags are not enabled for this table." or
"Your role cannot tag this record.". The answer is ignored when the
screen has moved to another record meanwhile, and a request error is
flashed like any other.
Tests: the fake server answers /table/person/<id>/features from a
`recordTagPolicy` (enabled, allowed; reset with the rest of the
collaboration state), and enterprise.test.tsx presses t with the
feature off and with the capability denied and expects the message and
no overlay. The existing tag editor test keeps its defaults.
Carried locally by GG Forge since cli-v1.0.0.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
openInBrowser used `cmd /c start "" <url>` on Windows. cmd treats `&`
as a command separator even inside an argument that was passed as one
string, so an OAuth authorization URL was cut at its first query
parameter: the browser opened the authorization endpoint without
client_id, state or redirect_uri and the loopback callback never
arrived. Only `qqq login --browser` was affected; the printed URL
worked when pasted.
The URL is now never shell text. The opener runs PowerShell
(QQQ_POWERSHELL, else powershell.exe) with a fixed -EncodedCommand,
`Start-Process -FilePath $env:QQQ_BROWSER_URL`, and the URL in that
environment variable; the child is detached, stdio ignored and
windowsHide set so no console window flashes.
Test (win32 only): stubs child_process.spawn to capture the call,
checks the command is not cmd, the URL is in the environment and
windowsHide is set, then runs the captured encoded command in a real
PowerShell with Start-Process shadowed to print its -FilePath, and
expects the full URL back, `&` and all.
Carried locally by GG Forge since cli-v1.0.0.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
The Windows keyring (DpapiFileStore) interpolated the secret into the
PowerShell -Command string with JSON.stringify. A token was therefore
visible in the process list and in process-creation auditing for the
lifetime of the call, and PowerShell's own parsing could still mangle
quotes, backticks and $() inside the quoted value.
The secret now goes over stdin: the fixed command sets UTF-8 on both
console streams, reads stdin to the end, runs the protect or unprotect
step on that value and writes the result as compact JSON, which the
caller parses. Unprotect zeroes and frees the BSTR and disposes the
SecureString after reading. Any subprocess failure, non-zero exit or
non-string result is reported as "Windows credential protection failed;
check PowerShell and your Windows profile", never with the arguments.
Tests (win32 only): a round trip of a value with fake JSON, shell
syntax, Unicode and surrounding whitespace that must not appear in the
secrets file in clear, and a corrupt blob that must fail with the
redacted message. Run with QQQ_POWERSHELL=pwsh where Windows PowerShell
5.1 cannot load Microsoft.PowerShell.Security.
Carried locally by GG Forge since cli-v1.0.0.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
CI (npm run generate) failed with TS 7: openapi-typescript 7.13.0 uses
ts.factory from the TypeScript JS API, which the native 7 compiler lacks.
6.0.3 builds, generates an identical schema, and passes all suites.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Supersedes the Dependabot PRs for both. TypeScript 7 stops loading all
@types packages implicitly, so the base tsconfig names node; @types/node 26
rejects an undefined assertion message, fixed in one conformance test.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Two terminals of one user collapsed into one viewer. With collaboration
1.2.0 (qbit-collaboration #54) the snapshot says how many connections
each viewer has, and perConnection=true lists each connection.
- core: PresencePerson.connections, PresenceState.connections, an
opt-in perConnection that fills person.leases (mode, field, start,
this client's own lease marked self via the server's opaque id)
- TUI: the record strip, panel ("Admin x2 (you)", one line per
terminal) and Viewing now entry show counts; the record presence
joins with perConnection; the screen saver's Online tile counts
connections ("2 online - you x2")
- qqq collab presence: connections column, --per-connection rows
- regenerated the client from the collaboration 1.2.0 document,
read from the local feat/per-session-presence worktree until it
lands on develop (extension-sources.json says so)
Servers before 1.2.0 send no connections: every person counts once and
nothing changes on screen; tests cover both.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
qqq ui opens on a splash (Query animated beside the qqq wordmark, version line, boot status line). With several profiles and none named, the Servers picker asks which to connect to (last used preselected, asynchronous probes, n adds, Esc quits with a hint); one profile or --profile/QQQ_PROFILE/--server connects straight away; no profiles opens the add form. Sign-in failures stay on the splash with retry, choose another, add, or quit. --no-splash, QQQ_UI_NO_SPLASH=1 or ui.splash off restores the old sign-in screen first. Behaviour change: with several profiles and a default set, the TUI now asks instead of connecting to the default.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
⇧F1 Keys, ⇧F2 Servers, ⇧F3 SQL, ⇧F4 Bulk, ⇧F5 Metadata, ⇧F6 Views, ⇧F7 Copy, ⇧F8 Export, ⇧F9 History, ⇧F10 Assistant; keys that do not apply are dimmed and explain why. Two bar rows from 30 lines (:fkeys one|two|auto), or the Shift row swapped in for 2 s after a Shift+F press; clicks on the Shift row work. Key decoding covers xterm modifier forms, kitty codes and legacy VT220/rxvt numbering; a guarded stdin rewrite delivers Shift+F3, which OpenTUI 0.8.1 otherwise drops as a cursor-position reply.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Save as PDF… is the first printer-picker entry and saves through the download flow (file dialog or silent download folder, stamped names, overwrite confirmation); :print pdf; qqq print … --to-file [path]. On non-record, non-grid screens Ctrl+P asks Print what? (Content: panels as titled sections without sidebar, title bar, key lines or borders; Screen: the terminal as drawn). Every document carries the app header (instance, breadcrumb, server, user, time) and footer (client and server, filter/sort line, Page n of m), rendered by qbit-printing 2.2 (printingRenderToPdf).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The Online tile reads the session store's storedSession table first (active sessions and distinct users), falls back to you plus collaboration presence, never says 'nobody around' while signed in, and the saver and lock keep the presence lease. The Query mascot is regenerated at three sizes from the qrun.io/query hero SVG in the logo's indigo (packages/tui/scripts/gen-query-art.mjs), with landmark tests per size.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A shared markdown renderer in core handles entities, headings, lists, bold/code and pipe tables for assistant answers; links render as 'label ↗' numbered once per target (Tab, Enter, u); the CLI prints the same blocks as sub-tables. F12 toggles the Assistant panel on every screen, keeping the conversation, context and draft; Esc hides; Ctrl+G, :ask and a click on 12Ask do the same; the F-key bar gains 11/12Ask at 120+ columns.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
isBrokenPipe also covers ECONNRESET, ERR_STREAM_DESTROYED and ERR_STREAM_WRITE_AFTER_END; an uncaughtException handler exits 0 on a broken pipe; io.out/io.err go through safeWrite; the top-level catch exits 0 immediately. The tests close the read end before the first write and after the first chunk of a 5,000-record answer.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
docs/packaging carries the real sha256 sums from the cli-v1.0.0 release. scripts/update-packaging.mjs now parses shasum lines strictly, requires 64 lowercase hex and non-zero sums for every target, validates both files before writing anything, and exits 1 with nothing written otherwise; covered by scripts/update-packaging.test.mjs (npm test runs test:scripts).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
qqq 1.0.0: the command-line client and the full-screen terminal UI (qqq ui) for QQQ applications over the UI API (/qqq/v1), with @qrun-io/qqq-cli 1.0.0, @qrun-io/qqq-core 1.0.0, @qrun-io/qqq-ui-api-client 1.8.0 and @qrun-io/qqq-ui-api-conformance 1.6.0 going final together. Squash of the release/1.0.0 branch at 79ba68e (the branch is kept with the full signed history; the tags v1.8.0 and cli-v1.0.0 point at that commit, which is what the released binaries were built from). See CHANGELOG.md [1.0.0] and docs/release-notes/1.0.0.md.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- @qrun-io/qqq-ui-api-client: typed client generated (openapi-typescript +
openapi-fetch) from the merged UI API document; device-flow sign-in, refresh,
UiApiError (BasicErrorResponseV1), SSE reader
- @qrun-io/qqq-cli: qqq login/whoami/apps/tables/query/get/run/jobs/tail,
only through /qqq/v1 with Authorization: Bearer
- @qrun-io/qqq-ui-api-conformance: black-box suite + qqq-dev-idp
- scripts/merge-spec.mjs, Prism mock, CI and GitHub Packages publish workflows
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>