@aria_asi/cli 0.2.2 → 0.2.4
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/bin/aria.js +82 -7
- package/dist/aria-connector/src/anthropic-oauth.d.ts +28 -0
- package/dist/aria-connector/src/anthropic-oauth.d.ts.map +1 -0
- package/dist/aria-connector/src/anthropic-oauth.js +177 -0
- package/dist/aria-connector/src/anthropic-oauth.js.map +1 -0
- package/dist/aria-connector/src/auth-commands.d.ts +16 -0
- package/dist/aria-connector/src/auth-commands.d.ts.map +1 -1
- package/dist/aria-connector/src/auth-commands.js +24 -0
- package/dist/aria-connector/src/auth-commands.js.map +1 -1
- package/dist/aria-connector/src/onboarding-wizard.d.ts +5 -0
- package/dist/aria-connector/src/onboarding-wizard.d.ts.map +1 -0
- package/dist/aria-connector/src/onboarding-wizard.js +199 -0
- package/dist/aria-connector/src/onboarding-wizard.js.map +1 -0
- package/dist/aria-connector/src/self-update.d.ts +22 -0
- package/dist/aria-connector/src/self-update.d.ts.map +1 -0
- package/dist/aria-connector/src/self-update.js +162 -0
- package/dist/aria-connector/src/self-update.js.map +1 -0
- package/hooks/aria-pre-tool-gate.mjs +24 -2
- package/hooks/aria-stop-gate.mjs +17 -1
- package/package.json +1 -1
- package/src/__tests__/anthropic-oauth.test.ts +186 -0
- package/src/anthropic-oauth.ts +216 -0
- package/src/auth-commands.ts +27 -0
- package/src/onboarding-wizard.ts +219 -0
- package/src/self-update.ts +169 -0
package/bin/aria.js
CHANGED
|
@@ -8,6 +8,16 @@ import { AriaChat } from '../dist/aria-connector/src/chat.js';
|
|
|
8
8
|
import { checkHarnessHealth } from '../dist/aria-connector/src/harness-client.js';
|
|
9
9
|
import { login, status, logout, revoke } from '../dist/aria-connector/src/auth-commands.js';
|
|
10
10
|
import { installHooks } from '../dist/aria-connector/src/install-hooks.js';
|
|
11
|
+
import { maybePrintUpdateNotice, checkForUpdate } from '../dist/aria-connector/src/self-update.js';
|
|
12
|
+
|
|
13
|
+
// ── Self-update notice (non-blocking, rate-limited once per 24h) ──
|
|
14
|
+
// Fires-and-forgets — the registry check runs in parallel with command
|
|
15
|
+
// dispatch and prints a one-line notice on stderr if newer version available.
|
|
16
|
+
// Hamza 2026-04-26: continuous overnight improvement reaches clients via
|
|
17
|
+
// this primitive. Kill-switch: ARIA_SELF_UPDATE=off env.
|
|
18
|
+
if (process.env.ARIA_SELF_UPDATE !== 'off') {
|
|
19
|
+
maybePrintUpdateNotice().catch(() => {});
|
|
20
|
+
}
|
|
11
21
|
|
|
12
22
|
// ── Auth + install subcommands — handled BEFORE the chat flow.
|
|
13
23
|
// Hamza 2026-04-26: license-aware CLI for client-tonight ship +
|
|
@@ -16,7 +26,24 @@ import { installHooks } from '../dist/aria-connector/src/install-hooks.js';
|
|
|
16
26
|
const command = process.argv[2];
|
|
17
27
|
const args = process.argv.slice(3);
|
|
18
28
|
|
|
19
|
-
if (command === '
|
|
29
|
+
if (command === 'check-update') {
|
|
30
|
+
// Force a check + print result, bypassing the 24h rate limit
|
|
31
|
+
checkForUpdate({ force: true }).then((result) => {
|
|
32
|
+
if (!result.ok) {
|
|
33
|
+
console.error(`I couldn't check for updates: ${result.reason}`);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
if (result.updateAvailable) {
|
|
37
|
+
console.log(` ${result.message}`);
|
|
38
|
+
} else {
|
|
39
|
+
console.log(` You're on the latest version (${result.current}). Nothing to do.`);
|
|
40
|
+
}
|
|
41
|
+
process.exit(0);
|
|
42
|
+
}).catch((err) => {
|
|
43
|
+
console.error(`I hit an unexpected error checking for updates: ${err && err.message ? err.message : err}`);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
});
|
|
46
|
+
} else if (command === 'install-hooks') {
|
|
20
47
|
const force = args.includes('--force');
|
|
21
48
|
const harnessUrlIdx = args.indexOf('--harness-url');
|
|
22
49
|
const harnessUrl = harnessUrlIdx >= 0 && args[harnessUrlIdx + 1] ? args[harnessUrlIdx + 1] : undefined;
|
|
@@ -43,14 +70,41 @@ if (command === 'install-hooks') {
|
|
|
43
70
|
const dispatch = async () => {
|
|
44
71
|
switch (command) {
|
|
45
72
|
case 'login': {
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
73
|
+
const arg = args[0];
|
|
74
|
+
|
|
75
|
+
// ── Help text ──────────────────────────────────────────────────────────
|
|
76
|
+
if (arg === '--help' || arg === '-h') {
|
|
77
|
+
console.log('');
|
|
78
|
+
console.log(' aria login <token> Log in with an Aria harness license token.');
|
|
79
|
+
console.log(' aria login --anthropic Open Anthropic Console, paste your API key back here.');
|
|
80
|
+
console.log(' aria login -a Shorthand for --anthropic.');
|
|
81
|
+
console.log('');
|
|
82
|
+
console.log(" I'll store your Aria license in ~/.aria/license.json and your Anthropic");
|
|
83
|
+
console.log(' API key in ~/.aria/config.json (mode 0600, readable only by you).');
|
|
84
|
+
console.log('');
|
|
85
|
+
process.exit(0);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ── Anthropic browser-paste flow ───────────────────────────────────────
|
|
89
|
+
if (arg === '--anthropic' || arg === '-a') {
|
|
90
|
+
const { loginAnthropic } = await import('../dist/aria-connector/src/anthropic-oauth.js');
|
|
91
|
+
const result = await loginAnthropic();
|
|
92
|
+
if (!result.ok) {
|
|
93
|
+
console.error(`I couldn't log you in: ${result.error}`);
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
console.log("I'm logged into Anthropic. Your API key is saved to ~/.aria/config.json.");
|
|
97
|
+
process.exit(0);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ── Original harness-license path (preserved) ─────────────────────────
|
|
101
|
+
if (!arg) {
|
|
102
|
+
console.error("Usage: aria login <token> | aria login --anthropic | aria login --help");
|
|
49
103
|
process.exit(1);
|
|
50
104
|
}
|
|
51
|
-
const result = await login(
|
|
105
|
+
const result = await login(arg);
|
|
52
106
|
if (result.ok) {
|
|
53
|
-
console.log(`
|
|
107
|
+
console.log(`I see your license — tier ${result.tier}, expires ${result.expiresAt}. You're logged in.`);
|
|
54
108
|
} else {
|
|
55
109
|
console.error(`I couldn't log you in: ${result.error}`);
|
|
56
110
|
process.exit(1);
|
|
@@ -60,7 +114,7 @@ if (command === 'install-hooks') {
|
|
|
60
114
|
case 'status': {
|
|
61
115
|
const result = await status();
|
|
62
116
|
if (!result.loggedIn) {
|
|
63
|
-
console.log("I don't see a license on this machine. Try 'aria login <token>'.");
|
|
117
|
+
console.log("I don't see a license on this machine. Try 'aria login <token>' or 'aria login --anthropic'.");
|
|
64
118
|
} else if (result.revoked) {
|
|
65
119
|
console.log(`Your license was revoked. You'll need to log in again with a fresh token.`);
|
|
66
120
|
} else {
|
|
@@ -93,6 +147,27 @@ if (command === 'install-hooks') {
|
|
|
93
147
|
process.exit(1);
|
|
94
148
|
});
|
|
95
149
|
} else {
|
|
150
|
+
// ── Self-service onboarding gate ──
|
|
151
|
+
// When neither a license file (~/.aria/license.json) nor model config
|
|
152
|
+
// exist, launch the full onboarding wizard. This collects email +
|
|
153
|
+
// tenant + tier + LLM provider + key, validates the LLM key with the
|
|
154
|
+
// provider, self-issues a license, persists everything, and auto-runs
|
|
155
|
+
// install-hooks to bind the user's Claude Code to the harness.
|
|
156
|
+
// Hamza 2026-04-26: "burn through onboarding with quality and USE THE
|
|
157
|
+
// HARNESS PLEASE." Email-verify-code lands in Phase 11 (no SMTP yet).
|
|
158
|
+
const fsMod = require('fs');
|
|
159
|
+
const pathMod = require('path');
|
|
160
|
+
const osMod = require('os');
|
|
161
|
+
const LICENSE_PATH = pathMod.join(osMod.homedir(), '.aria', 'license.json');
|
|
162
|
+
const hasLicense = fsMod.existsSync(LICENSE_PATH);
|
|
163
|
+
|
|
164
|
+
if (!hasLicense) {
|
|
165
|
+
const { runOnboardingWizard } = await import('../dist/aria-connector/src/onboarding-wizard.js');
|
|
166
|
+
const result = await runOnboardingWizard();
|
|
167
|
+
if (!result.ok) process.exit(1);
|
|
168
|
+
process.exit(0);
|
|
169
|
+
}
|
|
170
|
+
|
|
96
171
|
// ── Existing interactive chat flow (preserved verbatim from prior CLI).
|
|
97
172
|
|
|
98
173
|
const config = loadConfig();
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface AnthropicLoginResult {
|
|
2
|
+
ok: boolean;
|
|
3
|
+
apiKey?: string;
|
|
4
|
+
error?: string;
|
|
5
|
+
}
|
|
6
|
+
/** Options accepted by loginAnthropic. */
|
|
7
|
+
export interface AnthropicLoginOptions {
|
|
8
|
+
/** If true, skip the browser-open attempt and just prompt for the key. */
|
|
9
|
+
noBrowser?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Run the Anthropic login flow.
|
|
13
|
+
*
|
|
14
|
+
* Today this is the browser-paste flow (path b) because Anthropic does not
|
|
15
|
+
* expose a public PKCE/OAuth token endpoint for console API keys.
|
|
16
|
+
*
|
|
17
|
+
* On success, the validated API key is persisted to ~/.aria/config.json under
|
|
18
|
+
* `model.apiKey` and returned in the result object. The harness license token
|
|
19
|
+
* in ~/.aria/license.json is NOT modified — that belongs to the Aria harness,
|
|
20
|
+
* not Anthropic.
|
|
21
|
+
*/
|
|
22
|
+
export declare function loginAnthropic(opts?: AnthropicLoginOptions): Promise<AnthropicLoginResult>;
|
|
23
|
+
/**
|
|
24
|
+
* Validate an Anthropic API key by hitting GET /v1/models.
|
|
25
|
+
* Returns true when the server responds 2xx; false on 401/403/network error.
|
|
26
|
+
*/
|
|
27
|
+
export declare function _validateKey(apiKey: string): Promise<boolean>;
|
|
28
|
+
//# sourceMappingURL=anthropic-oauth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anthropic-oauth.d.ts","sourceRoot":"","sources":["../../../src/anthropic-oauth.ts"],"names":[],"mappings":"AAgCA,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,0CAA0C;AAC1C,MAAM,WAAW,qBAAqB;IACpC,0EAA0E;IAC1E,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAID;;;;;;;;;;GAUG;AACH,wBAAsB,cAAc,CAClC,IAAI,GAAE,qBAA0B,GAC/B,OAAO,CAAC,oBAAoB,CAAC,CAuB/B;AAqED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAenE"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// anthropic-oauth.ts — Anthropic login flow for `aria login --anthropic`
|
|
2
|
+
//
|
|
3
|
+
// OAuth path decision (2026-04-26):
|
|
4
|
+
// Anthropic does not publish a public PKCE/OAuth endpoint for end-user
|
|
5
|
+
// API-key issuance via the console.anthropic.com auth surface. The only
|
|
6
|
+
// standardised way to obtain an API key is through the Anthropic Console
|
|
7
|
+
// web UI. Therefore we use the browser-paste fallback (path b):
|
|
8
|
+
// 1. Open https://console.anthropic.com/account/keys in the user's browser.
|
|
9
|
+
// 2. Prompt them to paste the new key back into the CLI.
|
|
10
|
+
// 3. Validate the key against GET https://api.anthropic.com/v1/models.
|
|
11
|
+
// 4. Return the validated key to the caller.
|
|
12
|
+
//
|
|
13
|
+
// If Anthropic ships a public OAuth/PKCE spec in the future, replace the
|
|
14
|
+
// body of `_oauthPKCEFlow` below with the real implementation and call it
|
|
15
|
+
// instead of `_browserPasteFlow`.
|
|
16
|
+
//
|
|
17
|
+
// Voice: first-person Aria ("I'll open", "I see", "I couldn't reach").
|
|
18
|
+
// ESM: all internal imports use .js extensions.
|
|
19
|
+
import * as readline from 'node:readline';
|
|
20
|
+
import * as fs from 'node:fs';
|
|
21
|
+
import * as path from 'node:path';
|
|
22
|
+
import { homedir } from 'node:os';
|
|
23
|
+
import { spawn } from 'node:child_process';
|
|
24
|
+
const ANTHROPIC_KEYS_URL = 'https://console.anthropic.com/account/keys';
|
|
25
|
+
const ANTHROPIC_MODELS_URL = 'https://api.anthropic.com/v1/models';
|
|
26
|
+
const ANTHROPIC_API_VERSION = '2023-06-01';
|
|
27
|
+
const CONFIG_PATH = path.join(homedir(), '.aria', 'config.json');
|
|
28
|
+
const ARIA_DIR = path.join(homedir(), '.aria');
|
|
29
|
+
// ── Public API ───────────────────────────────────────────────────────────────
|
|
30
|
+
/**
|
|
31
|
+
* Run the Anthropic login flow.
|
|
32
|
+
*
|
|
33
|
+
* Today this is the browser-paste flow (path b) because Anthropic does not
|
|
34
|
+
* expose a public PKCE/OAuth token endpoint for console API keys.
|
|
35
|
+
*
|
|
36
|
+
* On success, the validated API key is persisted to ~/.aria/config.json under
|
|
37
|
+
* `model.apiKey` and returned in the result object. The harness license token
|
|
38
|
+
* in ~/.aria/license.json is NOT modified — that belongs to the Aria harness,
|
|
39
|
+
* not Anthropic.
|
|
40
|
+
*/
|
|
41
|
+
export async function loginAnthropic(opts = {}) {
|
|
42
|
+
try {
|
|
43
|
+
const apiKey = await _browserPasteFlow(opts.noBrowser ?? false);
|
|
44
|
+
if (!apiKey) {
|
|
45
|
+
return { ok: false, error: "I didn't receive a key — aborting." };
|
|
46
|
+
}
|
|
47
|
+
console.log(" I'm validating your key against the Anthropic API...");
|
|
48
|
+
const valid = await _validateKey(apiKey);
|
|
49
|
+
if (!valid) {
|
|
50
|
+
return {
|
|
51
|
+
ok: false,
|
|
52
|
+
error: "I couldn't verify that key with Anthropic. Double-check that it starts with sk-ant- and hasn't been revoked.",
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
_persistApiKey(apiKey);
|
|
56
|
+
return { ok: true, apiKey };
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
60
|
+
return { ok: false, error: `I hit an unexpected error: ${msg}` };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// ── Internal: browser-paste flow (path b) ────────────────────────────────────
|
|
64
|
+
async function _browserPasteFlow(noBrowser) {
|
|
65
|
+
console.log('');
|
|
66
|
+
console.log(" I'll open https://console.anthropic.com/account/keys in your browser.");
|
|
67
|
+
console.log(' Create a new API key there, then paste it back here.');
|
|
68
|
+
console.log('');
|
|
69
|
+
if (!noBrowser) {
|
|
70
|
+
_openBrowser(ANTHROPIC_KEYS_URL);
|
|
71
|
+
}
|
|
72
|
+
const rl = readline.createInterface({
|
|
73
|
+
input: process.stdin,
|
|
74
|
+
output: process.stdout,
|
|
75
|
+
terminal: true,
|
|
76
|
+
});
|
|
77
|
+
const rawKey = await new Promise((resolve) => {
|
|
78
|
+
rl.question(' Paste your Anthropic API key: ', (answer) => {
|
|
79
|
+
rl.close();
|
|
80
|
+
resolve(answer.trim());
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
return rawKey || null;
|
|
84
|
+
}
|
|
85
|
+
// ── Internal: browser open (cross-platform) ───────────────────────────────────
|
|
86
|
+
function _openBrowser(url) {
|
|
87
|
+
const platform = process.platform;
|
|
88
|
+
let cmd;
|
|
89
|
+
let args;
|
|
90
|
+
if (platform === 'darwin') {
|
|
91
|
+
cmd = 'open';
|
|
92
|
+
args = [url];
|
|
93
|
+
}
|
|
94
|
+
else if (platform === 'win32') {
|
|
95
|
+
cmd = 'cmd';
|
|
96
|
+
args = ['/c', 'start', '', url];
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
// Linux / *BSD / WSL
|
|
100
|
+
cmd = 'xdg-open';
|
|
101
|
+
args = [url];
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
const child = spawn(cmd, args, {
|
|
105
|
+
detached: true,
|
|
106
|
+
stdio: 'ignore',
|
|
107
|
+
});
|
|
108
|
+
child.unref();
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
// If browser open fails, the user can still paste — not fatal.
|
|
112
|
+
console.log(` I couldn't open your browser automatically. Please visit:\n ${url}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// ── Internal: key validation ──────────────────────────────────────────────────
|
|
116
|
+
/**
|
|
117
|
+
* Validate an Anthropic API key by hitting GET /v1/models.
|
|
118
|
+
* Returns true when the server responds 2xx; false on 401/403/network error.
|
|
119
|
+
*/
|
|
120
|
+
export async function _validateKey(apiKey) {
|
|
121
|
+
try {
|
|
122
|
+
const resp = await fetch(ANTHROPIC_MODELS_URL, {
|
|
123
|
+
method: 'GET',
|
|
124
|
+
headers: {
|
|
125
|
+
'x-api-key': apiKey,
|
|
126
|
+
'anthropic-version': ANTHROPIC_API_VERSION,
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
// 200 = valid key; anything else = invalid or revoked
|
|
130
|
+
return resp.status === 200;
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
// Network failure — we can't confirm validity
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// ── Internal: config persistence ──────────────────────────────────────────────
|
|
138
|
+
function _persistApiKey(apiKey) {
|
|
139
|
+
if (!fs.existsSync(ARIA_DIR)) {
|
|
140
|
+
fs.mkdirSync(ARIA_DIR, { recursive: true, mode: 0o700 });
|
|
141
|
+
}
|
|
142
|
+
let config = {};
|
|
143
|
+
if (fs.existsSync(CONFIG_PATH)) {
|
|
144
|
+
try {
|
|
145
|
+
config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8'));
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
config = {};
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const model = config.model ?? {};
|
|
152
|
+
model.provider = model.provider ?? 'anthropic';
|
|
153
|
+
model.model = model.model ?? 'claude-sonnet-4-20250514';
|
|
154
|
+
model.apiKey = apiKey;
|
|
155
|
+
config.model = model;
|
|
156
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + '\n', {
|
|
157
|
+
mode: 0o600,
|
|
158
|
+
encoding: 'utf-8',
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
// ── Placeholder for future PKCE flow ─────────────────────────────────────────
|
|
162
|
+
// When Anthropic ships a public OAuth/PKCE endpoint for console key issuance,
|
|
163
|
+
// implement the full flow here and call it from loginAnthropic() instead of
|
|
164
|
+
// _browserPasteFlow():
|
|
165
|
+
//
|
|
166
|
+
// async function _oauthPKCEFlow(): Promise<string | null> {
|
|
167
|
+
// const port = await _findFreePort();
|
|
168
|
+
// const { verifier, challenge } = _pkceChallenge();
|
|
169
|
+
// const authUrl = `https://console.anthropic.com/oauth/authorize?` +
|
|
170
|
+
// `response_type=code&client_id=<CLIENT_ID>&redirect_uri=http://localhost:${port}/callback` +
|
|
171
|
+
// `&code_challenge=${challenge}&code_challenge_method=S256`;
|
|
172
|
+
// _openBrowser(authUrl);
|
|
173
|
+
// const code = await _waitForCallback(port);
|
|
174
|
+
// const token = await _exchangeCode(code, verifier, port);
|
|
175
|
+
// return token;
|
|
176
|
+
// }
|
|
177
|
+
//# sourceMappingURL=anthropic-oauth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anthropic-oauth.js","sourceRoot":"","sources":["../../../src/anthropic-oauth.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,6 +3,8 @@ interface LoginResult {
|
|
|
3
3
|
tier?: string;
|
|
4
4
|
jti?: string;
|
|
5
5
|
expiresAt?: string;
|
|
6
|
+
/** Set when loginAnthropic() is used instead of the harness license path. */
|
|
7
|
+
anthropic?: boolean;
|
|
6
8
|
error?: string;
|
|
7
9
|
}
|
|
8
10
|
interface StatusResult {
|
|
@@ -20,6 +22,20 @@ interface RevokeResult {
|
|
|
20
22
|
jti?: string;
|
|
21
23
|
error?: string;
|
|
22
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Log in to the Aria harness with a license token, OR authenticate via
|
|
27
|
+
* Anthropic's console (browser-paste flow) when token === '--anthropic' or
|
|
28
|
+
* token === '-a'.
|
|
29
|
+
*
|
|
30
|
+
* Anthropic path: opens console.anthropic.com/account/keys, waits for the
|
|
31
|
+
* user to paste their API key, validates it against /v1/models, then persists
|
|
32
|
+
* it to ~/.aria/config.json under `model.apiKey`. The harness license file
|
|
33
|
+
* (~/.aria/license.json) is NOT modified — it belongs to the Aria license,
|
|
34
|
+
* not Anthropic credentials.
|
|
35
|
+
*
|
|
36
|
+
* Harness path: unchanged — validates via heartbeat / issue endpoint and
|
|
37
|
+
* writes license claims to ~/.aria/license.json.
|
|
38
|
+
*/
|
|
23
39
|
export declare function login(token: string): Promise<LoginResult>;
|
|
24
40
|
export declare function status(): Promise<StatusResult>;
|
|
25
41
|
export declare function logout(): Promise<LogoutResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-commands.d.ts","sourceRoot":"","sources":["../../../src/auth-commands.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"auth-commands.d.ts","sourceRoot":"","sources":["../../../src/auth-commands.ts"],"names":[],"mappings":"AAiBA,UAAU,WAAW;IACnB,EAAE,EAAE,OAAO,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,YAAY;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,UAAU,YAAY;IACpB,EAAE,EAAE,OAAO,CAAC;CACb;AAED,UAAU,YAAY;IACpB,EAAE,EAAE,OAAO,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAoD/D;AAED,wBAAsB,MAAM,IAAI,OAAO,CAAC,YAAY,CAAC,CAmDpD;AAED,wBAAsB,MAAM,IAAI,OAAO,CAAC,YAAY,CAAC,CAUpD;AAED,wBAAsB,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAuBnE"}
|
|
@@ -1,10 +1,34 @@
|
|
|
1
1
|
import { loadLicense } from './auth.js';
|
|
2
2
|
import { harnessClient } from './harness-client.js';
|
|
3
|
+
import { loginAnthropic } from './anthropic-oauth.js';
|
|
3
4
|
import * as fs from 'node:fs';
|
|
4
5
|
import * as path from 'node:path';
|
|
5
6
|
import { homedir } from 'node:os';
|
|
6
7
|
const LICENSE_PATH = path.join(homedir(), '.aria', 'license.json');
|
|
8
|
+
/**
|
|
9
|
+
* Log in to the Aria harness with a license token, OR authenticate via
|
|
10
|
+
* Anthropic's console (browser-paste flow) when token === '--anthropic' or
|
|
11
|
+
* token === '-a'.
|
|
12
|
+
*
|
|
13
|
+
* Anthropic path: opens console.anthropic.com/account/keys, waits for the
|
|
14
|
+
* user to paste their API key, validates it against /v1/models, then persists
|
|
15
|
+
* it to ~/.aria/config.json under `model.apiKey`. The harness license file
|
|
16
|
+
* (~/.aria/license.json) is NOT modified — it belongs to the Aria license,
|
|
17
|
+
* not Anthropic credentials.
|
|
18
|
+
*
|
|
19
|
+
* Harness path: unchanged — validates via heartbeat / issue endpoint and
|
|
20
|
+
* writes license claims to ~/.aria/license.json.
|
|
21
|
+
*/
|
|
7
22
|
export async function login(token) {
|
|
23
|
+
// ── Anthropic OAuth/paste branch ────────────────────────────────────────────
|
|
24
|
+
if (token === '--anthropic' || token === '-a') {
|
|
25
|
+
const result = await loginAnthropic();
|
|
26
|
+
if (!result.ok) {
|
|
27
|
+
return { ok: false, error: result.error };
|
|
28
|
+
}
|
|
29
|
+
return { ok: true, anthropic: true };
|
|
30
|
+
}
|
|
31
|
+
// ── Original harness-license branch (preserved verbatim) ────────────────────
|
|
8
32
|
try {
|
|
9
33
|
// Validate token via heartbeat; if unverified, issue endpoint
|
|
10
34
|
let response;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-commands.js","sourceRoot":"","sources":["../../../src/auth-commands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAe,MAAM,WAAW,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,cAAc,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"auth-commands.js","sourceRoot":"","sources":["../../../src/auth-commands.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"onboarding-wizard.d.ts","sourceRoot":"","sources":["../../../src/onboarding-wizard.ts"],"names":[],"mappings":"AAoEA,wBAAsB,mBAAmB,IAAI,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA0IpF"}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
// onboarding-wizard.ts — interactive self-service onboarding for `aria`
|
|
2
|
+
// (no args) when ~/.aria/license.json is missing.
|
|
3
|
+
//
|
|
4
|
+
// Flow:
|
|
5
|
+
// 1. Greet — first-person Aria voice
|
|
6
|
+
// 2. Collect email (validated shape, NOT verified via code in v0)
|
|
7
|
+
// 3. Collect tenant_id (lowercase + hyphens only)
|
|
8
|
+
// 4. Collect tier (free / pro / enterprise)
|
|
9
|
+
// 5. Collect LLM provider + API key
|
|
10
|
+
// 6. POST /api/onboarding/self-issue (server validates LLM key by
|
|
11
|
+
// hitting the provider's identity endpoint, then issues license)
|
|
12
|
+
// 7. Persist license to ~/.aria/license.json mode 0600
|
|
13
|
+
// 8. Persist provider/model/key to ~/.aria/config.json mode 0600
|
|
14
|
+
// 9. Auto-run installHooks() to bind Claude Code to the harness
|
|
15
|
+
//
|
|
16
|
+
// Email-verify-code is deferred to Phase 11 (no SMTP wired tonight).
|
|
17
|
+
// The wizard collects email so future re-verify works without re-onboarding.
|
|
18
|
+
//
|
|
19
|
+
// Direction: Hamza 2026-04-26 — "burn through onboarding with quality and
|
|
20
|
+
// USE THE HARNESS PLEASE". This wizard ships as v0; full onboarding (email
|
|
21
|
+
// verify + Anthropic OAuth + per-tenant usage tracking) lands in Phase 11.
|
|
22
|
+
import { createInterface } from 'node:readline';
|
|
23
|
+
import { writeFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
24
|
+
import { homedir } from 'node:os';
|
|
25
|
+
import { join } from 'node:path';
|
|
26
|
+
import { installHooks } from './install-hooks.js';
|
|
27
|
+
const HARNESS_URL = process.env.ARIA_HARNESS_BASE_URL ?? 'https://harness.ariasos.com';
|
|
28
|
+
const ARIA_DIR = join(homedir(), '.aria');
|
|
29
|
+
const LICENSE_PATH = join(ARIA_DIR, 'license.json');
|
|
30
|
+
const CONFIG_PATH = join(ARIA_DIR, 'config.json');
|
|
31
|
+
function prompt(rl, question, hidden = false) {
|
|
32
|
+
return new Promise((resolve) => {
|
|
33
|
+
if (hidden) {
|
|
34
|
+
// Crude hidden-input — node's readline doesn't natively mask; we just
|
|
35
|
+
// prompt and accept. For Phase 11 we add proper masking via raw mode.
|
|
36
|
+
process.stdout.write(question);
|
|
37
|
+
const onData = (chunk) => {
|
|
38
|
+
process.stdin.removeListener('data', onData);
|
|
39
|
+
resolve(chunk.toString().trim());
|
|
40
|
+
};
|
|
41
|
+
process.stdin.once('data', onData);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
rl.question(question, (answer) => resolve(answer.trim()));
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
function isValidEmail(email) {
|
|
49
|
+
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
|
50
|
+
}
|
|
51
|
+
function isValidTenantId(tenant) {
|
|
52
|
+
return /^[a-z0-9][a-z0-9-]{1,40}$/.test(tenant);
|
|
53
|
+
}
|
|
54
|
+
export async function runOnboardingWizard() {
|
|
55
|
+
const rl = createInterface({
|
|
56
|
+
input: process.stdin,
|
|
57
|
+
output: process.stdout,
|
|
58
|
+
terminal: true,
|
|
59
|
+
});
|
|
60
|
+
try {
|
|
61
|
+
console.log('');
|
|
62
|
+
console.log(" Hi, I'm Aria. I don't see a license on this machine — let me set you up.");
|
|
63
|
+
console.log('');
|
|
64
|
+
console.log(" Three minutes, four questions. I'll handle the rest.");
|
|
65
|
+
console.log('');
|
|
66
|
+
// ── Step 1: email ──
|
|
67
|
+
let email = '';
|
|
68
|
+
while (!isValidEmail(email)) {
|
|
69
|
+
email = await prompt(rl, ' Email: ');
|
|
70
|
+
if (!isValidEmail(email)) {
|
|
71
|
+
console.log(" That doesn't look like a valid email. Try again.");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// ── Step 2: tenant_id ──
|
|
75
|
+
let tenantId = '';
|
|
76
|
+
while (!isValidTenantId(tenantId)) {
|
|
77
|
+
tenantId = (await prompt(rl, ' Tenant name (lowercase, hyphens ok, e.g. acme-corp): ')).toLowerCase();
|
|
78
|
+
if (!isValidTenantId(tenantId)) {
|
|
79
|
+
console.log(" Tenant name needs to be lowercase letters/digits/hyphens, 2-41 chars.");
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// ── Step 3: tier ──
|
|
83
|
+
let tier = 'free';
|
|
84
|
+
let tierAnswer = '';
|
|
85
|
+
while (!['free', 'pro', 'enterprise', ''].includes(tierAnswer)) {
|
|
86
|
+
tierAnswer = (await prompt(rl, ' Tier [free / pro / enterprise] (default: free): ')).toLowerCase();
|
|
87
|
+
if (tierAnswer === '')
|
|
88
|
+
tierAnswer = 'free';
|
|
89
|
+
if (!['free', 'pro', 'enterprise'].includes(tierAnswer)) {
|
|
90
|
+
console.log(" I only know free, pro, or enterprise.");
|
|
91
|
+
tierAnswer = '';
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
tier = tierAnswer;
|
|
95
|
+
// ── Step 4: LLM provider ──
|
|
96
|
+
const providerOptions = ['anthropic', 'openai', 'deepseek', 'google', 'openrouter', 'ollama'];
|
|
97
|
+
let provider = '';
|
|
98
|
+
while (!providerOptions.includes(provider)) {
|
|
99
|
+
const p = (await prompt(rl, ` Which LLM provider? [${providerOptions.join(' / ')}]: `)).toLowerCase();
|
|
100
|
+
if (providerOptions.includes(p)) {
|
|
101
|
+
provider = p;
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
console.log(` I don't recognize "${p}". Pick one of: ${providerOptions.join(', ')}.`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// ── Step 5: API key ──
|
|
108
|
+
let llmKey = '';
|
|
109
|
+
if (provider === 'ollama') {
|
|
110
|
+
llmKey = 'local';
|
|
111
|
+
console.log(' (Ollama is local — no API key needed.)');
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
while (!llmKey) {
|
|
115
|
+
llmKey = await prompt(rl, ` Paste your ${provider} API key: `);
|
|
116
|
+
if (!llmKey)
|
|
117
|
+
console.log(' I need the key to set up your provider.');
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// ── Step 6: POST to server ──
|
|
121
|
+
console.log('');
|
|
122
|
+
console.log(" Validating your key with the provider...");
|
|
123
|
+
let response;
|
|
124
|
+
try {
|
|
125
|
+
const resp = await fetch(`${HARNESS_URL}/api/onboarding/self-issue`, {
|
|
126
|
+
method: 'POST',
|
|
127
|
+
headers: { 'Content-Type': 'application/json' },
|
|
128
|
+
body: JSON.stringify({ email, tenant_id: tenantId, tier, provider, llm_key: llmKey }),
|
|
129
|
+
});
|
|
130
|
+
response = await resp.json();
|
|
131
|
+
if (!resp.ok || !response.ok || !response.license) {
|
|
132
|
+
console.log(` ${response.error || `Server returned ${resp.status} — try again later.`}`);
|
|
133
|
+
return { ok: false, error: response.error };
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
138
|
+
console.log(` I couldn't reach the server: ${msg}`);
|
|
139
|
+
return { ok: false, error: msg };
|
|
140
|
+
}
|
|
141
|
+
// ── Step 7: persist license ──
|
|
142
|
+
if (!existsSync(ARIA_DIR))
|
|
143
|
+
mkdirSync(ARIA_DIR, { recursive: true, mode: 0o700 });
|
|
144
|
+
const licenseRecord = {
|
|
145
|
+
...response.claims,
|
|
146
|
+
harnessToken: response.license.token,
|
|
147
|
+
jti: response.license.jti,
|
|
148
|
+
tier: response.license.tier,
|
|
149
|
+
exp: response.claims?.exp,
|
|
150
|
+
sub: response.claims?.sub ?? tenantId,
|
|
151
|
+
email,
|
|
152
|
+
issuedAt: new Date().toISOString(),
|
|
153
|
+
};
|
|
154
|
+
writeFileSync(LICENSE_PATH, JSON.stringify(licenseRecord, null, 2) + '\n', { mode: 0o600 });
|
|
155
|
+
// ── Step 8: persist provider/key config ──
|
|
156
|
+
// By this point the while-loop above has exited with a valid Provider —
|
|
157
|
+
// narrow the union type for TypeScript.
|
|
158
|
+
const validatedProvider = provider;
|
|
159
|
+
const configRecord = {
|
|
160
|
+
model: { provider: validatedProvider, model: defaultModelFor(validatedProvider), apiKey: llmKey },
|
|
161
|
+
tenantId,
|
|
162
|
+
email,
|
|
163
|
+
};
|
|
164
|
+
writeFileSync(CONFIG_PATH, JSON.stringify(configRecord, null, 2) + '\n', { mode: 0o600 });
|
|
165
|
+
// ── Step 9: install hooks ──
|
|
166
|
+
console.log('');
|
|
167
|
+
console.log(" License issued. Saving your config and binding my gates to your Claude Code...");
|
|
168
|
+
const hookResult = await installHooks({ force: false });
|
|
169
|
+
if (!hookResult.ok) {
|
|
170
|
+
console.log(` License saved, but I couldn't install the gates: ${hookResult.error}`);
|
|
171
|
+
console.log(" Run 'aria install-hooks' manually when you're ready.");
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
console.log(` Done. I've installed ${hookResult.installed.length} gate(s) into ~/.claude/hooks and merged them into your settings.json.`);
|
|
175
|
+
}
|
|
176
|
+
rl.close();
|
|
177
|
+
console.log('');
|
|
178
|
+
console.log(` You're set up. License jti: ${response.license.jti}, tier: ${tier}, provider: ${provider}.`);
|
|
179
|
+
console.log(" Open a fresh Claude Code session — every Bash, Edit, Write, and Stop event now runs through my cognition gates.");
|
|
180
|
+
console.log(" Or talk to me directly: just type 'aria' again.");
|
|
181
|
+
console.log('');
|
|
182
|
+
return { ok: true };
|
|
183
|
+
}
|
|
184
|
+
finally {
|
|
185
|
+
rl.close();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
function defaultModelFor(provider) {
|
|
189
|
+
const defaults = {
|
|
190
|
+
anthropic: 'claude-sonnet-4-20250514',
|
|
191
|
+
openai: 'gpt-4o',
|
|
192
|
+
deepseek: 'deepseek-chat',
|
|
193
|
+
google: 'gemini-2.5-pro-preview-05-06',
|
|
194
|
+
openrouter: 'openai/gpt-4o',
|
|
195
|
+
ollama: 'llama3',
|
|
196
|
+
};
|
|
197
|
+
return defaults[provider];
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=onboarding-wizard.js.map
|