Skip to content

Superself 0.13.1: a refusal from the wrong server is one sentence

Latest

Choose a tag to compare

@tonite31 tonite31 released this 02 Sep 04:47
· 9 commits to main since this release
770488c

Superself 0.13.1 is one bug fix. In 0.13.0, a --cloud command pointed at a host that does not serve the workspace API printed error: followed by up to 2 KB of that host's own web page — an HTML 404, a proxy banner, a CDN challenge — and never said which host, which path or which status it came from. You could not tell "the server is not there" from "I am signed out" from "the API changed", and there was nothing in the output to act on. You now get one sentence naming the server, the status, the media type, the size of the body and the path, and the body itself is never quoted.

Nothing else changed. No verb, no flag, no file format. Every command below was run against the CLI built from this branch, and every text block is pasted from that run.

A non-JSON answer is never quoted, and the refusal names the host

Against a stub HTTP server on loopback answering 404 text/html with a page:

$ SUPERSELF_DEV=1 SUPERSELF_API_BASE=http://127.0.0.1:43161 self login --no-open --timeout 1
error: the server at 127.0.0.1:43161 answered 404 with text/html (142 bytes) at /api/device/start, which is not a workspace API response
    --api-base <url> or SUPERSELF_API_BASE chooses the server
(exit 1)

Against one answering 502 text/plain, the same sentence about a different answer — and the banner it sent is not echoed either:

$ SUPERSELF_DEV=1 SUPERSELF_API_BASE=http://127.0.0.1:42515 self login --no-open --timeout 1
error: the server at 127.0.0.1:42515 answered 502 with text/plain (11 bytes) at /api/device/start, which is not a workspace API response
    --api-base <url> or SUPERSELF_API_BASE chooses the server
(exit 1)

(SUPERSELF_DEV=1 is what allows a loopback http base at all; a workspace base is https only. It is on these lines because the stub is a plain HTTP server, and for no other reason.)

Under --json the same answer is fields rather than prose, and the body is absent from those too:

$ SUPERSELF_DEV=1 SUPERSELF_API_BASE=http://127.0.0.1:43161 self login --no-open --timeout 1 --json
{"error":{"code":"device_login_failed","message":"the server at 127.0.0.1:43161 answered 404 with text/html (142 bytes) at /api/device/start, which is not a workspace API response","status":404,"content_type":"text/html","bytes":142,"host":"127.0.0.1:43161","path":"/api/device/start","hint":"--api-base <url> or SUPERSELF_API_BASE chooses the server"}}
(exit 1)

A JSON error body is untouched. When the answer is this API's envelope, the message is still the server's own, exactly as before — against a stub answering 400 {"code":"device_flow_disabled","message":"device login is turned off for this workspace"}:

$ SUPERSELF_DEV=1 SUPERSELF_API_BASE=http://127.0.0.1:46263 self login --no-open --timeout 1
error: device login is turned off for this workspace
(exit 1)

That is the whole of the change a person sees. The runs above are self login's device start; the sentence itself is written in one function that every rail refusal path reads — device poll, refresh, the terminal 401 and every ordinary rail call included — so the same server is described the same way whichever command reached it.

Bounds and refusals

  • The content type is rendered as the media type alone. text/html; charset=utf-8 reads as text/html: the parameters are dropped, the value is sanitized like any other server string, and it is capped short, because a header of any length must not become the sentence.

  • The byte count is of the body actually received, under the response cap the rail already enforces — not a number the server claimed in a header.

  • The host is the URL's host and the path is its pathname, never the whole URL. A base can carry a query string, and a query string can carry a token; "where did the request go" is answered without one:

    $ SUPERSELF_DEV=1 SUPERSELF_API_BASE="http://127.0.0.1:43161/?token=not-a-real-token" self login --no-open --timeout 1
    error: the server at 127.0.0.1:43161 answered 404 with text/html (142 bytes) at /, which is not a workspace API response
        --api-base <url> or SUPERSELF_API_BASE chooses the server
    (exit 1)
    
  • JSON that is not an object is described rather than read. A body of "oops" parses, but nothing can be read off it; in 0.13.0 reading body.details from it was a TypeError rather than a refusal. It is now the same sentence, and the answer is what it is — application/json, 6 bytes:

    $ SUPERSELF_DEV=1 SUPERSELF_API_BASE=http://127.0.0.1:43309 self login --no-open --timeout 1
    error: the server at 127.0.0.1:43309 answered 400 with application/json (6 bytes) at /api/device/start, which is not a workspace API response
        --api-base <url> or SUPERSELF_API_BASE chooses the server
    (exit 1)
    

    An empty body on a non-2xx status is described the same way, and in 0.13.0 it was the same TypeError:

    $ SUPERSELF_DEV=1 SUPERSELF_API_BASE=http://127.0.0.1:43391 self login --no-open --timeout 1
    error: the server at 127.0.0.1:43391 answered 404 with an empty body (0 bytes) at /api/device/start, which is not a workspace API response
        --api-base <url> or SUPERSELF_API_BASE chooses the server
    (exit 1)
    
  • The test harness cannot reach a live host. This defect was found because a documentation run went to the default base. Every CLI the suite spawns is now pinned to a closed loopback — https://127.0.0.1:9 — unless the case sets a base of its own, so a test that names no base can no longer reach anything real.

Mixed versions

No store format changed in this release: no new file, no new field, no migration. A 0.13.0 CLI and a 0.13.1 CLI read the same store identically, and the only difference between them is the refusal text on an answer that is not this API's. Read off a real pair rather than assumed — the v0.13.0 tree was checked out into its own worktree, installed and built, and pointed at a git-backed store this release's verbs wrote:

$ self status     # 0.13.1
demo — goal: the refusal from a wrong server is one sentence
work: 0 active, 0 blocked, 1 next, 0 done
objectives: 0 open, 0 of 0 milestones reached, 0 of 0 exit criteria covered
waiting on you: 0
unshipped: nothing waiting to ship
health: ok
(exit 0)

The 0.13.0 CLI printed those same bytes, exit code included. Each read verb was run on both CLIs and the answers compared byte for byte with their exit codes — nine of nine identical:

IDENTICAL  self status
IDENTICAL  self context
IDENTICAL  self work
IDENTICAL  self state
IDENTICAL  self log
IDENTICAL  self artifact list
IDENTICAL  self objective
IDENTICAL  self milestone
IDENTICAL  self lang

Nothing refused on either side, and there is no row to write.

Two packages from one tag

apps/fold did not change in this release, and its version is raised to 0.1.1 anyway: one tag publishes both packages, and the gate refuses a version the registry already holds — @superself/fold@0.1.0 went out with v0.13.0. The CLI pins that version exactly, "@superself/fold": "0.1.1", in the same commit that raises it.

Upgrade after publication

After the v0.13.1 release workflow finishes, install the published package:

npm install -g superself@0.13.1