overlord-cli 0.2606070914.0 → 0.2606081356.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -0
- package/bin/_cli/auth.mjs +29 -0
- package/bin/_cli/credentials.mjs +51 -0
- package/bin/_cli/index.mjs +2 -1
- package/bin/_cli/onboard.mjs +107 -36
- package/bin/_cli/protocol.mjs +447 -48
- package/bin/_cli/signup.mjs +392 -0
- package/package.json +1 -1
- package/plugins/claude/skills/overlord-ticket/reference/cli.md +11 -0
- package/plugins/cursor/skills/overlord-ticket/reference/cli.md +11 -0
- package/plugins/overlord/.codex-plugin/plugin.json +1 -1
- package/plugins/overlord/scripts/user-prompt-submit-hook.sh +62 -1
- package/plugins/overlord/skills/overlord-ticket/reference/cli.md +11 -0
package/README.md
CHANGED
|
@@ -38,15 +38,36 @@ overlord help
|
|
|
38
38
|
### Auth
|
|
39
39
|
|
|
40
40
|
```bash
|
|
41
|
+
# No account yet? Create one entirely from the terminal with just an email.
|
|
42
|
+
ovld auth signup --email agent@example.com --name "Build Agent" # emails an 8-digit code, then mints a durable oat_ agent token by default
|
|
43
|
+
ovld onboard --email agent@example.com --project-name Repo # same signup, plus org/project/cwd setup
|
|
44
|
+
|
|
41
45
|
# Repair shared credentials first when a session already exists
|
|
42
46
|
ovld auth repair # mirror and chmod shared Desktop/CLI credentials when possible
|
|
43
47
|
|
|
44
48
|
# Login to Overlord if repair does not restore access
|
|
45
49
|
ovld auth login #opens a browser when possible and also prints a verification URL/code so login can be completed from another machine over SSH.
|
|
50
|
+
ovld auth login --email agent@example.com # log back in after logout with an emailed code (no browser)
|
|
46
51
|
ovld auth status # show current login status (use --verbose for redacted diagnostics)
|
|
47
52
|
ovld auth logout # remove stored credentials
|
|
48
53
|
```
|
|
49
54
|
|
|
55
|
+
#### Terminal-native account creation
|
|
56
|
+
|
|
57
|
+
`ovld auth signup` creates an Overlord account without a browser: it emails an
|
|
58
|
+
8-digit confirmation code, you enter it in the terminal, and the CLI stores valid
|
|
59
|
+
credentials. Pass `--password` to set a password-manager-generated password;
|
|
60
|
+
without one, signup is passwordless and you log back in later with
|
|
61
|
+
`ovld auth login --email`. By default a durable `oat_…` agent token is minted and
|
|
62
|
+
saved for headless use — pass `--no-agent-token` to skip it. For agents that read
|
|
63
|
+
email out of band, the split flow is available:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
ovld auth signup request --email agent@example.com --name "Build Agent" --json
|
|
67
|
+
ovld auth signup verify --email agent@example.com --code 12345678 --json
|
|
68
|
+
ovld onboard --use-current-auth --project-name Repo --directory "$PWD" --yes
|
|
69
|
+
```
|
|
70
|
+
|
|
50
71
|
Desktop-installed wrappers default `OVERLORD_URL` to `https://www.ovld.ai` unless you override it explicitly.
|
|
51
72
|
For local dev against the web app on port 3000, export the override before running auth or protocol commands:
|
|
52
73
|
|
package/bin/_cli/auth.mjs
CHANGED
|
@@ -726,7 +726,14 @@ export async function runAuthCommand(subcommand, args = []) {
|
|
|
726
726
|
console.log(`ovld auth <subcommand>
|
|
727
727
|
|
|
728
728
|
Subcommands:
|
|
729
|
+
signup Create a new Overlord account from the terminal (email + confirmation code)
|
|
730
|
+
--email <email> Email to create the account with.
|
|
731
|
+
--name <name> Display name.
|
|
732
|
+
--password <password> Optional password-manager password.
|
|
733
|
+
--no-agent-token Skip minting a durable oat_ token.
|
|
734
|
+
Run \`ovld auth signup help\` for request/verify split flow.
|
|
729
735
|
login Authorize the CLI via browser (works locally or over SSH)
|
|
736
|
+
--email <email> Log in with an emailed code instead of a browser.
|
|
730
737
|
--token <oat_…> Persist an agent token from Settings → Agents & MCP.
|
|
731
738
|
Skips the browser flow; token never expires.
|
|
732
739
|
--organization-id <id> Optional. Validated against your membership but
|
|
@@ -740,7 +747,29 @@ Subcommands:
|
|
|
740
747
|
return;
|
|
741
748
|
}
|
|
742
749
|
|
|
750
|
+
if (subcommand === 'signup') {
|
|
751
|
+
const { runAuthSignupCommand } = await import('./signup.mjs');
|
|
752
|
+
await runAuthSignupCommand(args);
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
|
|
743
756
|
if (subcommand === 'login') {
|
|
757
|
+
const [maybeSub, ...rest] = args;
|
|
758
|
+
if (maybeSub === 'request') {
|
|
759
|
+
const { runEmailLoginRequest } = await import('./signup.mjs');
|
|
760
|
+
await runEmailLoginRequest(rest);
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
if (maybeSub === 'verify') {
|
|
764
|
+
const { runEmailLoginVerify } = await import('./signup.mjs');
|
|
765
|
+
await runEmailLoginVerify(rest);
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
if (args.some(arg => arg === '--email' || arg.startsWith('--email='))) {
|
|
769
|
+
const { runEmailLogin } = await import('./signup.mjs');
|
|
770
|
+
await runEmailLogin(args);
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
744
773
|
await authLogin(args);
|
|
745
774
|
return;
|
|
746
775
|
}
|
package/bin/_cli/credentials.mjs
CHANGED
|
@@ -785,6 +785,57 @@ export async function searchTicketsAcrossOrganizations(auth, body, options = {})
|
|
|
785
785
|
return { tickets, count: tickets.length };
|
|
786
786
|
}
|
|
787
787
|
|
|
788
|
+
/**
|
|
789
|
+
* Resolve a fresh Supabase access token (JWT) from stored credentials, refreshing
|
|
790
|
+
* via the stored refresh token when the cached access token is stale.
|
|
791
|
+
*
|
|
792
|
+
* Unlike {@link resolveAuth}, this never returns an `oat_…` agent token: it is for
|
|
793
|
+
* callers that must present a real Supabase user JWT (e.g. `/api/auth/cli-onboarding`
|
|
794
|
+
* and `/api/auth/agent-token`, which validate the session with `auth.getUser`).
|
|
795
|
+
*
|
|
796
|
+
* @returns {Promise<{ accessToken: string, platformUrl: string, localSecret: string }>}
|
|
797
|
+
*/
|
|
798
|
+
export async function resolveStoredSupabaseAccessToken() {
|
|
799
|
+
const selected = selectStoredCredentials();
|
|
800
|
+
const creds = selected?.credentials ?? migrateLegacyCredentials();
|
|
801
|
+
const platformUrl =
|
|
802
|
+
normalizePlatformUrl(process.env.OVERLORD_URL) ??
|
|
803
|
+
normalizeStoredPlatformUrl(creds?.platform_url) ??
|
|
804
|
+
getDefaultOverlordUrl();
|
|
805
|
+
const runtime = isLocalhostUrl(platformUrl) ? loadRuntime(platformUrl) : null;
|
|
806
|
+
const localSecret =
|
|
807
|
+
runtime?.local_secret && runtime.platform_url === platformUrl ? runtime.local_secret : '';
|
|
808
|
+
|
|
809
|
+
if (!creds) {
|
|
810
|
+
throw new Error('No stored Overlord credentials. Run `ovld auth signup` or `ovld auth login` first.');
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
if (creds.access_token && isAccessTokenFresh(creds)) {
|
|
814
|
+
return { accessToken: creds.access_token, platformUrl, localSecret };
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
if (creds.refresh_token) {
|
|
818
|
+
const refreshed = await refreshOAuthAccessToken(platformUrl, creds.refresh_token, localSecret);
|
|
819
|
+
if (!refreshed.access_token) {
|
|
820
|
+
throw new Error('Failed to refresh the stored Supabase session. Run `ovld auth login --email` again.');
|
|
821
|
+
}
|
|
822
|
+
saveCredentialsToSource(
|
|
823
|
+
{
|
|
824
|
+
...creds,
|
|
825
|
+
access_token: refreshed.access_token,
|
|
826
|
+
access_token_expires_at: refreshed.access_token_expires_at,
|
|
827
|
+
refresh_token: refreshed.refresh_token || creds.refresh_token
|
|
828
|
+
},
|
|
829
|
+
selected?.source
|
|
830
|
+
);
|
|
831
|
+
return { accessToken: refreshed.access_token, platformUrl, localSecret };
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
throw new Error(
|
|
835
|
+
'Stored credentials have no Supabase session. Run `ovld auth login --email` to obtain one.'
|
|
836
|
+
);
|
|
837
|
+
}
|
|
838
|
+
|
|
788
839
|
export async function getAuthStatus() {
|
|
789
840
|
const creds = loadCredentials();
|
|
790
841
|
let resolved;
|
package/bin/_cli/index.mjs
CHANGED
|
@@ -59,7 +59,8 @@ Agents:
|
|
|
59
59
|
Key protocol commands: auth-status, discover-project, create, prompt, attach, connect, load-context, heartbeat.
|
|
60
60
|
|
|
61
61
|
Auth:
|
|
62
|
-
${primaryCommand} auth
|
|
62
|
+
${primaryCommand} auth signup Create an account from the terminal (--email <email>)
|
|
63
|
+
${primaryCommand} auth login Authorize CLI via browser, --email <email>, or --token <oat_…>
|
|
63
64
|
${primaryCommand} auth status Show login status
|
|
64
65
|
${primaryCommand} auth repair Repair shared Desktop/CLI credentials
|
|
65
66
|
${primaryCommand} auth logout Remove stored credentials
|
package/bin/_cli/onboard.mjs
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
buildAuthHeaders,
|
|
12
12
|
loadCredentials,
|
|
13
13
|
loadRuntime,
|
|
14
|
+
resolveStoredSupabaseAccessToken,
|
|
14
15
|
saveCredentials
|
|
15
16
|
} from './credentials.mjs';
|
|
16
17
|
import { upsertLocalOverlordConfig } from './local-config.mjs';
|
|
@@ -150,18 +151,87 @@ function buildWebProjectUrl(platformUrl, result) {
|
|
|
150
151
|
return `${platformUrl}/projects/${projectId}`;
|
|
151
152
|
}
|
|
152
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Resolve the credentials onboarding will use, based on the chosen auth mode:
|
|
156
|
+
* - `--use-current-auth`: reuse the already signed-in Supabase session.
|
|
157
|
+
* - `--email`: create the account entirely in the terminal (email + code).
|
|
158
|
+
* - default: browser device-authorization flow.
|
|
159
|
+
*
|
|
160
|
+
* Returns the Supabase access token to call `cli-onboarding` with, the resolved
|
|
161
|
+
* platform URL and local secret, and (only for the browser flow) the freshly
|
|
162
|
+
* issued session that still needs persisting.
|
|
163
|
+
*/
|
|
164
|
+
async function resolveOnboardingAuth({ flags, name, inviteToken }) {
|
|
165
|
+
const useCurrentAuth = flags['use-current-auth'] === true || flags['use-current-auth'] === 'true';
|
|
166
|
+
const email = trimFlag(flags, 'email');
|
|
167
|
+
|
|
168
|
+
if (useCurrentAuth) {
|
|
169
|
+
const { accessToken, platformUrl, localSecret } = await resolveStoredSupabaseAccessToken();
|
|
170
|
+
return { accessToken, platformUrl, localSecret, sessionToPersist: null };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (email) {
|
|
174
|
+
const { runCliSignupFlow } = await import('./signup.mjs');
|
|
175
|
+
const { session, platformUrl, localSecret } = await runCliSignupFlow({
|
|
176
|
+
email,
|
|
177
|
+
name,
|
|
178
|
+
password: trimFlag(flags, 'password') || undefined,
|
|
179
|
+
invite: inviteToken || undefined,
|
|
180
|
+
'no-agent-token': flags['no-agent-token'] === true
|
|
181
|
+
});
|
|
182
|
+
return {
|
|
183
|
+
accessToken: session.access_token,
|
|
184
|
+
platformUrl: session.platform_url ?? platformUrl,
|
|
185
|
+
localSecret,
|
|
186
|
+
sessionToPersist: null
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const storedCredentials = loadCredentials();
|
|
191
|
+
const platformUrl = resolveLoginPlatformUrl(null, storedCredentials?.platform_url ?? null);
|
|
192
|
+
const runtime = loadRuntime(platformUrl);
|
|
193
|
+
const localSecret = runtime?.local_secret ?? process.env.OVERLORD_LOCAL_SECRET ?? '';
|
|
194
|
+
|
|
195
|
+
console.log('\nStarting Overlord onboarding in your browser.');
|
|
196
|
+
console.log('Create an account or sign in, then approve the CLI authorization request.\n');
|
|
197
|
+
|
|
198
|
+
const credentials = await authLoginViaDeviceFlow(platformUrl, localSecret, {
|
|
199
|
+
browserOpener: verificationUri => {
|
|
200
|
+
const signupUrl = buildSignupUrl(platformUrl, verificationUri, name, inviteToken);
|
|
201
|
+
console.log(` Signup URL: ${signupUrl}`);
|
|
202
|
+
openBrowser(signupUrl);
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
accessToken: credentials.access_token,
|
|
208
|
+
platformUrl: credentials.platform_url ?? platformUrl,
|
|
209
|
+
localSecret,
|
|
210
|
+
sessionToPersist: credentials
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
153
214
|
export async function runOnboardCommand(args) {
|
|
154
215
|
const flags = parseFlags(args);
|
|
155
216
|
|
|
156
217
|
if (flags.help === true || flags.h === true || args[0] === 'help') {
|
|
157
|
-
console.log(`Usage: ovld onboard [--name <name>] [--organization-name <name>] [--project-name <name>] [--directory <path>] [--invite <token|url>] [--yes] [--no-desktop]
|
|
218
|
+
console.log(`Usage: ovld onboard [--name <name>] [--organization-name <name>] [--project-name <name>] [--directory <path>] [--invite <token|url>] [--email <email>] [--password <password>] [--use-current-auth] [--yes] [--no-desktop]
|
|
158
219
|
|
|
159
220
|
Creates an Overlord account setup from the terminal:
|
|
160
221
|
1. collects your name, organization, project, and repository directory
|
|
161
|
-
2.
|
|
222
|
+
2. authorizes the CLI (browser by default; terminal-only with --email)
|
|
162
223
|
3. creates the organization/project, links this directory, and creates the first onboarding ticket
|
|
163
224
|
4. opens the Desktop download page, or the web project when --no-desktop is used
|
|
164
225
|
|
|
226
|
+
Authorization modes:
|
|
227
|
+
(default) Browser signup/login + device authorization.
|
|
228
|
+
--email <email> Create the account entirely in the terminal: an 8-digit
|
|
229
|
+
confirmation code is emailed and entered here. Add
|
|
230
|
+
--password <password> to set a password-manager password;
|
|
231
|
+
otherwise future login uses \`ovld auth login --email\`.
|
|
232
|
+
--use-current-auth Skip auth and provision using the already signed-in session
|
|
233
|
+
(e.g. right after \`ovld auth signup\`).
|
|
234
|
+
|
|
165
235
|
With --invite <token>, joins the inviting organization (from an invite email)
|
|
166
236
|
with the invited role instead of creating a new organization; the organization
|
|
167
237
|
prompt is skipped. Accepts a bare token or a full /invite/<token> URL.
|
|
@@ -190,42 +260,43 @@ Run this from the repository you want Overlord agents to work in.`);
|
|
|
190
260
|
throw new Error('Organization name is required (or pass --invite <token> to join an org).');
|
|
191
261
|
}
|
|
192
262
|
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
console.log('Create an account or sign in, then approve the CLI authorization request.\n');
|
|
200
|
-
|
|
201
|
-
const credentials = await authLoginViaDeviceFlow(platformUrl, localSecret, {
|
|
202
|
-
browserOpener: verificationUri => {
|
|
203
|
-
const signupUrl = buildSignupUrl(platformUrl, verificationUri, name, inviteToken);
|
|
204
|
-
console.log(` Signup URL: ${signupUrl}`);
|
|
205
|
-
openBrowser(signupUrl);
|
|
206
|
-
}
|
|
207
|
-
});
|
|
263
|
+
const {
|
|
264
|
+
accessToken,
|
|
265
|
+
platformUrl: resolvedPlatformUrl,
|
|
266
|
+
localSecret,
|
|
267
|
+
sessionToPersist
|
|
268
|
+
} = await resolveOnboardingAuth({ flags, name, inviteToken });
|
|
208
269
|
|
|
209
|
-
const resolvedPlatformUrl = credentials.platform_url ?? platformUrl;
|
|
210
270
|
const deviceFingerprint = readOrCreateDeviceFingerprint(flags);
|
|
211
|
-
const result = await completeCliOnboarding(
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
271
|
+
const result = await completeCliOnboarding(
|
|
272
|
+
resolvedPlatformUrl,
|
|
273
|
+
localSecret,
|
|
274
|
+
{ access_token: accessToken },
|
|
275
|
+
{
|
|
276
|
+
name,
|
|
277
|
+
...(organizationName ? { organizationName } : {}),
|
|
278
|
+
projectName,
|
|
279
|
+
directoryPath,
|
|
280
|
+
deviceFingerprint,
|
|
281
|
+
deviceHostname: os.hostname(),
|
|
282
|
+
devicePlatform: process.platform,
|
|
283
|
+
...(inviteToken ? { inviteToken } : {})
|
|
284
|
+
}
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
// The browser device-auth flow returns a session that hasn't been stored yet;
|
|
288
|
+
// persist it (with the resolved org). The --email and --use-current-auth paths
|
|
289
|
+
// already saved credentials (including any minted agent token), so we leave the
|
|
290
|
+
// stored credential — and its durable agent token — untouched.
|
|
291
|
+
if (sessionToPersist) {
|
|
292
|
+
saveCredentials({
|
|
293
|
+
access_token: sessionToPersist.access_token,
|
|
294
|
+
access_token_expires_at: sessionToPersist.access_token_expires_at ?? undefined,
|
|
295
|
+
refresh_token: sessionToPersist.refresh_token,
|
|
296
|
+
organization_id: result.organization.organizationId,
|
|
297
|
+
platform_url: resolvedPlatformUrl
|
|
298
|
+
});
|
|
299
|
+
}
|
|
229
300
|
|
|
230
301
|
try {
|
|
231
302
|
const localConfig = await upsertLocalOverlordConfig({
|