@inkeep/agents-cli 0.70.5 → 0.70.7

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.
@@ -1,6 +1,7 @@
1
1
  import { checkKeychainAvailability, getKeychainUnavailableMessage, loadCredentials, saveCredentials } from "../utils/credentials.js";
2
2
  import { ProfileManager } from "../utils/profiles/profile-manager.js";
3
3
  import "../utils/profiles/index.js";
4
+ import { USER_AGENT } from "../utils/version-check.js";
4
5
  import * as p from "@clack/prompts";
5
6
  import chalk from "chalk";
6
7
  import open from "open";
@@ -29,7 +30,10 @@ async function pollForToken(cloudUrl, deviceCode, clientId, initialInterval) {
29
30
  await sleep(interval * 1e3);
30
31
  const data = await (await fetch(`${cloudUrl}/api/auth/device/token`, {
31
32
  method: "POST",
32
- headers: { "Content-Type": "application/json" },
33
+ headers: {
34
+ "Content-Type": "application/json",
35
+ "User-Agent": USER_AGENT
36
+ },
33
37
  body: JSON.stringify({
34
38
  grant_type: "urn:ietf:params:oauth:grant-type:device_code",
35
39
  device_code: deviceCode,
@@ -51,11 +55,17 @@ async function pollForToken(cloudUrl, deviceCode, clientId, initialInterval) {
51
55
  * Fetch user info and organization after authentication
52
56
  */
53
57
  async function fetchUserInfo(cloudUrl, accessToken) {
54
- const sessionResponse = await fetch(`${cloudUrl}/api/auth/get-session`, { headers: { Authorization: `Bearer ${accessToken}` } });
58
+ const sessionResponse = await fetch(`${cloudUrl}/api/auth/get-session`, { headers: {
59
+ Authorization: `Bearer ${accessToken}`,
60
+ "User-Agent": USER_AGENT
61
+ } });
55
62
  if (!sessionResponse.ok) throw new Error("Failed to fetch user session");
56
63
  const user = (await sessionResponse.json()).user;
57
64
  if (!user) throw new Error("No user found in session");
58
- const orgResponse = await fetch(`${cloudUrl}/manage/api/cli/me`, { headers: { Authorization: `Bearer ${accessToken}` } });
65
+ const orgResponse = await fetch(`${cloudUrl}/manage/api/cli/me`, { headers: {
66
+ Authorization: `Bearer ${accessToken}`,
67
+ "User-Agent": USER_AGENT
68
+ } });
59
69
  if (!orgResponse.ok) throw new Error("Failed to fetch organization info. Please ensure that you are a member of an organization.");
60
70
  const orgData = await orgResponse.json();
61
71
  return {
@@ -128,7 +138,10 @@ async function loginCommand(options = {}) {
128
138
  s.start("Requesting device code...");
129
139
  const deviceCodeResponse = await fetch(`${manageApiUrl}/api/auth/device/code`, {
130
140
  method: "POST",
131
- headers: { "Content-Type": "application/json" },
141
+ headers: {
142
+ "Content-Type": "application/json",
143
+ "User-Agent": USER_AGENT
144
+ },
132
145
  body: JSON.stringify({ client_id: "inkeep-cli" })
133
146
  });
134
147
  if (!deviceCodeResponse.ok) {
@@ -1 +1 @@
1
- {"version":3,"file":"login.js","names":[],"sources":["../../src/commands/login.ts"],"sourcesContent":["import * as p from '@clack/prompts';\nimport chalk from 'chalk';\nimport open from 'open';\nimport {\n checkKeychainAvailability,\n getKeychainUnavailableMessage,\n loadCredentials,\n saveCredentials,\n} from '../utils/credentials';\nimport { ProfileManager } from '../utils/profiles';\n\nexport interface LoginOptions {\n profile?: string;\n}\n\n/**\n * Format user code as XXXX-XXXX for display\n */\nfunction formatUserCode(code: string): string {\n const cleaned = code.replace(/[^A-Z0-9]/gi, '').toUpperCase();\n if (cleaned.length === 8) {\n return `${cleaned.slice(0, 4)}-${cleaned.slice(4)}`;\n }\n return cleaned;\n}\n\n/**\n * Sleep for a specified number of milliseconds\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Poll the device token endpoint until authorization is complete\n */\nasync function pollForToken(\n cloudUrl: string,\n deviceCode: string,\n clientId: string,\n initialInterval: number\n): Promise<string> {\n let interval = initialInterval;\n\n while (true) {\n await sleep(interval * 1000);\n\n const response = await fetch(`${cloudUrl}/api/auth/device/token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n grant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n device_code: deviceCode,\n client_id: clientId,\n }),\n });\n\n const data = await response.json();\n\n if (data.access_token) {\n return data.access_token;\n }\n\n if (data.error === 'authorization_pending') {\n // User hasn't approved yet, keep polling\n continue;\n }\n\n if (data.error === 'slow_down') {\n // Back off polling interval\n interval += 5;\n continue;\n }\n\n if (data.error === 'expired_token') {\n throw new Error('Device code expired. Please try again.');\n }\n\n if (data.error === 'access_denied') {\n throw new Error('Authorization denied.');\n }\n\n throw new Error(data.error || data.message || 'Unknown error during authorization');\n }\n}\n\n/**\n * Fetch user info and organization after authentication\n */\nasync function fetchUserInfo(\n cloudUrl: string,\n accessToken: string\n): Promise<{\n user: { id: string; email: string; name?: string };\n organization: { id: string; name: string; slug: string };\n}> {\n // First, get the session to get user info\n const sessionResponse = await fetch(`${cloudUrl}/api/auth/get-session`, {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n },\n });\n\n if (!sessionResponse.ok) {\n throw new Error('Failed to fetch user session');\n }\n\n const sessionData = await sessionResponse.json();\n const user = sessionData.user;\n\n if (!user) {\n throw new Error('No user found in session');\n }\n\n // Get user's organization\n const orgResponse = await fetch(`${cloudUrl}/manage/api/cli/me`, {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n },\n });\n\n if (!orgResponse.ok) {\n throw new Error(\n 'Failed to fetch organization info. Please ensure that you are a member of an organization.'\n );\n }\n\n const orgData = await orgResponse.json();\n\n return {\n user: {\n id: user.id,\n email: user.email,\n name: user.name,\n },\n organization: orgData.organization,\n };\n}\n\nexport async function loginCommand(options: LoginOptions = {}): Promise<void> {\n const profileManager = new ProfileManager();\n\n // Resolve profile to use\n let profileName: string;\n let credentialKey: string;\n let manageApiUrl: string;\n let manageUiUrl: string;\n\n try {\n if (options.profile) {\n const profile = profileManager.getProfile(options.profile);\n if (!profile) {\n console.error(chalk.red(`Profile '${options.profile}' not found.`));\n console.log(chalk.gray('Run \"inkeep profile list\" to see available profiles.'));\n process.exit(1);\n }\n profileName = options.profile;\n credentialKey = profile.credential;\n manageApiUrl = profile.remote.api;\n manageUiUrl = profile.remote.manageUi;\n } else {\n const activeProfile = profileManager.getActiveProfile();\n profileName = activeProfile.name;\n credentialKey = activeProfile.credential;\n manageApiUrl = activeProfile.remote.api;\n manageUiUrl = activeProfile.remote.manageUi;\n }\n } catch {\n // No profile configured, use defaults\n profileName = 'default';\n credentialKey = 'inkeep-cloud';\n manageApiUrl = 'https://agents-api.inkeep.com';\n manageUiUrl = 'https://manage.inkeep.com';\n }\n\n console.log(chalk.gray(`Using profile: ${profileName}`));\n\n // Check if keychain is available\n const { available, reason } = await checkKeychainAvailability();\n if (!available) {\n console.error(chalk.red('Error:'), getKeychainUnavailableMessage(reason));\n console.log();\n console.log(chalk.yellow('For CI/CD environments without keychain access:'));\n console.log(chalk.gray(' Set INKEEP_API_KEY environment variable instead of using login.'));\n console.log(chalk.gray(' See: https://docs.inkeep.com/cli/cicd'));\n process.exit(1);\n }\n\n // Check if already logged in for this profile\n const existingCredentials = await loadCredentials(credentialKey);\n if (existingCredentials) {\n const continueLogin = await p.confirm({\n message: `Already logged in as ${chalk.cyan(existingCredentials.userEmail)} for profile '${profileName}'. Continue with new login?`,\n initialValue: false,\n });\n\n if (p.isCancel(continueLogin)) {\n p.cancel('Login cancelled');\n process.exit(0);\n }\n\n if (!continueLogin) {\n console.log(chalk.gray('Login cancelled. You are still logged in.'));\n return;\n }\n }\n\n const s = p.spinner();\n\n try {\n // Request device code\n s.start('Requesting device code...');\n\n const deviceCodeResponse = await fetch(`${manageApiUrl}/api/auth/device/code`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ client_id: 'inkeep-cli' }),\n });\n\n if (!deviceCodeResponse.ok) {\n const errorData = await deviceCodeResponse.json().catch(() => ({}));\n throw new Error(\n errorData.message || `Failed to get device code: ${deviceCodeResponse.statusText}`\n );\n }\n\n const { device_code, user_code, interval } = await deviceCodeResponse.json();\n\n s.stop('Device code received');\n\n // Display instructions\n console.log();\n console.log(chalk.bold('To authenticate, visit:'));\n console.log(chalk.cyan(` ${manageUiUrl}/device?user_code=${user_code}`));\n console.log();\n console.log(chalk.bold('And enter code:'));\n console.log(chalk.yellow.bold(` ${formatUserCode(user_code)}`));\n console.log();\n\n // Try to open browser automatically\n try {\n await open(`${manageUiUrl}/device?user_code=${user_code}`);\n console.log(chalk.gray(' (Browser opened automatically)'));\n console.log();\n } catch {\n // Browser opening failed, user can copy the URL manually\n }\n\n // Poll for token\n s.start('Waiting for authorization...');\n const accessToken = await pollForToken(manageApiUrl, device_code, 'inkeep-cli', interval || 5);\n s.stop('Authorized!');\n\n // Fetch user info and organization\n s.start('Fetching account info...');\n const userInfo = await fetchUserInfo(manageApiUrl, accessToken);\n s.stop('Account info retrieved');\n\n // Store credentials under the profile's credential key\n await saveCredentials(\n {\n accessToken,\n userId: userInfo.user.id,\n userEmail: userInfo.user.email,\n organizationId: userInfo.organization.id,\n organizationName: userInfo.organization.name,\n createdAt: new Date().toISOString(),\n },\n credentialKey\n );\n\n // Success message\n console.log();\n console.log(chalk.green('✓'), `Logged in as ${chalk.cyan(userInfo.user.email)}`);\n console.log(chalk.green('✓'), `Organization: ${chalk.cyan(userInfo.organization.name)}`);\n console.log(chalk.green('✓'), `Profile: ${chalk.cyan(profileName)}`);\n } catch (error) {\n s.stop('Login failed');\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\n console.error(chalk.red('Error:'), errorMessage);\n process.exit(1);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"login.js","names":[],"sources":["../../src/commands/login.ts"],"sourcesContent":["import * as p from '@clack/prompts';\nimport chalk from 'chalk';\nimport open from 'open';\nimport {\n checkKeychainAvailability,\n getKeychainUnavailableMessage,\n loadCredentials,\n saveCredentials,\n} from '../utils/credentials';\nimport { ProfileManager } from '../utils/profiles';\nimport { USER_AGENT } from '../utils/version-check';\n\nexport interface LoginOptions {\n profile?: string;\n}\n\n/**\n * Format user code as XXXX-XXXX for display\n */\nfunction formatUserCode(code: string): string {\n const cleaned = code.replace(/[^A-Z0-9]/gi, '').toUpperCase();\n if (cleaned.length === 8) {\n return `${cleaned.slice(0, 4)}-${cleaned.slice(4)}`;\n }\n return cleaned;\n}\n\n/**\n * Sleep for a specified number of milliseconds\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Poll the device token endpoint until authorization is complete\n */\nasync function pollForToken(\n cloudUrl: string,\n deviceCode: string,\n clientId: string,\n initialInterval: number\n): Promise<string> {\n let interval = initialInterval;\n\n while (true) {\n await sleep(interval * 1000);\n\n const response = await fetch(`${cloudUrl}/api/auth/device/token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'User-Agent': USER_AGENT },\n body: JSON.stringify({\n grant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n device_code: deviceCode,\n client_id: clientId,\n }),\n });\n\n const data = await response.json();\n\n if (data.access_token) {\n return data.access_token;\n }\n\n if (data.error === 'authorization_pending') {\n // User hasn't approved yet, keep polling\n continue;\n }\n\n if (data.error === 'slow_down') {\n // Back off polling interval\n interval += 5;\n continue;\n }\n\n if (data.error === 'expired_token') {\n throw new Error('Device code expired. Please try again.');\n }\n\n if (data.error === 'access_denied') {\n throw new Error('Authorization denied.');\n }\n\n throw new Error(data.error || data.message || 'Unknown error during authorization');\n }\n}\n\n/**\n * Fetch user info and organization after authentication\n */\nasync function fetchUserInfo(\n cloudUrl: string,\n accessToken: string\n): Promise<{\n user: { id: string; email: string; name?: string };\n organization: { id: string; name: string; slug: string };\n}> {\n // First, get the session to get user info\n const sessionResponse = await fetch(`${cloudUrl}/api/auth/get-session`, {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'User-Agent': USER_AGENT,\n },\n });\n\n if (!sessionResponse.ok) {\n throw new Error('Failed to fetch user session');\n }\n\n const sessionData = await sessionResponse.json();\n const user = sessionData.user;\n\n if (!user) {\n throw new Error('No user found in session');\n }\n\n // Get user's organization\n const orgResponse = await fetch(`${cloudUrl}/manage/api/cli/me`, {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'User-Agent': USER_AGENT,\n },\n });\n\n if (!orgResponse.ok) {\n throw new Error(\n 'Failed to fetch organization info. Please ensure that you are a member of an organization.'\n );\n }\n\n const orgData = await orgResponse.json();\n\n return {\n user: {\n id: user.id,\n email: user.email,\n name: user.name,\n },\n organization: orgData.organization,\n };\n}\n\nexport async function loginCommand(options: LoginOptions = {}): Promise<void> {\n const profileManager = new ProfileManager();\n\n // Resolve profile to use\n let profileName: string;\n let credentialKey: string;\n let manageApiUrl: string;\n let manageUiUrl: string;\n\n try {\n if (options.profile) {\n const profile = profileManager.getProfile(options.profile);\n if (!profile) {\n console.error(chalk.red(`Profile '${options.profile}' not found.`));\n console.log(chalk.gray('Run \"inkeep profile list\" to see available profiles.'));\n process.exit(1);\n }\n profileName = options.profile;\n credentialKey = profile.credential;\n manageApiUrl = profile.remote.api;\n manageUiUrl = profile.remote.manageUi;\n } else {\n const activeProfile = profileManager.getActiveProfile();\n profileName = activeProfile.name;\n credentialKey = activeProfile.credential;\n manageApiUrl = activeProfile.remote.api;\n manageUiUrl = activeProfile.remote.manageUi;\n }\n } catch {\n // No profile configured, use defaults\n profileName = 'default';\n credentialKey = 'inkeep-cloud';\n manageApiUrl = 'https://agents-api.inkeep.com';\n manageUiUrl = 'https://manage.inkeep.com';\n }\n\n console.log(chalk.gray(`Using profile: ${profileName}`));\n\n // Check if keychain is available\n const { available, reason } = await checkKeychainAvailability();\n if (!available) {\n console.error(chalk.red('Error:'), getKeychainUnavailableMessage(reason));\n console.log();\n console.log(chalk.yellow('For CI/CD environments without keychain access:'));\n console.log(chalk.gray(' Set INKEEP_API_KEY environment variable instead of using login.'));\n console.log(chalk.gray(' See: https://docs.inkeep.com/cli/cicd'));\n process.exit(1);\n }\n\n // Check if already logged in for this profile\n const existingCredentials = await loadCredentials(credentialKey);\n if (existingCredentials) {\n const continueLogin = await p.confirm({\n message: `Already logged in as ${chalk.cyan(existingCredentials.userEmail)} for profile '${profileName}'. Continue with new login?`,\n initialValue: false,\n });\n\n if (p.isCancel(continueLogin)) {\n p.cancel('Login cancelled');\n process.exit(0);\n }\n\n if (!continueLogin) {\n console.log(chalk.gray('Login cancelled. You are still logged in.'));\n return;\n }\n }\n\n const s = p.spinner();\n\n try {\n // Request device code\n s.start('Requesting device code...');\n\n const deviceCodeResponse = await fetch(`${manageApiUrl}/api/auth/device/code`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'User-Agent': USER_AGENT },\n body: JSON.stringify({ client_id: 'inkeep-cli' }),\n });\n\n if (!deviceCodeResponse.ok) {\n const errorData = await deviceCodeResponse.json().catch(() => ({}));\n throw new Error(\n errorData.message || `Failed to get device code: ${deviceCodeResponse.statusText}`\n );\n }\n\n const { device_code, user_code, interval } = await deviceCodeResponse.json();\n\n s.stop('Device code received');\n\n // Display instructions\n console.log();\n console.log(chalk.bold('To authenticate, visit:'));\n console.log(chalk.cyan(` ${manageUiUrl}/device?user_code=${user_code}`));\n console.log();\n console.log(chalk.bold('And enter code:'));\n console.log(chalk.yellow.bold(` ${formatUserCode(user_code)}`));\n console.log();\n\n // Try to open browser automatically\n try {\n await open(`${manageUiUrl}/device?user_code=${user_code}`);\n console.log(chalk.gray(' (Browser opened automatically)'));\n console.log();\n } catch {\n // Browser opening failed, user can copy the URL manually\n }\n\n // Poll for token\n s.start('Waiting for authorization...');\n const accessToken = await pollForToken(manageApiUrl, device_code, 'inkeep-cli', interval || 5);\n s.stop('Authorized!');\n\n // Fetch user info and organization\n s.start('Fetching account info...');\n const userInfo = await fetchUserInfo(manageApiUrl, accessToken);\n s.stop('Account info retrieved');\n\n // Store credentials under the profile's credential key\n await saveCredentials(\n {\n accessToken,\n userId: userInfo.user.id,\n userEmail: userInfo.user.email,\n organizationId: userInfo.organization.id,\n organizationName: userInfo.organization.name,\n createdAt: new Date().toISOString(),\n },\n credentialKey\n );\n\n // Success message\n console.log();\n console.log(chalk.green('✓'), `Logged in as ${chalk.cyan(userInfo.user.email)}`);\n console.log(chalk.green('✓'), `Organization: ${chalk.cyan(userInfo.organization.name)}`);\n console.log(chalk.green('✓'), `Profile: ${chalk.cyan(profileName)}`);\n } catch (error) {\n s.stop('Login failed');\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\n console.error(chalk.red('Error:'), errorMessage);\n process.exit(1);\n }\n}\n"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { clearCredentials, loadCredentials } from "../utils/credentials.js";
2
2
  import { ProfileManager } from "../utils/profiles/profile-manager.js";
3
3
  import "../utils/profiles/index.js";
4
+ import { USER_AGENT } from "../utils/version-check.js";
4
5
  import * as p from "@clack/prompts";
5
6
  import chalk from "chalk";
6
7
 
@@ -10,6 +11,7 @@ async function logoutCommand(options = {}) {
10
11
  const s = p.spinner();
11
12
  let profileName;
12
13
  let credentialKey;
14
+ let manageApiUrl;
13
15
  try {
14
16
  if (options.profile) {
15
17
  const profile = profileManager.getProfile(options.profile);
@@ -20,21 +22,36 @@ async function logoutCommand(options = {}) {
20
22
  }
21
23
  profileName = options.profile;
22
24
  credentialKey = profile.credential;
25
+ manageApiUrl = profile.remote.api;
23
26
  } else {
24
27
  const activeProfile = profileManager.getActiveProfile();
25
28
  profileName = activeProfile.name;
26
29
  credentialKey = activeProfile.credential;
30
+ manageApiUrl = activeProfile.remote.api;
27
31
  }
28
32
  } catch {
29
33
  profileName = "default";
30
34
  credentialKey = "inkeep-cloud";
35
+ manageApiUrl = "https://agents-api.inkeep.com";
31
36
  }
32
37
  console.log(chalk.gray(`Using profile: ${profileName}`));
33
- if (!await loadCredentials(credentialKey)) {
38
+ const credentials = await loadCredentials(credentialKey);
39
+ if (!credentials) {
34
40
  console.log(chalk.yellow(`Not logged in for profile '${profileName}'.`));
35
41
  return;
36
42
  }
37
43
  s.start("Logging out...");
44
+ if (credentials.accessToken) try {
45
+ await fetch(`${manageApiUrl}/api/auth/sign-out`, {
46
+ method: "POST",
47
+ headers: {
48
+ Authorization: `Bearer ${credentials.accessToken}`,
49
+ "Content-Type": "application/json",
50
+ "User-Agent": USER_AGENT
51
+ },
52
+ body: "{}"
53
+ });
54
+ } catch {}
38
55
  try {
39
56
  if (await clearCredentials(credentialKey)) {
40
57
  s.stop("Logged out successfully");
@@ -1 +1 @@
1
- {"version":3,"file":"logout.js","names":[],"sources":["../../src/commands/logout.ts"],"sourcesContent":["import * as p from '@clack/prompts';\nimport chalk from 'chalk';\nimport { clearCredentials, loadCredentials } from '../utils/credentials';\nimport { ProfileManager } from '../utils/profiles';\n\nexport interface LogoutOptions {\n profile?: string;\n}\n\nexport async function logoutCommand(options: LogoutOptions = {}): Promise<void> {\n const profileManager = new ProfileManager();\n const s = p.spinner();\n\n // Resolve profile to use\n let profileName: string;\n let credentialKey: string;\n\n try {\n if (options.profile) {\n const profile = profileManager.getProfile(options.profile);\n if (!profile) {\n console.error(chalk.red(`Profile '${options.profile}' not found.`));\n console.log(chalk.gray('Run \"inkeep profile list\" to see available profiles.'));\n process.exit(1);\n }\n profileName = options.profile;\n credentialKey = profile.credential;\n } else {\n const activeProfile = profileManager.getActiveProfile();\n profileName = activeProfile.name;\n credentialKey = activeProfile.credential;\n }\n } catch {\n // No profile configured, use default\n profileName = 'default';\n credentialKey = 'inkeep-cloud';\n }\n\n console.log(chalk.gray(`Using profile: ${profileName}`));\n\n // Check if logged in for this profile\n const credentials = await loadCredentials(credentialKey);\n if (!credentials) {\n console.log(chalk.yellow(`Not logged in for profile '${profileName}'.`));\n return;\n }\n\n s.start('Logging out...');\n\n try {\n const cleared = await clearCredentials(credentialKey);\n\n if (cleared) {\n s.stop('Logged out successfully');\n console.log(chalk.green('✓'), `Logged out from profile '${chalk.cyan(profileName)}'.`);\n console.log(chalk.gray(` • Credential '${credentialKey}' removed from keychain.`));\n } else {\n s.stop('Logout completed');\n console.log(chalk.gray('No credentials were stored.'));\n }\n } catch (error) {\n s.stop('Logout failed');\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\n console.error(chalk.red('Error:'), errorMessage);\n process.exit(1);\n }\n}\n"],"mappings":";;;;;;;AASA,eAAsB,cAAc,UAAyB,EAAE,EAAiB;CAC9E,MAAM,iBAAiB,IAAI,gBAAgB;CAC3C,MAAM,IAAI,EAAE,SAAS;CAGrB,IAAI;CACJ,IAAI;AAEJ,KAAI;AACF,MAAI,QAAQ,SAAS;GACnB,MAAM,UAAU,eAAe,WAAW,QAAQ,QAAQ;AAC1D,OAAI,CAAC,SAAS;AACZ,YAAQ,MAAM,MAAM,IAAI,YAAY,QAAQ,QAAQ,cAAc,CAAC;AACnE,YAAQ,IAAI,MAAM,KAAK,yDAAuD,CAAC;AAC/E,YAAQ,KAAK,EAAE;;AAEjB,iBAAc,QAAQ;AACtB,mBAAgB,QAAQ;SACnB;GACL,MAAM,gBAAgB,eAAe,kBAAkB;AACvD,iBAAc,cAAc;AAC5B,mBAAgB,cAAc;;SAE1B;AAEN,gBAAc;AACd,kBAAgB;;AAGlB,SAAQ,IAAI,MAAM,KAAK,kBAAkB,cAAc,CAAC;AAIxD,KAAI,CADgB,MAAM,gBAAgB,cAAc,EACtC;AAChB,UAAQ,IAAI,MAAM,OAAO,8BAA8B,YAAY,IAAI,CAAC;AACxE;;AAGF,GAAE,MAAM,iBAAiB;AAEzB,KAAI;AAGF,MAFgB,MAAM,iBAAiB,cAAc,EAExC;AACX,KAAE,KAAK,0BAA0B;AACjC,WAAQ,IAAI,MAAM,MAAM,IAAI,EAAE,4BAA4B,MAAM,KAAK,YAAY,CAAC,IAAI;AACtF,WAAQ,IAAI,MAAM,KAAK,mBAAmB,cAAc,0BAA0B,CAAC;SAC9E;AACL,KAAE,KAAK,mBAAmB;AAC1B,WAAQ,IAAI,MAAM,KAAK,8BAA8B,CAAC;;UAEjD,OAAO;AACd,IAAE,KAAK,gBAAgB;EACvB,MAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU;AAC9D,UAAQ,MAAM,MAAM,IAAI,SAAS,EAAE,aAAa;AAChD,UAAQ,KAAK,EAAE"}
1
+ {"version":3,"file":"logout.js","names":[],"sources":["../../src/commands/logout.ts"],"sourcesContent":["import * as p from '@clack/prompts';\nimport chalk from 'chalk';\nimport { clearCredentials, loadCredentials } from '../utils/credentials';\nimport { ProfileManager } from '../utils/profiles';\nimport { USER_AGENT } from '../utils/version-check';\n\nexport interface LogoutOptions {\n profile?: string;\n}\n\nexport async function logoutCommand(options: LogoutOptions = {}): Promise<void> {\n const profileManager = new ProfileManager();\n const s = p.spinner();\n\n // Resolve profile to use\n let profileName: string;\n let credentialKey: string;\n let manageApiUrl: string;\n\n try {\n if (options.profile) {\n const profile = profileManager.getProfile(options.profile);\n if (!profile) {\n console.error(chalk.red(`Profile '${options.profile}' not found.`));\n console.log(chalk.gray('Run \"inkeep profile list\" to see available profiles.'));\n process.exit(1);\n }\n profileName = options.profile;\n credentialKey = profile.credential;\n manageApiUrl = profile.remote.api;\n } else {\n const activeProfile = profileManager.getActiveProfile();\n profileName = activeProfile.name;\n credentialKey = activeProfile.credential;\n manageApiUrl = activeProfile.remote.api;\n }\n } catch {\n // No profile configured, use default\n profileName = 'default';\n credentialKey = 'inkeep-cloud';\n manageApiUrl = 'https://agents-api.inkeep.com';\n }\n\n console.log(chalk.gray(`Using profile: ${profileName}`));\n\n // Check if logged in for this profile\n const credentials = await loadCredentials(credentialKey);\n if (!credentials) {\n console.log(chalk.yellow(`Not logged in for profile '${profileName}'.`));\n return;\n }\n\n s.start('Logging out...');\n\n // Best-effort revoke; if it fails the server session still expires naturally.\n if (credentials.accessToken) {\n try {\n await fetch(`${manageApiUrl}/api/auth/sign-out`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${credentials.accessToken}`,\n 'Content-Type': 'application/json',\n 'User-Agent': USER_AGENT,\n },\n body: '{}',\n });\n } catch {\n // Continue with local cleanup.\n }\n }\n\n try {\n const cleared = await clearCredentials(credentialKey);\n\n if (cleared) {\n s.stop('Logged out successfully');\n console.log(chalk.green('✓'), `Logged out from profile '${chalk.cyan(profileName)}'.`);\n console.log(chalk.gray(` • Credential '${credentialKey}' removed from keychain.`));\n } else {\n s.stop('Logout completed');\n console.log(chalk.gray('No credentials were stored.'));\n }\n } catch (error) {\n s.stop('Logout failed');\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\n console.error(chalk.red('Error:'), errorMessage);\n process.exit(1);\n }\n}\n"],"mappings":";;;;;;;;AAUA,eAAsB,cAAc,UAAyB,EAAE,EAAiB;CAC9E,MAAM,iBAAiB,IAAI,gBAAgB;CAC3C,MAAM,IAAI,EAAE,SAAS;CAGrB,IAAI;CACJ,IAAI;CACJ,IAAI;AAEJ,KAAI;AACF,MAAI,QAAQ,SAAS;GACnB,MAAM,UAAU,eAAe,WAAW,QAAQ,QAAQ;AAC1D,OAAI,CAAC,SAAS;AACZ,YAAQ,MAAM,MAAM,IAAI,YAAY,QAAQ,QAAQ,cAAc,CAAC;AACnE,YAAQ,IAAI,MAAM,KAAK,yDAAuD,CAAC;AAC/E,YAAQ,KAAK,EAAE;;AAEjB,iBAAc,QAAQ;AACtB,mBAAgB,QAAQ;AACxB,kBAAe,QAAQ,OAAO;SACzB;GACL,MAAM,gBAAgB,eAAe,kBAAkB;AACvD,iBAAc,cAAc;AAC5B,mBAAgB,cAAc;AAC9B,kBAAe,cAAc,OAAO;;SAEhC;AAEN,gBAAc;AACd,kBAAgB;AAChB,iBAAe;;AAGjB,SAAQ,IAAI,MAAM,KAAK,kBAAkB,cAAc,CAAC;CAGxD,MAAM,cAAc,MAAM,gBAAgB,cAAc;AACxD,KAAI,CAAC,aAAa;AAChB,UAAQ,IAAI,MAAM,OAAO,8BAA8B,YAAY,IAAI,CAAC;AACxE;;AAGF,GAAE,MAAM,iBAAiB;AAGzB,KAAI,YAAY,YACd,KAAI;AACF,QAAM,MAAM,GAAG,aAAa,qBAAqB;GAC/C,QAAQ;GACR,SAAS;IACP,eAAe,UAAU,YAAY;IACrC,gBAAgB;IAChB,cAAc;IACf;GACD,MAAM;GACP,CAAC;SACI;AAKV,KAAI;AAGF,MAFgB,MAAM,iBAAiB,cAAc,EAExC;AACX,KAAE,KAAK,0BAA0B;AACjC,WAAQ,IAAI,MAAM,MAAM,IAAI,EAAE,4BAA4B,MAAM,KAAK,YAAY,CAAC,IAAI;AACtF,WAAQ,IAAI,MAAM,KAAK,mBAAmB,cAAc,0BAA0B,CAAC;SAC9E;AACL,KAAE,KAAK,mBAAmB;AAC1B,WAAQ,IAAI,MAAM,KAAK,8BAA8B,CAAC;;UAEjD,OAAO;AACd,IAAE,KAAK,gBAAgB;EACvB,MAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU;AAC9D,UAAQ,MAAM,MAAM,IAAI,SAAS,EAAE,aAAa;AAChD,UAAQ,KAAK,EAAE"}
package/dist/package.js CHANGED
@@ -1,6 +1,6 @@
1
1
  //#region package.json
2
2
  var name = "@inkeep/agents-cli";
3
- var version = "0.70.5";
3
+ var version = "0.70.7";
4
4
 
5
5
  //#endregion
6
6
  export { name, version };
package/dist/program.js CHANGED
@@ -1,12 +1,12 @@
1
1
  import { addCommand } from "./commands/add.js";
2
2
  import { configGetCommand, configListCommand, configSetCommand } from "./commands/config.js";
3
3
  import { devCommand } from "./commands/dev.js";
4
+ import { PACKAGE_VERSION } from "./utils/version-check.js";
4
5
  import { loginCommand } from "./commands/login.js";
5
6
  import { initCommand } from "./commands/init.js";
6
7
  import { listAgentsCommand } from "./commands/list-agents.js";
7
8
  import { logoutCommand } from "./commands/logout.js";
8
9
  import { profileAddCommand, profileCurrentCommand, profileListCommand, profileRemoveCommand, profileUseCommand } from "./commands/profile.js";
9
- import { PACKAGE_VERSION } from "./utils/version-check.js";
10
10
  import { pullV4Command } from "./commands/pull-v4/introspect/index.js";
11
11
  import { pushCommand } from "./commands/push.js";
12
12
  import { statusCommand } from "./commands/status.js";
@@ -10,6 +10,11 @@ const DEFAULT_PACKAGE_NAME = name;
10
10
  */
11
11
  const PACKAGE_VERSION = version;
12
12
  /**
13
+ * User-Agent header sent on auth requests so the resulting session is
14
+ * identifiable in /profile (otherwise Node's default fetch sends "node").
15
+ */
16
+ const USER_AGENT = `inkeep-cli/${PACKAGE_VERSION} node/${process.versions.node} ${process.platform}/${process.arch}`;
17
+ /**
13
18
  * Fetch the latest version from npm registry
14
19
  */
15
20
  async function getLatestVersion(packageName = DEFAULT_PACKAGE_NAME) {
@@ -67,5 +72,5 @@ async function checkForUpdate() {
67
72
  const PACKAGE_CHANGELOG = "https://github.com/inkeep/agents/blob/main/agents-cli/CHANGELOG.md";
68
73
 
69
74
  //#endregion
70
- export { DEFAULT_PACKAGE_NAME, PACKAGE_CHANGELOG, PACKAGE_VERSION, checkForUpdate, compareVersions, getLatestVersion };
75
+ export { DEFAULT_PACKAGE_NAME, PACKAGE_CHANGELOG, PACKAGE_VERSION, USER_AGENT, checkForUpdate, compareVersions, getLatestVersion };
71
76
  //# sourceMappingURL=version-check.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version-check.js","names":["pkgJson.name","pkgJson.version"],"sources":["../../src/utils/version-check.ts"],"sourcesContent":["import pkgJson from '../../package.json' with { type: 'json' };\n\n/**\n * The default package name for version checks and updates\n */\nexport const DEFAULT_PACKAGE_NAME = pkgJson.name;\n\nexport interface VersionInfo {\n current: string;\n latest: string;\n needsUpdate: boolean;\n}\n\n/**\n * Get the current installed version from package.json\n */\nexport const PACKAGE_VERSION = pkgJson.version;\n\n/**\n * Fetch the latest version from npm registry\n */\nexport async function getLatestVersion(\n packageName: string = DEFAULT_PACKAGE_NAME\n): Promise<string> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 10000); // 10s timeout\n\n try {\n const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`, {\n signal: controller.signal,\n });\n if (!response.ok) {\n throw new Error(`Failed to fetch latest version: ${response.statusText}`);\n }\n const data = await response.json();\n return data.version;\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error('Unable to check for updates: Request timed out after 10 seconds');\n }\n throw new Error(\n `Unable to check for updates: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n } finally {\n clearTimeout(timeoutId);\n }\n}\n\n/**\n * Compare two semver versions\n * Returns: -1 if v1 < v2, 0 if v1 === v2, 1 if v1 > v2\n *\n * Note: This is a simplified semver comparison that handles basic major.minor.patch versions.\n * It does NOT handle pre-release versions (e.g., 1.0.0-beta.1) or build metadata (e.g., 1.0.0+build.1).\n * Pre-release tags and build metadata will be stripped before comparison.\n *\n * For the Inkeep CLI use case, this is sufficient as we only publish stable releases.\n */\nexport function compareVersions(v1: string, v2: string): number {\n // Strip pre-release and build metadata for comparison\n // Examples: \"1.0.0-beta.1\" -> \"1.0.0\", \"1.0.0+build.1\" -> \"1.0.0\"\n const cleanV1 = v1.split('-')[0].split('+')[0];\n const cleanV2 = v2.split('-')[0].split('+')[0];\n\n const parts1 = cleanV1.split('.').map(Number);\n const parts2 = cleanV2.split('.').map(Number);\n\n for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {\n const part1 = parts1[i] || 0;\n const part2 = parts2[i] || 0;\n\n if (part1 < part2) return -1;\n if (part1 > part2) return 1;\n }\n\n return 0;\n}\n\n/**\n * Check if an update is available\n */\nexport async function checkForUpdate(): Promise<VersionInfo> {\n const current = PACKAGE_VERSION;\n const latest = await getLatestVersion();\n const needsUpdate = compareVersions(current, latest) < 0;\n\n return {\n current,\n latest,\n needsUpdate,\n };\n}\n\n/**\n * Get the changelog URL for the package\n */\nexport const PACKAGE_CHANGELOG =\n 'https://github.com/inkeep/agents/blob/main/agents-cli/CHANGELOG.md';\n"],"mappings":";;;;;;AAKA,MAAa,uBAAuBA;;;;AAWpC,MAAa,kBAAkBC;;;;AAK/B,eAAsB,iBACpB,cAAsB,sBACL;CACjB,MAAM,aAAa,IAAI,iBAAiB;CACxC,MAAM,YAAY,iBAAiB,WAAW,OAAO,EAAE,IAAM;AAE7D,KAAI;EACF,MAAM,WAAW,MAAM,MAAM,8BAA8B,YAAY,UAAU,EAC/E,QAAQ,WAAW,QACpB,CAAC;AACF,MAAI,CAAC,SAAS,GACZ,OAAM,IAAI,MAAM,mCAAmC,SAAS,aAAa;AAG3E,UADa,MAAM,SAAS,MAAM,EACtB;UACL,OAAO;AACd,MAAI,iBAAiB,SAAS,MAAM,SAAS,aAC3C,OAAM,IAAI,MAAM,kEAAkE;AAEpF,QAAM,IAAI,MACR,gCAAgC,iBAAiB,QAAQ,MAAM,UAAU,kBAC1E;WACO;AACR,eAAa,UAAU;;;;;;;;;;;;;AAc3B,SAAgB,gBAAgB,IAAY,IAAoB;CAG9D,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC;CAC5C,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC;CAE5C,MAAM,SAAS,QAAQ,MAAM,IAAI,CAAC,IAAI,OAAO;CAC7C,MAAM,SAAS,QAAQ,MAAM,IAAI,CAAC,IAAI,OAAO;AAE7C,MAAK,IAAI,IAAI,GAAG,IAAI,KAAK,IAAI,OAAO,QAAQ,OAAO,OAAO,EAAE,KAAK;EAC/D,MAAM,QAAQ,OAAO,MAAM;EAC3B,MAAM,QAAQ,OAAO,MAAM;AAE3B,MAAI,QAAQ,MAAO,QAAO;AAC1B,MAAI,QAAQ,MAAO,QAAO;;AAG5B,QAAO;;;;;AAMT,eAAsB,iBAAuC;CAC3D,MAAM,UAAU;CAChB,MAAM,SAAS,MAAM,kBAAkB;AAGvC,QAAO;EACL;EACA;EACA,aALkB,gBAAgB,SAAS,OAAO,GAAG;EAMtD;;;;;AAMH,MAAa,oBACX"}
1
+ {"version":3,"file":"version-check.js","names":["pkgJson.name","pkgJson.version"],"sources":["../../src/utils/version-check.ts"],"sourcesContent":["import pkgJson from '../../package.json' with { type: 'json' };\n\n/**\n * The default package name for version checks and updates\n */\nexport const DEFAULT_PACKAGE_NAME = pkgJson.name;\n\nexport interface VersionInfo {\n current: string;\n latest: string;\n needsUpdate: boolean;\n}\n\n/**\n * Get the current installed version from package.json\n */\nexport const PACKAGE_VERSION = pkgJson.version;\n\n/**\n * User-Agent header sent on auth requests so the resulting session is\n * identifiable in /profile (otherwise Node's default fetch sends \"node\").\n */\nexport const USER_AGENT = `inkeep-cli/${PACKAGE_VERSION} node/${process.versions.node} ${process.platform}/${process.arch}`;\n\n/**\n * Fetch the latest version from npm registry\n */\nexport async function getLatestVersion(\n packageName: string = DEFAULT_PACKAGE_NAME\n): Promise<string> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 10000); // 10s timeout\n\n try {\n const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`, {\n signal: controller.signal,\n });\n if (!response.ok) {\n throw new Error(`Failed to fetch latest version: ${response.statusText}`);\n }\n const data = await response.json();\n return data.version;\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error('Unable to check for updates: Request timed out after 10 seconds');\n }\n throw new Error(\n `Unable to check for updates: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n } finally {\n clearTimeout(timeoutId);\n }\n}\n\n/**\n * Compare two semver versions\n * Returns: -1 if v1 < v2, 0 if v1 === v2, 1 if v1 > v2\n *\n * Note: This is a simplified semver comparison that handles basic major.minor.patch versions.\n * It does NOT handle pre-release versions (e.g., 1.0.0-beta.1) or build metadata (e.g., 1.0.0+build.1).\n * Pre-release tags and build metadata will be stripped before comparison.\n *\n * For the Inkeep CLI use case, this is sufficient as we only publish stable releases.\n */\nexport function compareVersions(v1: string, v2: string): number {\n // Strip pre-release and build metadata for comparison\n // Examples: \"1.0.0-beta.1\" -> \"1.0.0\", \"1.0.0+build.1\" -> \"1.0.0\"\n const cleanV1 = v1.split('-')[0].split('+')[0];\n const cleanV2 = v2.split('-')[0].split('+')[0];\n\n const parts1 = cleanV1.split('.').map(Number);\n const parts2 = cleanV2.split('.').map(Number);\n\n for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {\n const part1 = parts1[i] || 0;\n const part2 = parts2[i] || 0;\n\n if (part1 < part2) return -1;\n if (part1 > part2) return 1;\n }\n\n return 0;\n}\n\n/**\n * Check if an update is available\n */\nexport async function checkForUpdate(): Promise<VersionInfo> {\n const current = PACKAGE_VERSION;\n const latest = await getLatestVersion();\n const needsUpdate = compareVersions(current, latest) < 0;\n\n return {\n current,\n latest,\n needsUpdate,\n };\n}\n\n/**\n * Get the changelog URL for the package\n */\nexport const PACKAGE_CHANGELOG =\n 'https://github.com/inkeep/agents/blob/main/agents-cli/CHANGELOG.md';\n"],"mappings":";;;;;;AAKA,MAAa,uBAAuBA;;;;AAWpC,MAAa,kBAAkBC;;;;;AAM/B,MAAa,aAAa,cAAc,gBAAgB,QAAQ,QAAQ,SAAS,KAAK,GAAG,QAAQ,SAAS,GAAG,QAAQ;;;;AAKrH,eAAsB,iBACpB,cAAsB,sBACL;CACjB,MAAM,aAAa,IAAI,iBAAiB;CACxC,MAAM,YAAY,iBAAiB,WAAW,OAAO,EAAE,IAAM;AAE7D,KAAI;EACF,MAAM,WAAW,MAAM,MAAM,8BAA8B,YAAY,UAAU,EAC/E,QAAQ,WAAW,QACpB,CAAC;AACF,MAAI,CAAC,SAAS,GACZ,OAAM,IAAI,MAAM,mCAAmC,SAAS,aAAa;AAG3E,UADa,MAAM,SAAS,MAAM,EACtB;UACL,OAAO;AACd,MAAI,iBAAiB,SAAS,MAAM,SAAS,aAC3C,OAAM,IAAI,MAAM,kEAAkE;AAEpF,QAAM,IAAI,MACR,gCAAgC,iBAAiB,QAAQ,MAAM,UAAU,kBAC1E;WACO;AACR,eAAa,UAAU;;;;;;;;;;;;;AAc3B,SAAgB,gBAAgB,IAAY,IAAoB;CAG9D,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC;CAC5C,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC;CAE5C,MAAM,SAAS,QAAQ,MAAM,IAAI,CAAC,IAAI,OAAO;CAC7C,MAAM,SAAS,QAAQ,MAAM,IAAI,CAAC,IAAI,OAAO;AAE7C,MAAK,IAAI,IAAI,GAAG,IAAI,KAAK,IAAI,OAAO,QAAQ,OAAO,OAAO,EAAE,KAAK;EAC/D,MAAM,QAAQ,OAAO,MAAM;EAC3B,MAAM,QAAQ,OAAO,MAAM;AAE3B,MAAI,QAAQ,MAAO,QAAO;AAC1B,MAAI,QAAQ,MAAO,QAAO;;AAG5B,QAAO;;;;;AAMT,eAAsB,iBAAuC;CAC3D,MAAM,UAAU;CAChB,MAAM,SAAS,MAAM,kBAAkB;AAGvC,QAAO;EACL;EACA;EACA,aALkB,gBAAgB,SAAS,OAAO,GAAG;EAMtD;;;;;AAMH,MAAa,oBACX"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inkeep/agents-cli",
3
- "version": "0.70.5",
3
+ "version": "0.70.7",
4
4
  "description": "Inkeep CLI tool",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -39,8 +39,8 @@
39
39
  "tsx": "^4.20.5",
40
40
  "yaml": "^2.7.0",
41
41
  "zod": "^4.3.6",
42
- "@inkeep/agents-sdk": "^0.70.5",
43
- "@inkeep/agents-core": "^0.70.5"
42
+ "@inkeep/agents-sdk": "^0.70.7",
43
+ "@inkeep/agents-core": "^0.70.7"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@types/degit": "^2.8.6",