@aident-ai/cli 0.1.7 → 0.1.8-rc.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 +5 -0
- package/dist/cli.mjs +139 -41
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -28,6 +28,11 @@ aident update --check
|
|
|
28
28
|
aident --help
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
+
`aident setup` installs or updates the verified Aident Skill globally, migrates recognized clean project copies that
|
|
32
|
+
could shadow it, reuses or opens Aident authentication, verifies Loadout access, and records completion. Add
|
|
33
|
+
`--client-name <name>` to record the agent client and `--json` for a single machine-readable report; a configured
|
|
34
|
+
`AIDENT_TOKEN` makes the flow non-interactive.
|
|
35
|
+
|
|
31
36
|
Use `--oob` for browserless auth environments:
|
|
32
37
|
|
|
33
38
|
```bash
|
package/dist/cli.mjs
CHANGED
|
@@ -45,7 +45,7 @@ function logErr(text) {
|
|
|
45
45
|
// src/prompt.ts
|
|
46
46
|
function readLine(prompt, options = {}) {
|
|
47
47
|
return new Promise((resolve, reject) => {
|
|
48
|
-
process.stdout.write(prompt);
|
|
48
|
+
(options.output ?? process.stdout).write(prompt);
|
|
49
49
|
let data = "";
|
|
50
50
|
let timeout;
|
|
51
51
|
const cleanup = () => {
|
|
@@ -86,13 +86,14 @@ var LOGIN_COMPLETE_TIMEOUT_AFTER_OPENED_MS = 5 * 60 * 1000;
|
|
|
86
86
|
var LOGIN_OPENED_MESSAGE = `User opened the Aident login window. They have ${LOGIN_COMPLETE_TIMEOUT_AFTER_OPENED_MS / 60000} minutes to finish; if it fails, run \`aident login --oob\`, share the URL, and ask for the verification code.`;
|
|
87
87
|
async function login(options) {
|
|
88
88
|
const baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
89
|
+
const writeInfo = options.format === "json" ? logErr : logInfo;
|
|
89
90
|
if (options.oob)
|
|
90
|
-
return loginOob(baseUrl);
|
|
91
|
+
return loginOob(baseUrl, writeInfo, options.format === "json" ? process.stderr : process.stdout);
|
|
91
92
|
try {
|
|
92
|
-
return await loginLoopback(baseUrl);
|
|
93
|
+
return await loginLoopback(baseUrl, writeInfo);
|
|
93
94
|
} catch (err) {
|
|
94
95
|
logErr(`Loopback OAuth failed (${err instanceof Error ? err.message : String(err)}). Falling back to OOB flow.`);
|
|
95
|
-
return loginOob(baseUrl);
|
|
96
|
+
return loginOob(baseUrl, writeInfo, options.format === "json" ? process.stderr : process.stdout);
|
|
96
97
|
}
|
|
97
98
|
}
|
|
98
99
|
async function refreshToken(creds) {
|
|
@@ -127,11 +128,11 @@ async function logout(creds) {
|
|
|
127
128
|
return;
|
|
128
129
|
});
|
|
129
130
|
}
|
|
130
|
-
async function loginLoopback(baseUrl) {
|
|
131
|
+
async function loginLoopback(baseUrl, writeInfo) {
|
|
131
132
|
const verifier = base64UrlEncode(randomBytes(48));
|
|
132
133
|
const challenge = base64UrlEncode(createHash("sha256").update(verifier).digest());
|
|
133
134
|
const state = base64UrlEncode(randomBytes(16));
|
|
134
|
-
const { server, port, codePromise, setExpectedOpenRedirectUrl } = await startCallbackServer(state);
|
|
135
|
+
const { server, port, codePromise, setExpectedOpenRedirectUrl } = await startCallbackServer(state, writeInfo);
|
|
135
136
|
const redirectUri = `http://${LOOPBACK_HOST}:${port}/callback`;
|
|
136
137
|
const clientId = await registerClient(baseUrl, [redirectUri]);
|
|
137
138
|
const authorizeUrl = new URL(`${baseUrl}/api/mcp/oauth/authorize`);
|
|
@@ -145,8 +146,8 @@ async function loginLoopback(baseUrl) {
|
|
|
145
146
|
const authorizeUrlString = authorizeUrl.toString();
|
|
146
147
|
setExpectedOpenRedirectUrl(authorizeUrlString);
|
|
147
148
|
const loginOpenUrl = buildLoginOpenUrl(port, state, authorizeUrlString);
|
|
148
|
-
|
|
149
|
-
|
|
149
|
+
writeInfo(`Opening browser for Aident login...`);
|
|
150
|
+
writeInfo(`If the browser does not open, visit: ${authorizeUrlString}`);
|
|
150
151
|
openBrowser(shouldUseLoopbackBrowserHandoff() ? loginOpenUrl : authorizeUrlString);
|
|
151
152
|
let code;
|
|
152
153
|
try {
|
|
@@ -157,7 +158,7 @@ async function loginLoopback(baseUrl) {
|
|
|
157
158
|
const tok = await exchangeCode(baseUrl, clientId, code, redirectUri, verifier);
|
|
158
159
|
return buildCreds(baseUrl, clientId, tok);
|
|
159
160
|
}
|
|
160
|
-
async function loginOob(baseUrl) {
|
|
161
|
+
async function loginOob(baseUrl, writeInfo, promptOutput) {
|
|
161
162
|
const redirectUri = `${baseUrl}/mcp/oob`;
|
|
162
163
|
const verifier = base64UrlEncode(randomBytes(48));
|
|
163
164
|
const challenge = base64UrlEncode(createHash("sha256").update(verifier).digest());
|
|
@@ -174,11 +175,12 @@ async function loginOob(baseUrl) {
|
|
|
174
175
|
authorizeUrl.searchParams.set("code_challenge_method", "S256");
|
|
175
176
|
authorizeUrl.searchParams.set("state", state);
|
|
176
177
|
const authorizeUrlString = authorizeUrl.toString();
|
|
177
|
-
|
|
178
|
-
|
|
178
|
+
writeInfo(`Opening browser for Aident login...`);
|
|
179
|
+
writeInfo(`If the browser does not open, visit: ${authorizeUrlString}`);
|
|
179
180
|
openBrowser(authorizeUrlString);
|
|
180
|
-
|
|
181
|
+
writeInfo("After approving, paste the 8-digit verification code shown on the Aident page below.");
|
|
181
182
|
const pastedInput = await readLine("Paste 8-digit code here: ", {
|
|
183
|
+
output: promptOutput,
|
|
182
184
|
timeoutMs: LOGIN_COMPLETE_TIMEOUT_AFTER_OPENED_MS,
|
|
183
185
|
timeoutMessage: `Verification code not received within ${LOGIN_COMPLETE_TIMEOUT_AFTER_OPENED_MS / 1000}s; aborting.`
|
|
184
186
|
});
|
|
@@ -288,7 +290,7 @@ function updateCredsFromToken(creds, tok) {
|
|
|
288
290
|
expires_at: expiresAt
|
|
289
291
|
};
|
|
290
292
|
}
|
|
291
|
-
function startCallbackServer(expectedState) {
|
|
293
|
+
function startCallbackServer(expectedState, writeInfo = logInfo) {
|
|
292
294
|
return new Promise((resolve, reject) => {
|
|
293
295
|
let codeResolver = () => {
|
|
294
296
|
return;
|
|
@@ -310,7 +312,7 @@ function startCallbackServer(expectedState) {
|
|
|
310
312
|
};
|
|
311
313
|
const openTimeout = setTimeout(() => {
|
|
312
314
|
if (!opened) {
|
|
313
|
-
|
|
315
|
+
writeInfo(`Login window open signal not received within ${LOGIN_OPEN_TIMEOUT_MS / 1000}s; still waiting for login to finish.`);
|
|
314
316
|
}
|
|
315
317
|
}, LOGIN_OPEN_TIMEOUT_MS);
|
|
316
318
|
scheduleCompleteTimeout(LOGIN_COMPLETE_TIMEOUT_AFTER_OPENED_MS, `Authorization callback not received within ${LOGIN_COMPLETE_TIMEOUT_AFTER_OPENED_MS / 1000}s; aborting.`);
|
|
@@ -328,7 +330,7 @@ function startCallbackServer(expectedState) {
|
|
|
328
330
|
return;
|
|
329
331
|
opened = true;
|
|
330
332
|
clearTimeout(openTimeout);
|
|
331
|
-
|
|
333
|
+
writeInfo(LOGIN_OPENED_MESSAGE);
|
|
332
334
|
const timeoutMs = LOGIN_COMPLETE_TIMEOUT_AFTER_OPENED_MS;
|
|
333
335
|
scheduleCompleteTimeout(timeoutMs, `Login page opened but did not complete within ${timeoutMs / 1000}s; aborting.`);
|
|
334
336
|
};
|
|
@@ -596,7 +598,7 @@ function normalizeBaseUrl(url) {
|
|
|
596
598
|
}
|
|
597
599
|
|
|
598
600
|
// src/version.ts
|
|
599
|
-
var VERSION = "0.1.
|
|
601
|
+
var VERSION = "0.1.8-rc.0";
|
|
600
602
|
|
|
601
603
|
// src/catalogCache.ts
|
|
602
604
|
var CACHE_TTL_MS = 5 * 60 * 1000;
|
|
@@ -6449,7 +6451,7 @@ var LOCAL_HELP_COMMANDS = [
|
|
|
6449
6451
|
{ command: "config get <key>", description: "Read a single value" },
|
|
6450
6452
|
{ command: "packages add <playbook|intern>", description: "Enable an add-on package" },
|
|
6451
6453
|
{ command: "doctor", description: "Validate installation" },
|
|
6452
|
-
{ command: "setup", description: "
|
|
6454
|
+
{ command: "setup", description: "Install Aident globally, authenticate, and verify Loadout" },
|
|
6453
6455
|
{ command: "integrations migrate-local", description: "Plan migration from local MCP configs to Loadout" },
|
|
6454
6456
|
{ command: "<domain> <command> [--flag value ...] [--json]", description: "Run a catalog command after login" }
|
|
6455
6457
|
];
|
|
@@ -6467,7 +6469,7 @@ function getLocalHelp(version) {
|
|
|
6467
6469
|
"aident config get <key>",
|
|
6468
6470
|
"aident packages add <playbook|intern>",
|
|
6469
6471
|
"aident doctor",
|
|
6470
|
-
"aident setup",
|
|
6472
|
+
"aident setup [--client-name <name>]",
|
|
6471
6473
|
"aident integrations migrate-local",
|
|
6472
6474
|
"aident <domain> <command> [--flag value ...] [--json]"
|
|
6473
6475
|
],
|
|
@@ -9432,32 +9434,128 @@ function formatAidentUpdateReport(report) {
|
|
|
9432
9434
|
`);
|
|
9433
9435
|
}
|
|
9434
9436
|
async function runSetup(parsed) {
|
|
9435
|
-
|
|
9436
|
-
|
|
9437
|
-
|
|
9438
|
-
const input = (await readLine(`Base URL [${current}]: `)).trim();
|
|
9439
|
-
const chosen = input ? normalizeBaseUrl(input) : current;
|
|
9440
|
-
if (chosen !== current) {
|
|
9441
|
-
await setConfigValue("baseUrl", chosen);
|
|
9442
|
-
logInfo(`${colors.green}Saved${colors.reset} baseUrl = ${chosen}`);
|
|
9437
|
+
if (parsed.isHelp) {
|
|
9438
|
+
logInfo(renderSetupHelp());
|
|
9439
|
+
return;
|
|
9443
9440
|
}
|
|
9444
|
-
|
|
9445
|
-
|
|
9446
|
-
|
|
9447
|
-
|
|
9448
|
-
|
|
9449
|
-
|
|
9450
|
-
|
|
9451
|
-
|
|
9452
|
-
await
|
|
9453
|
-
|
|
9454
|
-
|
|
9455
|
-
|
|
9441
|
+
const report = { schemaVersion: 1, success: false };
|
|
9442
|
+
let errorCode = "invalid-input";
|
|
9443
|
+
try {
|
|
9444
|
+
const options = await getSetupOptions(parsed);
|
|
9445
|
+
errorCode = "setup-failed";
|
|
9446
|
+
report.baseUrl = options.baseUrl;
|
|
9447
|
+
report.clientName = options.clientName;
|
|
9448
|
+
if (options.persistBaseUrl)
|
|
9449
|
+
await setConfigValue("baseUrl", options.baseUrl);
|
|
9450
|
+
const update = await runAidentUpdate({
|
|
9451
|
+
baseUrl: options.baseUrl,
|
|
9452
|
+
currentVersion: VERSION,
|
|
9453
|
+
mode: "apply" /* Apply */,
|
|
9454
|
+
includeCli: true,
|
|
9455
|
+
includeSkill: true,
|
|
9456
|
+
includeProject: true
|
|
9457
|
+
});
|
|
9458
|
+
recordLoadoutUpdateTelemetry(options.baseUrl, getLoadoutUpdateCompletionTelemetry(update));
|
|
9459
|
+
report.update = update;
|
|
9460
|
+
if (!update.success) {
|
|
9461
|
+
throw new Error(update.errors?.[0]?.message ?? update.skill?.detail ?? "Aident installation was incomplete");
|
|
9462
|
+
}
|
|
9463
|
+
const skillVersion = update.skill?.targetVersion;
|
|
9464
|
+
let credentials = await readCredentials();
|
|
9465
|
+
const hasEnvironmentToken = !!process.env.AIDENT_TOKEN?.trim();
|
|
9466
|
+
if ((!credentials || !credentialsForBaseUrl(credentials, options.baseUrl)) && !hasEnvironmentToken) {
|
|
9467
|
+
credentials = await login({ baseUrl: options.baseUrl, format: parsed.format, oob: options.oob });
|
|
9468
|
+
await writeCredentials(credentials);
|
|
9469
|
+
}
|
|
9470
|
+
const client = await getAuthenticatedClient(["loadout"]);
|
|
9471
|
+
if (!client)
|
|
9472
|
+
throw new Error("Aident credentials could not be loaded after sign-in");
|
|
9473
|
+
const catalog = await fetchCatalog(client);
|
|
9474
|
+
if (!catalog)
|
|
9475
|
+
throw new Error("Aident Loadout command catalog is unavailable");
|
|
9476
|
+
const identity = await callWithRefresh(client, (activeClient) => activeClient.exec("account", "auth status", {}));
|
|
9477
|
+
if (!identity.body.success) {
|
|
9478
|
+
throw new Error(identity.body.error?.message ?? "Aident account verification failed");
|
|
9479
|
+
}
|
|
9480
|
+
report.authenticated = true;
|
|
9481
|
+
report.account = identity.body.data;
|
|
9482
|
+
const doctor = await runDoctor({
|
|
9483
|
+
baseUrl: options.baseUrl,
|
|
9484
|
+
configFile: getConfigFile(),
|
|
9485
|
+
credentialsFile: getCredentialsFile()
|
|
9486
|
+
});
|
|
9487
|
+
report.doctor = doctor;
|
|
9488
|
+
if (!doctor.ok)
|
|
9489
|
+
throw new Error("Aident installation verification failed");
|
|
9490
|
+
const completion = await callWithRefresh(client, (activeClient) => activeClient.exec("loadout", "setup complete", {
|
|
9491
|
+
clientName: options.clientName,
|
|
9492
|
+
...skillVersion ? { skillVersion } : {}
|
|
9493
|
+
}));
|
|
9494
|
+
if (!completion.body.success || !completion.body.data?.recorded) {
|
|
9495
|
+
throw new Error(completion.body.error?.message ?? "Aident setup completion was not recorded");
|
|
9456
9496
|
}
|
|
9497
|
+
report.completion = completion.body.data;
|
|
9498
|
+
report.success = true;
|
|
9499
|
+
await maybeOfferLocalIntegrationMigration(parsed);
|
|
9500
|
+
emitSetupReport(parsed.format, report);
|
|
9501
|
+
} catch (error) {
|
|
9502
|
+
report.error = {
|
|
9503
|
+
code: errorCode,
|
|
9504
|
+
message: error instanceof Error ? error.message : String(error)
|
|
9505
|
+
};
|
|
9506
|
+
emitSetupReport(parsed.format, report);
|
|
9507
|
+
process.exitCode = 1;
|
|
9457
9508
|
}
|
|
9458
|
-
|
|
9459
|
-
|
|
9460
|
-
|
|
9509
|
+
}
|
|
9510
|
+
async function getSetupOptions(parsed) {
|
|
9511
|
+
if (parsed.positional.length !== 1)
|
|
9512
|
+
throw new Error("Usage: aident setup [flags]");
|
|
9513
|
+
const allowedFlags = new Set(["base-url", "client-name", "oob"]);
|
|
9514
|
+
const unknownFlag = Object.keys(parsed.flags).find((flag) => !allowedFlags.has(flag));
|
|
9515
|
+
if (unknownFlag)
|
|
9516
|
+
throw new Error(`Unknown setup flag: --${unknownFlag}`);
|
|
9517
|
+
const rawBaseUrl = getOptionalStringFlag(parsed.flags, "base-url");
|
|
9518
|
+
const clientName = getOptionalStringFlag(parsed.flags, "client-name") ?? "Aident CLI";
|
|
9519
|
+
if (clientName.length > 160)
|
|
9520
|
+
throw new Error("--client-name must be at most 160 characters");
|
|
9521
|
+
return {
|
|
9522
|
+
baseUrl: rawBaseUrl ? normalizeBaseUrl(rawBaseUrl) : await resolveDefaultBaseUrl(),
|
|
9523
|
+
clientName,
|
|
9524
|
+
oob: getBooleanUpdateFlag(parsed.flags, "oob"),
|
|
9525
|
+
persistBaseUrl: !!rawBaseUrl
|
|
9526
|
+
};
|
|
9527
|
+
}
|
|
9528
|
+
function getOptionalStringFlag(flags, name) {
|
|
9529
|
+
const value = flags[name];
|
|
9530
|
+
if (value === undefined)
|
|
9531
|
+
return;
|
|
9532
|
+
if (typeof value !== "string" || !value.trim())
|
|
9533
|
+
throw new Error(`--${name} requires a value`);
|
|
9534
|
+
return value.trim();
|
|
9535
|
+
}
|
|
9536
|
+
function emitSetupReport(format, report) {
|
|
9537
|
+
if (format === "json") {
|
|
9538
|
+
logInfo(JSON.stringify(report));
|
|
9539
|
+
return;
|
|
9540
|
+
}
|
|
9541
|
+
if (report.success) {
|
|
9542
|
+
logInfo(`${colors.green}Aident Loadout setup is complete.${colors.reset}`);
|
|
9543
|
+
return;
|
|
9544
|
+
}
|
|
9545
|
+
const error = report.error;
|
|
9546
|
+
logErr(`${colors.red}Aident Loadout setup failed:${colors.reset} ${error?.message ?? "Unknown error"}`);
|
|
9547
|
+
}
|
|
9548
|
+
function renderSetupHelp() {
|
|
9549
|
+
return [
|
|
9550
|
+
`${colors.bold}AIDENT SETUP${colors.reset}`,
|
|
9551
|
+
"",
|
|
9552
|
+
"USAGE:",
|
|
9553
|
+
" aident setup [--client-name <name>] [--base-url <url>] [--oob] [--json]",
|
|
9554
|
+
"",
|
|
9555
|
+
"The setup command updates the CLI, installs or reconciles the verified Aident Skill globally, and opens sign-in when needed.",
|
|
9556
|
+
"It verifies Loadout access and records completion. AIDENT_TOKEN enables non-interactive authentication."
|
|
9557
|
+
].join(`
|
|
9558
|
+
`);
|
|
9461
9559
|
}
|
|
9462
9560
|
async function maybeOfferLocalIntegrationMigration(parsed) {
|
|
9463
9561
|
if (parsed.format !== "tui")
|
package/package.json
CHANGED