Skip to content

Commit 91c57af

Browse files
authored
install: open the dashboard sign-in page from the CLI (#90)
Onboarding starts on the web, since the GitHub App install lives there. Give callers one command that gets them to the right page instead of a URL they have to be told. The link is built from the active host's app base, so a beta or self-hosted CLI does not send its user to prod.
1 parent 0224c7c commit 91c57af

7 files changed

Lines changed: 47 additions & 5 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ skills:
4040
## Usage
4141
4242
```sh
43+
agent install # open the dashboard sign-in page, where you install Ellipsis
4344
agent login # device-code auth against the active host
4445
agent logout # remove stored credentials (--all for every host)
4546
agent me # show the current credential's identity

skills/ellipsis/SKILL.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,15 @@ sandbox image actually builds, then deploy. The steps below are that flow.
4545

4646
```sh
4747
brew install ellipsis-dev/cli/agent
48+
agent install # opens the dashboard sign-in page, where you install Ellipsis
4849
agent login # device-code flow tied to your GitHub identity
4950
agent ping # confirms the CLI can authenticate and reach the API
5051
```
5152

5253
Sessions clone real repositories, so the GitHub App must be installed on
53-
the account (app.ellipsis.dev walks through it). In CI or another headless
54-
environment, export an API key as `ELLIPSIS_API_TOKEN` instead of logging
55-
in.
54+
the account. `agent install` opens the page that walks through it. In CI or
55+
another headless environment, export an API key as `ELLIPSIS_API_TOKEN`
56+
instead of logging in.
5657

5758
2. See a session run before configuring anything:
5859

src/cli.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Command } from 'commander'
2+
import { registerInstall } from './commands/install'
23
import { registerLogin } from './commands/login'
34
import { registerHost } from './commands/host'
45
import { registerMe } from './commands/me'
@@ -33,6 +34,7 @@ program
3334
// rendering (sorted, alias-free, grouped at the top level).
3435
configureCliHelp(program)
3536

37+
registerInstall(program)
3638
registerLogin(program)
3739
registerHost(program)
3840
registerMe(program)

src/commands/install.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import type { Command } from 'commander'
2+
import { resolveAppBase } from '../lib/config'
3+
import { openBrowser } from '../lib/auth'
4+
import { appLoginUrl } from '../lib/urls'
5+
6+
export function registerInstall(program: Command): void {
7+
program
8+
.command('install')
9+
.description('Open the dashboard sign-in page, where you install Ellipsis')
10+
.option('--no-browser', 'print the URL without opening a browser (for headless or SSH)')
11+
.action((opts: { browser?: boolean }) => {
12+
const url = appLoginUrl(resolveAppBase())
13+
console.log('To install Ellipsis, open this URL and sign in:')
14+
console.log(` ${url}`)
15+
if (opts.browser !== false) {
16+
openBrowser(url)
17+
}
18+
})
19+
}

src/lib/help.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const TOP_LEVEL_GROUPS: ReadonlyArray<{ title: string; commands: readonly string
2323
{ title: 'Platform', commands: ['variable', 'asset', 'hook'] },
2424
{ title: 'Integrations', commands: ['integration', 'github', 'slack', 'linear', 'sentry'] },
2525
{ title: 'Spend', commands: ['budget', 'usage', 'analytics'] },
26-
{ title: 'Account', commands: ['login', 'logout', 'me', 'host', 'ping'] },
26+
{ title: 'Account', commands: ['install', 'login', 'logout', 'me', 'host', 'ping'] },
2727
]
2828

2929
export function configureCliHelp(program: Command): void {

src/lib/urls.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ export function cliAuthUrl(appBase: string, userCode: string): string {
2525
return `${appBase}/cli-auth?code=${encodeURIComponent(userCode)}`
2626
}
2727

28+
// The dashboard sign-in page, where a new customer installs the Ellipsis
29+
// GitHub App. Built from the active host's app base for the same reason as
30+
// cliAuthUrl: a beta or self-hosted CLI must not send its user to prod.
31+
export function appLoginUrl(appBase: string): string {
32+
return `${appBase}/login`
33+
}
34+
2835
// Wrap `label` in an OSC 8 hyperlink so it opens `url` on click in terminals
2936
// that support it (iTerm2, VS Code, WezTerm, kitty, GNOME); degrades to plain
3037
// text elsewhere. Only emitted to a TTY — piped output stays free of escapes.

test/urls.test.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, expect, it } from 'vitest'
2-
import { cliAuthUrl, configUrl, hyperlink, sessionUrl } from '../src/lib/urls'
2+
import { appLoginUrl, cliAuthUrl, configUrl, hyperlink, sessionUrl } from '../src/lib/urls'
33

44
describe('sessionUrl', () => {
55
it('builds the account page link with the session query param', () => {
@@ -37,6 +37,18 @@ describe('cliAuthUrl', () => {
3737
})
3838
})
3939

40+
describe('appLoginUrl', () => {
41+
it('builds the dashboard sign-in page url', () => {
42+
expect(appLoginUrl('https://app.ellipsis.dev')).toBe('https://app.ellipsis.dev/login')
43+
})
44+
45+
it('tracks the app base host, so a beta base yields a beta sign-in url', () => {
46+
expect(appLoginUrl('https://beta-app.ellipsis.dev')).toBe(
47+
'https://beta-app.ellipsis.dev/login',
48+
)
49+
})
50+
})
51+
4052
describe('hyperlink', () => {
4153
const ESC = String.fromCharCode(27)
4254

0 commit comments

Comments
 (0)