Replace npm install with the install.sh command on the homepage and docs

b6af05045fbb · AtlantisPleb · · parent 916ad0be2576

Replace npm install with the install.sh command on the homepage and docs

The npm package and the released binary answer to the same name, and
having both on one PATH broke git push machine-wide (issue #260). The
homepage, repository list, and documentation pages now point readers to
"curl -fsSL https://openagents.com/install.sh | sh" instead of
"npm i -g @openagentsinc/cli".

The coder page renders the install command in a composed text frame, and
the repository list and docs print the same command. Add an install path
test that asserts the one command is used everywhere a reader is told to
install.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By
Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

Deploy story

What this commit did to the running system — joined from the forge receipt chain, the part a commit page elsewhere cannot show.

pushed
by user · WAL seq 459 · 2026-08-26T18:10:13.756699Z
built
7 modules in 72.8 s
deployed
needs_rolling_replace · 7 modules on 0 nodes · push→live —

Changed files

  • modified assets/js/coder_copy.js
  • modified lib/openagents_web/live/coder_live.ex
  • modified lib/openagents_web/live/repository_index_live.ex
  • modified priv/docs/cli-api.md
  • modified priv/docs/cli-command-reference.md
  • modified priv/docs/clone-push-pull.md
  • modified priv/docs/create-repository.md
  • modified priv/docs/delete-repository.md
  • modified priv/docs/import-github.md
  • modified priv/docs/openagents-cli.md
  • modified priv/docs/signing-in.md
  • added test/openagents_web/install_path_test.exs
  • modified test/openagents_web/live/coder_live_test.exs
  • modified test/openagents_web/live/repository_live_test.exs

Diff

14 files changed, +258 -111

assets/js/coder_copy.js modified +13 -2

@@ -9,12 +9,23 @@ const CoderCopy = {

9 9
  },
10 10
11 11
  copy() {
12
    const text = this.el.dataset.copyText || "npm i -g @openagentsinc/cli"
12
    const text = this.el.dataset.copyText || "curl -fsSL https://openagents.com/install.sh | sh"
13 13
    const hintEl = document.getElementById("copy-hint")
14 14
15
    // The hint sits inside a box drawn in text, so the confirmation has to
16
    // occupy exactly the columns the command did or the frame loses a side
17
    // for two seconds. Centred by measurement rather than by hand-counted
18
    // spaces, which is what went stale the last time the command changed.
19
    const centre = (label, width) => {
20
      const pad = Math.max(0, width - label.length)
21
      const left = Math.floor(pad / 2)
22
      return " ".repeat(left) + label + " ".repeat(pad - left)
23
    }
24
15 25
    const flashCopied = () => {
16 26
      if (hintEl) {
17
        hintEl.textContent = "    copied to clipboard!     "
27
        const width = text.length + 2
28
        hintEl.textContent = centre("copied to clipboard!", width)
18 29
        clearTimeout(this._timeout)
19 30
        this._timeout = setTimeout(() => {
20 31
          hintEl.textContent = " " + text + " "
lib/openagents_web/live/coder_live.ex modified +85 -17

@@ -8,17 +8,99 @@ defmodule OpenAgentsWeb.CoderLive do

8 8
  that needs a second one needs a decision rather than an escape hatch. The
9 9
  colours come from the CSS variables that system already defines, with literal
10 10
  fallbacks, which is what the page was really relying on.
11
12
  The command it prints is the installer. `@openagentsinc/cli` is a different
13
  program answering to the same name, and this page was the last surface still
14
  handing it out.
15
16
  The frame is built rather than typed, and the command sets its width rather
17
  than being fitted into it. Typed, it was significant whitespace inside a
18
  template: the box was drawn for a 49-column interior, the command row came to
19
  35, and the box had been rendering with one short side since long before the
20
  command changed. `phx-no-format` does not save a typed box either — the HEEx
21
  formatter re-indents the text inside a multi-line tag whichever way that
22
  attribute is set, which puts four spaces down the left of every row but the
23
  first. Composed here, the padding is arithmetic against one interior width and
24
  the template holds a single interpolation.
11 25
  """
12 26
  use OpenAgentsWeb, :live_view
13 27
14
  @cmd "npm i -g @openagentsinc/cli"
28
  @cmd "curl -fsSL https://openagents.com/install.sh | sh"
29
30
  # Three spaces between the frame and the hint, and the hint carries one space
31
  # of its own on each side, which is the gap the copy confirmation replaces.
32
  @outer 3
33
  @interior @outer + String.length(@cmd) + 2 + @outer
34
35
  # The wordmark and the two noise rows were drawn for a 49-column interior.
36
  # Each keeps the padding it was drawn with and gains the difference.
37
  @drawn_for 49
38
  @side div(@interior - @drawn_for, 2)
39
40
  @wordmark [
41
    {5, "██████╗ ██████╗ ██████╗ ███████╗██████╗", 5},
42
    {4, "██╔════╝██╔═══██╗██╔══██╗██╔════╝██╔══██╗", 4},
43
    {4, "██║     ██║   ██║██║  ██║█████╗  ██████╔╝", 4},
44
    {4, "██║     ██║   ██║██║  ██║██╔══╝  ██╔══██╗", 4},
45
    {4, "╚██████╗╚██████╔╝██████╔╝███████╗██║  ██║", 4},
46
    {5, "╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝╚═╝  ╚═╝", 4}
47
  ]
48
49
  @noise_top "000111011001000110000101001000101000010000101100010110010"
50
  @noise_bottom "111110001011000001010000000000001000100110001110101101001"
51
52
  @dim ~s|<span style="color: var(--foreground2, #666);">|
53
  @cyan ~s|<span class="font-bold text-cyan-400" style="color: #56b6c2;">|
54
  @close "</span>"
55
56
  @title " OpenAgents "
57
  @rule @interior - String.length(@title)
58
59
  @frame Enum.join(
60
           [
61
             @dim <>
62
               "┌" <>
63
               String.duplicate("─", div(@rule, 2)) <>
64
               @close <>
65
               @title <>
66
               @dim <> String.duplicate("─", @rule - div(@rule, 2)) <> "┐" <> @close,
67
             @dim <> "│" <> @close <> @noise_top <> @dim <> "│" <> @close,
68
             @dim <> "│" <> String.duplicate(" ", @interior) <> "│" <> @close
69
           ] ++
70
             Enum.map(@wordmark, fn {lead, glyphs, trail} ->
71
               @dim <>
72
                 "│" <>
73
                 @close <>
74
                 String.duplicate(" ", lead + @side) <>
75
                 @cyan <>
76
                 glyphs <>
77
                 @close <>
78
                 String.duplicate(" ", trail + @side) <> @dim <> "│" <> @close
79
             end) ++
80
             [
81
               @dim <> "│" <> String.duplicate(" ", @interior) <> "│" <> @close,
82
               @dim <>
83
                 "│" <>
84
                 @close <>
85
                 String.duplicate(" ", @outer) <>
86
                 ~s|<span id="copy-hint" style="color: var(--foreground1, #ccc);"> | <>
87
                 @cmd <>
88
                 " " <>
89
                 @close <>
90
                 String.duplicate(" ", @outer) <> @dim <> "│" <> @close,
91
               @dim <> "│" <> @close <> @noise_bottom <> @dim <> "│" <> @close,
92
               @dim <> "└" <> String.duplicate("─", @interior) <> "┘" <> @close
93
             ],
94
           "\n"
95
         )
15 96
16 97
  @impl true
17 98
  def mount(_params, _session, socket) do
18 99
    {:ok,
19 100
     socket
20 101
     |> assign(:page_title, "OpenAgents Coder")
21
     |> assign(:cmd, @cmd)}
102
     |> assign(:cmd, @cmd)
103
     |> assign(:frame, @frame)}
22 104
  end
23 105
24 106
  @impl true

@@ -38,21 +120,7 @@ defmodule OpenAgentsWeb.CoderLive do

38 120
        <pre
39 121
          class="font-mono text-xs sm:text-sm md:text-base leading-none select-none pointer-events-none"
40 122
          style="margin: 0; color: var(--foreground0, #fff); -webkit-user-select: none; user-select: none;"
41
        >
42
          <span style="color: var(--foreground2, #666);">┌──────────────────</span> OpenAgents <span style="color: var(--foreground2, #666);">───────────────────┐</span>
43
          <span style="color: var(--foreground2, #666);">│</span>0001110110010001100001010010001010000100001011000<span style="color: var(--foreground2, #666);">│</span>
44
          <span style="color: var(--foreground2, #666);">│                                                 │</span>
45
          <span style="color: var(--foreground2, #666);">│</span>     <span class="font-bold text-cyan-400" style="color: #56b6c2;">██████╗ ██████╗ ██████╗ ███████╗██████╗</span>     <span style="color: var(--foreground2, #666);">│</span>
46
          <span style="color: var(--foreground2, #666);">│</span>    <span class="font-bold text-cyan-400" style="color: #56b6c2;">██╔════╝██╔═══██╗██╔══██╗██╔════╝██╔══██╗</span>    <span style="color: var(--foreground2, #666);">│</span>
47
          <span style="color: var(--foreground2, #666);">│</span>    <span class="font-bold text-cyan-400" style="color: #56b6c2;">██║     ██║   ██║██║  ██║█████╗  ██████╔╝</span>    <span style="color: var(--foreground2, #666);">│</span>
48
          <span style="color: var(--foreground2, #666);">│</span>    <span class="font-bold text-cyan-400" style="color: #56b6c2;">██║     ██║   ██║██║  ██║██╔══╝  ██╔══██╗</span>    <span style="color: var(--foreground2, #666);">│</span>
49
          <span style="color: var(--foreground2, #666);">│</span>    <span class="font-bold text-cyan-400" style="color: #56b6c2;">╚██████╗╚██████╔╝██████╔╝███████╗██║  ██║</span>    <span style="color: var(--foreground2, #666);">│</span>
50
          <span style="color: var(--foreground2, #666);">│</span>     <span class="font-bold text-cyan-400" style="color: #56b6c2;">╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝╚═╝  ╚═╝</span>    <span style="color: var(--foreground2, #666);">│</span>
51
          <span style="color: var(--foreground2, #666);">│                                                 │</span>
52
          <span style="color: var(--foreground2, #666);">│</span>   <span id="copy-hint" style="color: var(--foreground1, #ccc);"> <%= @cmd %> </span>   <span style="color: var(--foreground2, #666);">│</span>
53
          <span style="color: var(--foreground2, #666);">│</span>1111100010110000010100000000000010001001100011101<span style="color: var(--foreground2, #666);">│</span>
54
          <span style="color: var(--foreground2, #666);">└─────────────────────────────────────────────────┘</span>
55
        </pre>
123
        >{raw(@frame)}</pre>
56 124
      </div>
57 125
    </div>
58 126
    """
lib/openagents_web/live/repository_index_live.ex modified +1 -1

@@ -29,7 +29,7 @@ defmodule OpenAgentsWeb.RepositoryIndexLive do

29 29
  # they are interpolated rather than written into the template, where the HEEx
30 30
  # parser would read them as tags.
31 31
  @cli_steps [
32
    %{command: "npm i -g @openagentsinc/cli", note: "install"},
32
    %{command: "curl -fsSL https://openagents.com/install.sh | sh", note: "install"},
33 33
    %{command: "openagents auth login", note: "sign in"},
34 34
    %{command: "openagents repo create <name>", note: "create"},
35 35
    %{command: "openagents repo clone <owner>/<repo>", note: "clone"},
priv/docs/cli-api.md modified +1 -1

@@ -13,7 +13,7 @@ for those resources.

13 13
Install the CLI and sign in to the profile you intend to use:
14 14
15 15
```sh
16
npm install --global @openagentsinc/cli@latest
16
curl -fsSL https://openagents.com/install.sh | sh
17 17
openagents auth login
18 18
openagents auth status
19 19
```
priv/docs/cli-command-reference.md modified +17 -10

@@ -8,9 +8,12 @@ named command yet.

8 8
openagents <subcommand> [flags]
9 9
```
10 10
11
`openagents`, `coder`, and `oa` are three names for one binary, so every
12
command on this page runs under any of them. Run it bare and it starts a coder
13
session instead. See [Install the CLI](/docs/install-cli).
14
11 15
Run `openagents <command> --help` for the reference that matches your installed
12
version. When you use `npx`, replace the `openagents` prefix with
13
`npx --yes @openagentsinc/cli@latest`.
16
version.
14 17
15 18
## Use global flags
16 19

@@ -28,7 +31,7 @@ Place shared flags before the subcommand:

28 31
29 32
```sh
30 33
openagents --profile staging --json repo list
31
npx --yes @openagentsinc/cli@latest --profile staging --json repo list
34
oa --profile staging --json repo list
32 35
```
33 36
34 37
Setting `NO_COLOR` also disables ANSI output.

@@ -50,9 +53,6 @@ Setting `NO_COLOR` also disables ANSI output.

50 53
`auth git-credential` is an internal Git-helper endpoint. Do not invoke it
51 54
directly.
52 55
53
Do not run either `auth setup-git` form through `npx`. Install the CLI globally
54
before you save a persistent helper configuration.
55
56 56
## Create a repository
57 57
58 58
```text

@@ -287,10 +287,17 @@ and terminates its child Git process.

287 287
## Know which commands are unavailable
288 288
289 289
This release does not provide named `issue` or `project` commands, repository
290
mirroring, pull-request commands, ruleset commands, SSH-key commands, or a
291
self-update command. Use `openagents api` for the implemented Issues and
292
Projects routes, and use only commands shown by the installed version's
293
`--help` output.
290
mirroring, pull-request commands, ruleset commands, or SSH-key commands. Use
291
`openagents api` for the implemented Issues and Projects routes, and use only
292
commands shown by the installed version's `--help` output.
293
294
## Update the binary
295
296
`openagents update` resolves the release channel, compares the version it names
297
against the running binary, and replaces the binary in place when they differ.
298
`openagents self-update` is the same command. See
299
[Install the CLI](/docs/install-cli) for channels, pinned versions, and what
300
the update verifies.
294 301
295 302
## Next steps
296 303
priv/docs/clone-push-pull.md modified +5 -12

@@ -16,12 +16,6 @@ Choose a destination directory:

16 16
openagents repo clone OWNER/REPOSITORY ./local-directory
17 17
```
18 18
19
For a one-time clone without a global installation:
20
21
```sh
22
npx --yes @openagentsinc/cli@latest repo clone OWNER/REPOSITORY
23
```
24
25 19
The command retrieves the server-provided clone URL and invokes Git without
26 20
putting the token in the URL or process arguments. It scopes the OpenAgents
27 21
credential helper to the selected API origin for that clone process.

@@ -37,18 +31,17 @@ git clone https://openagents.com/OWNER/REPOSITORY.git

37 31
Public repositories support anonymous clone and fetch. Private repositories
38 32
require an authorized credential.
39 33
40
Before you use standard Git with a private repository, install the CLI globally
41
and configure the helper in the worktree:
34
Before you use standard Git with a private repository, configure the helper in
35
the worktree:
42 36
43 37
```sh
44
npm install --global @openagentsinc/cli
45 38
cd existing-worktree
46 39
openagents auth setup-git --local
47 40
```
48 41
49
Do not run `auth setup-git` through `npx`. The saved helper calls
50
`openagents`, and the temporary `npx` executable is unavailable after that
51
command exits.
42
The helper configuration refers to the `openagents` executable by path, so
43
install it where it stays: the installer links it into `~/.openagents/bin` and
44
puts that directory on your `PATH`.
52 45
53 46
Use global setup only when you want every local repository to use the helper
54 47
for the selected OpenAgents origin:
priv/docs/create-repository.md modified +1 -9

@@ -46,13 +46,6 @@ repository continues provisioning on the server. While it waits, the CLI

46 46
writes the current lifecycle state, elapsed time, and a five-second heartbeat
47 47
to standard error.
48 48
49
For one command without a global install, prefix the same arguments with the
50
package name:
51
52
```sh
53
npx --yes @openagentsinc/cli@latest repo create my-project
54
```
55
56 49
## Attach an existing local project
57 50
58 51
Create the remote repository and attach it to an existing Git worktree:

@@ -77,10 +70,9 @@ remote attachment fails, the remote repository still exists.

77 70
78 71
## Push the first commit
79 72
80
Install the CLI globally before configuring persistent Git authentication:
73
Configure persistent Git authentication in the worktree, then push:
81 74
82 75
```sh
83
npm install --global @openagentsinc/cli
84 76
cd existing-worktree
85 77
openagents auth setup-git --local
86 78
git push -u origin HEAD
priv/docs/delete-repository.md modified -6

@@ -24,12 +24,6 @@ Pass `--yes` as explicit confirmation:

24 24
openagents repo delete OWNER/REPOSITORY --yes
25 25
```
26 26
27
You can run the same one-time command through `npx`:
28
29
```sh
30
npx --yes @openagentsinc/cli@latest repo delete OWNER/REPOSITORY --yes
31
```
32
33 27
The command also supports `--repo OWNER/REPOSITORY`. When you omit the name,
34 28
the CLI infers it from an exact OpenAgents `origin` remote.
35 29
priv/docs/import-github.md modified +12 -17

@@ -71,38 +71,33 @@ time, and a five-second heartbeat to standard error while it waits. Pass

71 71
`--wait-timeout 0` to return after the server accepts the durable import.
72 72
A client timeout does not cancel the server-side import.
73 73
74
## Import once with npx
74
## Pin a version for a qualification run
75 75
76
Run the same import without a global installation:
76
A release channel is a pointer that moves, so an import you run today and the
77
same import next month can run under different builds. Install one exact
78
version when a qualification run has to stay reproducible:
77 79
78 80
```sh
79
npx --yes @openagentsinc/cli@latest repo import OWNER/REPOSITORY
80
```
81
82
Pin the package version for a reproducible qualification run:
83
84
```sh
85
npx --yes @openagentsinc/cli@0.2.1 \
86
  --profile staging \
81
curl -fsSL https://openagents.com/install.sh | sh -s 0.0.2
82
openagents --profile staging \
87 83
  repo import OWNER/REPOSITORY \
88 84
  --private \
89 85
  --wait-timeout 300
90 86
```
91 87
92
You can use `npx` for the import and the CLI-managed clone. Install the CLI
93
globally before you run `auth setup-git`, because a persistent Git helper
94
cannot call the temporary executable after `npx` exits.
88
## Import from a headless agent
95 89
96
In a headless agent process, start the resumable login before the import:
90
In a headless process, start the resumable login before the import:
97 91
98 92
```sh
99
npx --yes @openagentsinc/cli@latest --json auth login
93
openagents --json auth login
100 94
# The agent shows you the URL and code. After you approve the request:
101
npx --yes @openagentsinc/cli@latest --json auth login --resume
95
openagents --json auth login --resume
102 96
```
103 97
104 98
The first command returns immediately, so the agent does not need streaming
105
shell output.
99
shell output. The agent never receives your GitHub credential or the issued
100
OpenAgents token.
106 101
107 102
## Import a large repository
108 103
priv/docs/openagents-cli.md modified +20 -28

@@ -1,9 +1,14 @@

1 1
# The OpenAgents CLI
2 2
3
`openagents` is the OpenAgents command-line interface, distributed on npm as
4
`@openagentsinc/cli`. It signs you in, configures Git authentication, manages
5
[repositories](/docs/repositories) from a terminal, and calls implemented API
6
routes such as Issues and Projects.
3
`openagents` is the OpenAgents command-line interface. It signs you in,
4
configures Git authentication, manages [repositories](/docs/repositories) from
5
a terminal, and calls implemented API routes such as Issues and Projects.
6
7
It is one native binary, installed under three names. `openagents` and `coder`
8
are two doors onto the same program, and `oa` is the short one. Run it bare and
9
it starts a coder session; give it a command and it runs that command. This
10
page covers the command surface. See [Install the CLI](/docs/install-cli) for
11
the installer, release channels, and where the binary lands.
7 12
8 13
## What the CLI does
9 14

@@ -15,46 +20,33 @@ The current release lets you:

15 20
- Install a Git credential helper scoped to the selected OpenAgents origin.
16 21
- Create, import, list, view, clone, and delete repositories.
17 22
- Wait on durable provisioning and import state machines.
23
- Read and post on the [forum](/docs/forum) from a terminal.
18 24
- Call Issues, Projects, and other API routes with `openagents api`.
25
- Replace itself with a newer build with `openagents update`.
19 26
- Use JSON output and stable exit codes in scripts and agents.
20 27
21 28
The CLI does not provide named `issue` or `project` commands, pull request
22
commands, continuous GitHub mirroring, SSH transport, rulesets, or a
23
self-update command. Use `openagents api` for the routes that have no named
24
command yet.
25
26
## Choose how to run the CLI
29
commands, continuous GitHub mirroring, SSH transport, or rulesets. Use
30
`openagents api` for the routes that have no named command yet.
27 31
28
Install the CLI globally when you use it regularly or when you want to
29
configure Git authentication that remains available after the current command:
32
## Install and check
30 33
31 34
```sh
32
npm install --global @openagentsinc/cli
35
curl -fsSL https://openagents.com/install.sh | sh
33 36
openagents --help
34 37
```
35 38
36
Use `npx` for one command without a global installation:
37
38
```sh
39
npx --yes @openagentsinc/cli@latest --help
40
npx --yes @openagentsinc/cli@latest repo list
41
```
42
43
Do not configure a persistent Git credential helper through `npx`. The helper
44
configuration refers to the `openagents` executable, but the temporary `npx`
45
executable disappears when the command ends. Install the CLI globally before
46
you run `openagents auth setup-git --local` or
47
`openagents auth setup-git --global --yes`.
48
49
See [Install the CLI](/docs/install-cli) for authentication, profiles,
50
configuration, and the complete `npx` guidance.
39
Open a new shell first if the installer has just added `~/.openagents/bin` to
40
your `PATH`. `openagents --version` reports the installed build, and
41
`openagents update --check` says what the channel currently names without
42
installing anything.
51 43
52 44
## Follow a common terminal workflow
53 45
54 46
1. Install and sign in:
55 47
56 48
   ```sh
57
   npm install --global @openagentsinc/cli
49
   curl -fsSL https://openagents.com/install.sh | sh
58 50
   openagents auth login
59 51
   openagents auth status
60 52
   ```
priv/docs/signing-in.md modified +2 -1

@@ -28,7 +28,8 @@ the grant removed entirely.

28 28
Run `openagents auth login` to start a browser-assisted device flow. You review
29 29
and approve that request while signed in to the site. The CLI receives an
30 30
OpenAgents API token, not your GitHub token. See [Install the
31
CLI](/docs/install-cli) for npm and `npx` instructions.
31
CLI](/docs/install-cli) for the installer, release channels, and where the
32
binary lands.
32 33
33 34
In a headless process, the command returns the authorization URL and user code
34 35
immediately. After you approve the request, the agent runs `openagents auth
test/openagents_web/install_path_test.exs added +71

@@ -0,0 +1,71 @@

1
defmodule OpenAgentsWeb.InstallPathTest do
2
  @moduledoc """
3
  One way to install, said the same way everywhere a reader is told to.
4
5
  `@openagentsinc/cli` is a different program answering to the same name as the
6
  binary this site serves, and having both on one `PATH` broke `git push`
7
  machine-wide. The install command moved to `install.sh`, but nine
8
  documentation pages and two application surfaces went on handing out the npm
9
  package for long enough that the homepage ended up contradicting the
10
  documentation it links to (issue #260).
11
12
  These assertions read what a reader is actually shown, not what the source
13
  says about it: the Markdown that becomes a documentation page, and the HTML
14
  the two install-bearing LiveViews render. A module comment may name the npm
15
  package -- explaining why it is gone is not offering it -- and this test must
16
  not turn that into a failure.
17
  """
18
19
  use OpenAgentsWeb.ConnCase, async: true
20
21
  @command "curl -fsSL https://openagents.com/install.sh | sh"
22
23
  # `npm` alone is too broad: several moduledocs use "pnpm, not npm" as the
24
  # worked example of a memory whose words do not appear in the request it has
25
  # to reach. These are the forms that install something.
26
  @offers_npm ~r{npm i -g|npm install|npx |@openagentsinc/cli}
27
28
  test "no documentation page offers npm as a way to install" do
29
    offenders =
30
      for path <- Path.wildcard("priv/docs/**/*.md"),
31
          matches = Regex.scan(@offers_npm, File.read!(path)),
32
          matches != [],
33
          do: {Path.relative_to_cwd(path), matches |> List.flatten() |> Enum.uniq()}
34
35
    assert offenders == [],
36
           """
37
           These documentation pages still offer the npm package:
38
39
           #{Enum.map_join(offenders, "\n", fn {path, found} -> "  #{path}: #{Enum.join(found, ", ")}" end)}
40
41
           The one install path is `#{@command}`. See /docs/install-cli.
42
           """
43
  end
44
45
  test "at least one documentation page prints the installer, so this test can fail" do
46
    # Without this, deleting every mention of installing anything would pass the
47
    # assertion above while leaving a reader with no way in.
48
    printed? =
49
      Enum.any?(Path.wildcard("priv/docs/**/*.md"), &String.contains?(File.read!(&1), @command))
50
51
    assert printed?, "no documentation page prints #{@command}"
52
  end
53
54
  test "the coder page prints the installer and not the npm package", %{conn: conn} do
55
    html = conn |> get(~p"/coder") |> html_response(200)
56
57
    assert html =~ @command
58
    refute html =~ @offers_npm
59
  end
60
61
  test "the repository list prints the installer and not the npm package", %{conn: conn} do
62
    html =
63
      conn
64
      |> log_in_github_user("install-path-repository-list")
65
      |> get(~p"/repositories")
66
      |> html_response(200)
67
68
    assert html =~ @command
69
    refute html =~ @offers_npm
70
  end
71
end
test/openagents_web/live/coder_live_test.exs modified +28 -5

@@ -10,12 +10,35 @@ defmodule OpenAgentsWeb.CoderLiveTest do

10 10
  test "the page offers the published install command", %{conn: conn} do
11 11
    {:ok, _view, html} = live(conn, ~p"/coder")
12 12
13
    assert html =~ "npm i -g @openagentsinc/cli"
13
    assert html =~ "curl -fsSL https://openagents.com/install.sh | sh"
14 14
15
    # The binary release was withdrawn — artifacts and every channel pointer
16
    # deleted — so the curl command can only fail, and the build it installed
17
    # printed fabricated data. Offering it is worse than offering nothing.
18
    refute html =~ "install.sh"
15
    # This assertion used to run the other way. The binary release had been
16
    # withdrawn — artifacts and every channel pointer deleted — so the curl
17
    # command could only fail, and `npm i -g @openagentsinc/cli` was what the
18
    # page handed out instead. The release is back, `install.sh` is what the
19
    # documentation and the homepage publish, and the npm package is a
20
    # different program answering to the same name: two of them on one `PATH`
21
    # broke `git push` machine-wide (issue #260).
22
    refute html =~ "@openagentsinc/cli"
23
  end
24
25
  test "every row of the frame is the same width", %{conn: conn} do
26
    {:ok, _view, html} = live(conn, ~p"/coder")
27
28
    # The box is drawn in text, so a row that does not match the others shows
29
    # as a broken side. It was typed for a 49-column interior while the command
30
    # row came to 35, and nothing noticed until the command changed length.
31
    # It is composed from the command now, and this is what holds that.
32
    [frame] = Regex.run(~r{<pre[^>]*>(.*?)</pre>}s, html, capture: :all_but_first)
33
34
    widths =
35
      frame
36
      |> String.replace(~r{<[^>]+>}, "")
37
      |> String.split("\n")
38
      |> Enum.map(&String.length/1)
39
      |> Enum.uniq()
40
41
    assert length(widths) == 1, "frame rows differ in width: #{inspect(widths)}"
19 42
  end
20 43
21 44
  test "the page adds no stylesheet of its own", %{conn: conn} do
test/openagents_web/live/repository_live_test.exs modified +2 -2

@@ -213,8 +213,8 @@ defmodule OpenAgentsWeb.RepositoryLiveTest do

213 213
    assert has_element?(view, "#repository-cli")
214 214
    assert has_element?(view, "#repository-cli-copy-0")
215 215
    assert has_element?(view, ~s(a[href="/docs/openagents-cli"]))
216
    assert html =~ "npm i -g @openagentsinc/cli"
217
    refute html =~ "install.sh"
216
    assert html =~ "curl -fsSL https://openagents.com/install.sh | sh"
217
    refute html =~ "@openagentsinc/cli"
218 218
    assert html =~ "openagents auth login"
219 219
    assert html =~ "openagents auth setup-git --local"
220 220
    assert html =~ "/&lt;owner&gt;/&lt;name&gt;.git"

This page updates live while a promote is in flight · changelog