Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions src/cli/commands/add/__tests__/add-identity.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,7 @@ import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

describe('add identity command', () => {
describe('add credential command', () => {
let testDir: string;
let projectDir: string;

Expand All@@ -28,27 +28,27 @@ describe('add identity command', () => {

describe('validation', () => {
it('requires name flag', async () => {
const result = await runCLI(['add', 'identity', '--json'], projectDir);
const result = await runCLI(['add', 'credential', '--json'], projectDir);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
expect(json.error.includes('--name'), `Error: ${json.error}`).toBeTruthy();
});

it('requires api-key flag', async () => {
const result = await runCLI(['add', 'identity', '--name', 'test', '--json'], projectDir);
const result = await runCLI(['add', 'credential', '--name', 'test', '--json'], projectDir);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
expect(json.error.includes('--api-key'), `Error: ${json.error}`).toBeTruthy();
});
});

describe('identity creation', () => {
describe('credential creation', () => {
it('creates credential as top-level resource', async () => {
const identityName = `id${Date.now()}`;
const result = await runCLI(
['add', 'identity', '--name', identityName, '--api-key', 'test-key-123', '--json'],
['add', 'credential', '--name', identityName, '--api-key', 'test-key-123', '--json'],
projectDir
);

Expand All@@ -65,13 +65,13 @@ describe('add identity command', () => {
});
});

describe('oauth identity creation', () => {
describe('oauth credential creation', () => {
it('creates OAuth credential with discovery URL and scopes', async () => {
const identityName = `oauth-${Date.now()}`;
const result = await runCLI(
[
'add',
'identity',
'credential',
'--type',
'oauth',
'--name',
Expand Down
28 changes: 14 additions & 14 deletions src/cli/commands/add/__tests__/validate.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
import type {
AddAgentOptions,
AddCredentialOptions,
AddGatewayOptions,
AddGatewayTargetOptions,
AddIdentityOptions,
AddMemoryOptions,
} from '../types.js';
import {
validateAddAgentOptions,
validateAddCredentialOptions,
validateAddGatewayOptions,
validateAddGatewayTargetOptions,
validateAddIdentityOptions,
validateAddMemoryOptions,
} from '../validate.js';
import { existsSync, readFileSync } from 'fs';
Expand DownExpand Up@@ -75,7 +75,7 @@ const validMemoryOptions: AddMemoryOptions = {
strategies: 'SEMANTIC,SUMMARIZATION',
};

const validIdentityOptions: AddIdentityOptions = {
const validCredentialOptions: AddCredentialOptions = {
name: 'test-identity',
apiKey: 'test-key',
};
Expand DownExpand Up@@ -996,25 +996,25 @@ describe('validate', () => {
});
});

describe('validateAddIdentityOptions', () => {
describe('validateAddCredentialOptions', () => {
// AC23: Required fields validated
it('returns error for missing required fields', () => {
const requiredFields: { field: keyof AddIdentityOptions; error: string }[] = [
const requiredFields: { field: keyof AddCredentialOptions; error: string }[] = [
{ field: 'name', error: '--name is required' },
{ field: 'apiKey', error: '--api-key is required' },
];

for (const { field, error } of requiredFields) {
const opts = { ...validIdentityOptions, [field]: undefined };
const result = validateAddIdentityOptions(opts);
const opts = { ...validCredentialOptions, [field]: undefined };
const result = validateAddCredentialOptions(opts);
expect(result.valid, `Should fail for missing ${String(field)}`).toBe(false);
expect(result.error).toBe(error);
}
});

// AC25: Valid options pass
it('passes for valid options', () => {
expect(validateAddIdentityOptions(validIdentityOptions)).toEqual({ valid: true });
expect(validateAddCredentialOptions(validCredentialOptions)).toEqual({ valid: true });
});
});

Expand DownExpand Up@@ -1193,9 +1193,9 @@ describe('validate', () => {
});
});

describe('validateAddIdentityOptions OAuth', () => {
describe('validateAddCredentialOptions OAuth', () => {
it('passes for valid OAuth identity', () => {
const result = validateAddIdentityOptions({
const result = validateAddCredentialOptions({
name: 'my-oauth',
type: 'oauth',
discoveryUrl: 'https://auth.example.com/.well-known/openid-configuration',
Expand All@@ -1206,7 +1206,7 @@ describe('validate', () => {
});

it('returns error for OAuth without discovery-url', () => {
const result = validateAddIdentityOptions({
const result = validateAddCredentialOptions({
name: 'my-oauth',
type: 'oauth',
clientId: 'client123',
Expand All@@ -1217,7 +1217,7 @@ describe('validate', () => {
});

it('returns error for OAuth without client-id', () => {
const result = validateAddIdentityOptions({
const result = validateAddCredentialOptions({
name: 'my-oauth',
type: 'oauth',
discoveryUrl: 'https://auth.example.com',
Expand All@@ -1228,7 +1228,7 @@ describe('validate', () => {
});

it('returns error for OAuth without client-secret', () => {
const result = validateAddIdentityOptions({
const result = validateAddCredentialOptions({
name: 'my-oauth',
type: 'oauth',
discoveryUrl: 'https://auth.example.com',
Expand All@@ -1239,7 +1239,7 @@ describe('validate', () => {
});

it('still requires api-key for default type', () => {
const result = validateAddIdentityOptions({ name: 'my-key' });
const result = validateAddCredentialOptions({ name: 'my-key' });
expect(result.valid).toBe(false);
expect(result.error).toContain('--api-key');
});
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/add/command.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,7 +33,7 @@ export function registerAdd(program: Command): Command {
);
});

// Subcommands (agent, memory, identity, gateway, gateway-target) are registered
// Subcommands (agent, memory, credential, gateway, gateway-target) are registered
// via primitive.registerCommands() in cli.ts

return addCmd;
Expand Down
9 changes: 6 additions & 3 deletions src/cli/commands/add/types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -118,8 +118,8 @@ export interface AddMemoryResult {
error?: string;
}

// Identity types (v2: credential, no owner/user concept)
export interface AddIdentityOptions {
// Credential types (v2: credential, no owner/user concept)
export interface AddCredentialOptions {
name?: string;
type?: 'api-key' | 'oauth';
apiKey?: string;
Expand All@@ -130,7 +130,10 @@ export interface AddIdentityOptions {
json?: boolean;
}

export interface AddIdentityResult {
/** @deprecated Use AddCredentialOptions */
export type AddIdentityOptions = AddCredentialOptions;

export interface AddCredentialResult {
success: boolean;
credentialName?: string;
error?: string;
Expand Down
8 changes: 4 additions & 4 deletions src/cli/commands/add/validate.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,9 +19,9 @@ import { validateVpcOptions } from '../shared/vpc-utils';
import { validateJwtAuthorizerOptions } from './auth-options';
import type {
AddAgentOptions,
AddCredentialOptions,
AddGatewayOptions,
AddGatewayTargetOptions,
AddIdentityOptions,
AddMemoryOptions,
} from './types';
import { existsSync, readFileSync } from 'fs';
Expand DownExpand Up@@ -50,7 +50,7 @@ async function validateCredentialExists(credentialName: string): Promise<Validat
if (availableCredentials.length === 0) {
return {
valid: false,
error: `Credential "${credentialName}" not found. No credentials are configured. Add credentials using 'agentcore add identity'.`,
error: `Credential "${credentialName}" not found. No credentials are configured. Add credentials using 'agentcore add credential'.`,
};
}
return {
Expand DownExpand Up@@ -680,8 +680,8 @@ export function validateAddMemoryOptions(options: AddMemoryOptions): ValidationR
return { valid: true };
}

// Identity validation (v2: credential resource, no owner)
export function validateAddIdentityOptions(options: AddIdentityOptions): ValidationResult {
// Credential validation (v2: credential resource, no owner)
export function validateAddCredentialOptions(options: AddCredentialOptions): ValidationResult {
if (!options.name) {
return { valid: false, error: '--name is required' };
}
Expand Down
10 changes: 7 additions & 3 deletions src/cli/commands/deploy/command.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -90,7 +90,7 @@ async function handleDeployCLI(options: DeployOptions): Promise<void> {
if (options.diff) {
console.log(`\n✓ Diff complete for '${result.targetName}' (stack: ${result.stackName})`);
} else if (options.plan) {
console.log(`\n✓ Plan complete for '${result.targetName}' (stack: ${result.stackName})`);
console.log(`\n✓ Dry run complete for '${result.targetName}' (stack: ${result.stackName})`);
console.log('\nRun `agentcore deploy` to deploy.');
} else {
console.log(`\n✓ Deployed to '${result.targetName}' (stack: ${result.stackName})`);
Expand DownExpand Up@@ -136,23 +136,27 @@ export const registerDeploy = (program: Command) => {
.option('-y, --yes', 'Auto-confirm prompts, read credentials from env [non-interactive]')
.option('-v, --verbose', 'Show resource-level deployment events [non-interactive]')
.option('--json', 'Output as JSON [non-interactive]')
.option('--plan', 'Preview deployment without deploying (dry-run) [non-interactive]')
.option('--dry-run', 'Preview deployment without deploying [non-interactive]')
.option('--plan', 'Preview deployment without deploying (alias for --dry-run) [non-interactive]')
.option('--diff', 'Show CDK diff without deploying [non-interactive]')
.action(
async (cliOptions: {
target?: string;
yes?: boolean;
verbose?: boolean;
json?: boolean;
dryRun?: boolean;
plan?: boolean;
diff?: boolean;
}) => {
try {
requireProject();
if (cliOptions.json || cliOptions.target || cliOptions.plan || cliOptions.yes || cliOptions.verbose) {
const isDryRun = cliOptions.dryRun ?? cliOptions.plan;
if (cliOptions.json || cliOptions.target || isDryRun || cliOptions.yes || cliOptions.verbose) {
// CLI mode - any flag triggers non-interactive mode
const options = {
...cliOptions,
plan: isDryRun,
target: cliOptions.target ?? 'default',
progress: !cliOptions.json,
};
Expand Down
14 changes: 7 additions & 7 deletions src/cli/commands/remove/__tests__/remove-identity.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,7 @@ import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

describe('remove identity command', () => {
describe('remove credential command', () => {
let testDir: string;
let projectDir: string;
const identityName = 'TestIdentity';
Expand All@@ -24,7 +24,7 @@ describe('remove identity command', () => {

// Add identity as top-level credential
result = await runCLI(
['add', 'identity', '--name', identityName, '--api-key', 'test-key-123', '--json'],
['add', 'credential', '--name', identityName, '--api-key', 'test-key-123', '--json'],
projectDir
);
if (result.exitCode !== 0) {
Expand All@@ -38,15 +38,15 @@ describe('remove identity command', () => {

describe('validation', () => {
it('requires name flag', async () => {
const result = await runCLI(['remove', 'identity', '--json'], projectDir);
const result = await runCLI(['remove', 'credential', '--json'], projectDir);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
expect(json.error.includes('--name'), `Error: ${json.error}`).toBeTruthy();
});

it('rejects non-existent identity', async () => {
const result = await runCLI(['remove', 'identity', '--name', 'nonexistent', '--json'], projectDir);
const result = await runCLI(['remove', 'credential', '--name', 'nonexistent', '--json'], projectDir);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
Expand All@@ -58,9 +58,9 @@ describe('remove identity command', () => {
it('removes credential without dependents', async () => {
// Add a temp credential to remove
const tempId = `tempId${Date.now()}`;
await runCLI(['add', 'identity', '--name', tempId, '--api-key', 'temp-key', '--json'], projectDir);
await runCLI(['add', 'credential', '--name', tempId, '--api-key', 'temp-key', '--json'], projectDir);

const result = await runCLI(['remove', 'identity', '--name', tempId, '--json'], projectDir);
const result = await runCLI(['remove', 'credential', '--name', tempId, '--json'], projectDir);
expect(result.exitCode, `stdout: ${result.stdout}`).toBe(0);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(true);
Expand All@@ -72,7 +72,7 @@ describe('remove identity command', () => {
});

it('removes the setup credential', async () => {
const result = await runCLI(['remove', 'identity', '--name', identityName, '--json'], projectDir);
const result = await runCLI(['remove', 'credential', '--name', identityName, '--json'], projectDir);
expect(result.exitCode, `stdout: ${result.stdout}`).toBe(0);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(true);
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/remove/__tests__/validate.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,7 +21,7 @@ describe('validateRemoveOptions', () => {
});

it('returns valid with no json and no name', () => {
expect(validateRemoveOptions({ resourceType: 'identity' })).toEqual({ valid: true });
expect(validateRemoveOptions({ resourceType: 'credential' })).toEqual({ valid: true });
});
});

Expand Down
12 changes: 7 additions & 5 deletions src/cli/commands/remove/command.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -62,15 +62,17 @@ export const registerRemove = (program: Command): Command => {
removeCommand
.command('all')
.description('Reset all agentcore schemas to empty state')
.option('--force', 'Skip confirmation prompts [non-interactive]')
.option('-y, --yes', 'Skip confirmation prompts [non-interactive]')
.option('--force', 'Skip confirmation prompts (alias for --yes) [non-interactive]')
.option('--dry-run', 'Show what would be reset without actually resetting [non-interactive]')
.option('--json', 'Output as JSON [non-interactive]')
.action(async (cliOptions: { force?: boolean; dryRun?: boolean; json?: boolean }) => {
.action(async (cliOptions: { yes?: boolean; force?: boolean; dryRun?: boolean; json?: boolean }) => {
try {
const skipConfirm = cliOptions.yes ?? cliOptions.force;
// Any flag triggers non-interactive CLI mode
if (cliOptions.force || cliOptions.dryRun || cliOptions.json) {
if (skipConfirm || cliOptions.dryRun || cliOptions.json) {
await handleRemoveAllCLI({
force: cliOptions.force,
force: skipConfirm,
dryRun: cliOptions.dryRun,
json: cliOptions.json,
});
Expand All@@ -95,7 +97,7 @@ export const registerRemove = (program: Command): Command => {
}
});

// Resource subcommands (agent, memory, identity, gateway, mcp-tool) are registered
// Resource subcommands (agent, memory, credential, gateway, mcp-tool) are registered
// via primitive.registerCommands() in cli.ts

// Catch-all for TUI fallback when no subcommand is specified.
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/remove/types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,7 +3,7 @@ export type ResourceType =
| 'gateway'
| 'gateway-target'
| 'memory'
| 'identity'
| 'credential'
| 'evaluator'
| 'online-eval'
| 'policy-engine'
Expand Down
2 changes: 1 addition & 1 deletion src/cli/logging/remove-logger.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,7 @@ export interface RemoveLoggerOptions {
resourceType:
| 'agent'
| 'memory'
| 'identity'
| 'credential'
| 'gateway'
| 'gateway-target'
| 'evaluator'
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions src/cli/commands/add/__tests__/add-identity.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,7 @@ import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

describe('add identity command', () => {
describe('add credential command', () => {
let testDir: string;
let projectDir: string;

Expand All@@ -28,27 +28,27 @@ describe('add identity command', () => {

describe('validation', () => {
it('requires name flag', async () => {
const result = await runCLI(['add', 'identity', '--json'], projectDir);
const result = await runCLI(['add', 'credential', '--json'], projectDir);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
expect(json.error.includes('--name'), `Error: ${json.error}`).toBeTruthy();
});

it('requires api-key flag', async () => {
const result = await runCLI(['add', 'identity', '--name', 'test', '--json'], projectDir);
const result = await runCLI(['add', 'credential', '--name', 'test', '--json'], projectDir);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
expect(json.error.includes('--api-key'), `Error: ${json.error}`).toBeTruthy();
});
});

describe('identity creation', () => {
describe('credential creation', () => {
it('creates credential as top-level resource', async () => {
const identityName = `id${Date.now()}`;
const result = await runCLI(
['add', 'identity', '--name', identityName, '--api-key', 'test-key-123', '--json'],
['add', 'credential', '--name', identityName, '--api-key', 'test-key-123', '--json'],
projectDir
);

Expand All@@ -65,13 +65,13 @@ describe('add identity command', () => {
});
});

describe('oauth identity creation', () => {
describe('oauth credential creation', () => {
it('creates OAuth credential with discovery URL and scopes', async () => {
const identityName = `oauth-${Date.now()}`;
const result = await runCLI(
[
'add',
'identity',
'credential',
'--type',
'oauth',
'--name',
Expand Down
28 changes: 14 additions & 14 deletions src/cli/commands/add/__tests__/validate.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
import type {
AddAgentOptions,
AddCredentialOptions,
AddGatewayOptions,
AddGatewayTargetOptions,
AddIdentityOptions,
AddMemoryOptions,
} from '../types.js';
import {
validateAddAgentOptions,
validateAddCredentialOptions,
validateAddGatewayOptions,
validateAddGatewayTargetOptions,
validateAddIdentityOptions,
validateAddMemoryOptions,
} from '../validate.js';
import { existsSync, readFileSync } from 'fs';
Expand DownExpand Up@@ -75,7 +75,7 @@ const validMemoryOptions: AddMemoryOptions = {
strategies: 'SEMANTIC,SUMMARIZATION',
};

const validIdentityOptions: AddIdentityOptions = {
const validCredentialOptions: AddCredentialOptions = {
name: 'test-identity',
apiKey: 'test-key',
};
Expand DownExpand Up@@ -996,25 +996,25 @@ describe('validate', () => {
});
});

describe('validateAddIdentityOptions', () => {
describe('validateAddCredentialOptions', () => {
// AC23: Required fields validated
it('returns error for missing required fields', () => {
const requiredFields: { field: keyof AddIdentityOptions; error: string }[] = [
const requiredFields: { field: keyof AddCredentialOptions; error: string }[] = [
{ field: 'name', error: '--name is required' },
{ field: 'apiKey', error: '--api-key is required' },
];

for (const { field, error } of requiredFields) {
const opts = { ...validIdentityOptions, [field]: undefined };
const result = validateAddIdentityOptions(opts);
const opts = { ...validCredentialOptions, [field]: undefined };
const result = validateAddCredentialOptions(opts);
expect(result.valid, `Should fail for missing ${String(field)}`).toBe(false);
expect(result.error).toBe(error);
}
});

// AC25: Valid options pass
it('passes for valid options', () => {
expect(validateAddIdentityOptions(validIdentityOptions)).toEqual({ valid: true });
expect(validateAddCredentialOptions(validCredentialOptions)).toEqual({ valid: true });
});
});

Expand DownExpand Up@@ -1193,9 +1193,9 @@ describe('validate', () => {
});
});

describe('validateAddIdentityOptions OAuth', () => {
describe('validateAddCredentialOptions OAuth', () => {
it('passes for valid OAuth identity', () => {
const result = validateAddIdentityOptions({
const result = validateAddCredentialOptions({
name: 'my-oauth',
type: 'oauth',
discoveryUrl: 'https://auth.example.com/.well-known/openid-configuration',
Expand All@@ -1206,7 +1206,7 @@ describe('validate', () => {
});

it('returns error for OAuth without discovery-url', () => {
const result = validateAddIdentityOptions({
const result = validateAddCredentialOptions({
name: 'my-oauth',
type: 'oauth',
clientId: 'client123',
Expand All@@ -1217,7 +1217,7 @@ describe('validate', () => {
});

it('returns error for OAuth without client-id', () => {
const result = validateAddIdentityOptions({
const result = validateAddCredentialOptions({
name: 'my-oauth',
type: 'oauth',
discoveryUrl: 'https://auth.example.com',
Expand All@@ -1228,7 +1228,7 @@ describe('validate', () => {
});

it('returns error for OAuth without client-secret', () => {
const result = validateAddIdentityOptions({
const result = validateAddCredentialOptions({
name: 'my-oauth',
type: 'oauth',
discoveryUrl: 'https://auth.example.com',
Expand All@@ -1239,7 +1239,7 @@ describe('validate', () => {
});

it('still requires api-key for default type', () => {
const result = validateAddIdentityOptions({ name: 'my-key' });
const result = validateAddCredentialOptions({ name: 'my-key' });
expect(result.valid).toBe(false);
expect(result.error).toContain('--api-key');
});
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/add/command.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,7 +33,7 @@ export function registerAdd(program: Command): Command {
);
});

// Subcommands (agent, memory, identity, gateway, gateway-target) are registered
// Subcommands (agent, memory, credential, gateway, gateway-target) are registered
// via primitive.registerCommands() in cli.ts

return addCmd;
Expand Down
9 changes: 6 additions & 3 deletions src/cli/commands/add/types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -118,8 +118,8 @@ export interface AddMemoryResult {
error?: string;
}

// Identity types (v2: credential, no owner/user concept)
export interface AddIdentityOptions {
// Credential types (v2: credential, no owner/user concept)
export interface AddCredentialOptions {
name?: string;
type?: 'api-key' | 'oauth';
apiKey?: string;
Expand All@@ -130,7 +130,10 @@ export interface AddIdentityOptions {
json?: boolean;
}

export interface AddIdentityResult {
/** @deprecated Use AddCredentialOptions */
export type AddIdentityOptions = AddCredentialOptions;

export interface AddCredentialResult {
success: boolean;
credentialName?: string;
error?: string;
Expand Down
8 changes: 4 additions & 4 deletions src/cli/commands/add/validate.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,9 +19,9 @@ import { validateVpcOptions } from '../shared/vpc-utils';
import { validateJwtAuthorizerOptions } from './auth-options';
import type {
AddAgentOptions,
AddCredentialOptions,
AddGatewayOptions,
AddGatewayTargetOptions,
AddIdentityOptions,
AddMemoryOptions,
} from './types';
import { existsSync, readFileSync } from 'fs';
Expand DownExpand Up@@ -50,7 +50,7 @@ async function validateCredentialExists(credentialName: string): Promise<Validat
if (availableCredentials.length === 0) {
return {
valid: false,
error: `Credential "${credentialName}" not found. No credentials are configured. Add credentials using 'agentcore add identity'.`,
error: `Credential "${credentialName}" not found. No credentials are configured. Add credentials using 'agentcore add credential'.`,
};
}
return {
Expand DownExpand Up@@ -680,8 +680,8 @@ export function validateAddMemoryOptions(options: AddMemoryOptions): ValidationR
return { valid: true };
}

// Identity validation (v2: credential resource, no owner)
export function validateAddIdentityOptions(options: AddIdentityOptions): ValidationResult {
// Credential validation (v2: credential resource, no owner)
export function validateAddCredentialOptions(options: AddCredentialOptions): ValidationResult {
if (!options.name) {
return { valid: false, error: '--name is required' };
}
Expand Down
10 changes: 7 additions & 3 deletions src/cli/commands/deploy/command.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -90,7 +90,7 @@ async function handleDeployCLI(options: DeployOptions): Promise<void> {
if (options.diff) {
console.log(`\n✓ Diff complete for '${result.targetName}' (stack: ${result.stackName})`);
} else if (options.plan) {
console.log(`\n✓ Plan complete for '${result.targetName}' (stack: ${result.stackName})`);
console.log(`\n✓ Dry run complete for '${result.targetName}' (stack: ${result.stackName})`);
console.log('\nRun `agentcore deploy` to deploy.');
} else {
console.log(`\n✓ Deployed to '${result.targetName}' (stack: ${result.stackName})`);
Expand DownExpand Up@@ -136,23 +136,27 @@ export const registerDeploy = (program: Command) => {
.option('-y, --yes', 'Auto-confirm prompts, read credentials from env [non-interactive]')
.option('-v, --verbose', 'Show resource-level deployment events [non-interactive]')
.option('--json', 'Output as JSON [non-interactive]')
.option('--plan', 'Preview deployment without deploying (dry-run) [non-interactive]')
.option('--dry-run', 'Preview deployment without deploying [non-interactive]')
.option('--plan', 'Preview deployment without deploying (alias for --dry-run) [non-interactive]')
.option('--diff', 'Show CDK diff without deploying [non-interactive]')
.action(
async (cliOptions: {
target?: string;
yes?: boolean;
verbose?: boolean;
json?: boolean;
dryRun?: boolean;
plan?: boolean;
diff?: boolean;
}) => {
try {
requireProject();
if (cliOptions.json || cliOptions.target || cliOptions.plan || cliOptions.yes || cliOptions.verbose) {
const isDryRun = cliOptions.dryRun ?? cliOptions.plan;
if (cliOptions.json || cliOptions.target || isDryRun || cliOptions.yes || cliOptions.verbose) {
// CLI mode - any flag triggers non-interactive mode
const options = {
...cliOptions,
plan: isDryRun,
target: cliOptions.target ?? 'default',
progress: !cliOptions.json,
};
Expand Down
14 changes: 7 additions & 7 deletions src/cli/commands/remove/__tests__/remove-identity.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,7 @@ import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

describe('remove identity command', () => {
describe('remove credential command', () => {
let testDir: string;
let projectDir: string;
const identityName = 'TestIdentity';
Expand All@@ -24,7 +24,7 @@ describe('remove identity command', () => {

// Add identity as top-level credential
result = await runCLI(
['add', 'identity', '--name', identityName, '--api-key', 'test-key-123', '--json'],
['add', 'credential', '--name', identityName, '--api-key', 'test-key-123', '--json'],
projectDir
);
if (result.exitCode !== 0) {
Expand All@@ -38,15 +38,15 @@ describe('remove identity command', () => {

describe('validation', () => {
it('requires name flag', async () => {
const result = await runCLI(['remove', 'identity', '--json'], projectDir);
const result = await runCLI(['remove', 'credential', '--json'], projectDir);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
expect(json.error.includes('--name'), `Error: ${json.error}`).toBeTruthy();
});

it('rejects non-existent identity', async () => {
const result = await runCLI(['remove', 'identity', '--name', 'nonexistent', '--json'], projectDir);
const result = await runCLI(['remove', 'credential', '--name', 'nonexistent', '--json'], projectDir);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
Expand All@@ -58,9 +58,9 @@ describe('remove identity command', () => {
it('removes credential without dependents', async () => {
// Add a temp credential to remove
const tempId = `tempId${Date.now()}`;
await runCLI(['add', 'identity', '--name', tempId, '--api-key', 'temp-key', '--json'], projectDir);
await runCLI(['add', 'credential', '--name', tempId, '--api-key', 'temp-key', '--json'], projectDir);

const result = await runCLI(['remove', 'identity', '--name', tempId, '--json'], projectDir);
const result = await runCLI(['remove', 'credential', '--name', tempId, '--json'], projectDir);
expect(result.exitCode, `stdout: ${result.stdout}`).toBe(0);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(true);
Expand All@@ -72,7 +72,7 @@ describe('remove identity command', () => {
});

it('removes the setup credential', async () => {
const result = await runCLI(['remove', 'identity', '--name', identityName, '--json'], projectDir);
const result = await runCLI(['remove', 'credential', '--name', identityName, '--json'], projectDir);
expect(result.exitCode, `stdout: ${result.stdout}`).toBe(0);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(true);
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/remove/__tests__/validate.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,7 +21,7 @@ describe('validateRemoveOptions', () => {
});

it('returns valid with no json and no name', () => {
expect(validateRemoveOptions({ resourceType: 'identity' })).toEqual({ valid: true });
expect(validateRemoveOptions({ resourceType: 'credential' })).toEqual({ valid: true });
});
});

Expand Down
12 changes: 7 additions & 5 deletions src/cli/commands/remove/command.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -62,15 +62,17 @@ export const registerRemove = (program: Command): Command => {
removeCommand
.command('all')
.description('Reset all agentcore schemas to empty state')
.option('--force', 'Skip confirmation prompts [non-interactive]')
.option('-y, --yes', 'Skip confirmation prompts [non-interactive]')
.option('--force', 'Skip confirmation prompts (alias for --yes) [non-interactive]')
.option('--dry-run', 'Show what would be reset without actually resetting [non-interactive]')
.option('--json', 'Output as JSON [non-interactive]')
.action(async (cliOptions: { force?: boolean; dryRun?: boolean; json?: boolean }) => {
.action(async (cliOptions: { yes?: boolean; force?: boolean; dryRun?: boolean; json?: boolean }) => {
try {
const skipConfirm = cliOptions.yes ?? cliOptions.force;
// Any flag triggers non-interactive CLI mode
if (cliOptions.force || cliOptions.dryRun || cliOptions.json) {
if (skipConfirm || cliOptions.dryRun || cliOptions.json) {
await handleRemoveAllCLI({
force: cliOptions.force,
force: skipConfirm,
dryRun: cliOptions.dryRun,
json: cliOptions.json,
});
Expand All@@ -95,7 +97,7 @@ export const registerRemove = (program: Command): Command => {
}
});

// Resource subcommands (agent, memory, identity, gateway, mcp-tool) are registered
// Resource subcommands (agent, memory, credential, gateway, mcp-tool) are registered
// via primitive.registerCommands() in cli.ts

// Catch-all for TUI fallback when no subcommand is specified.
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/remove/types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,7 +3,7 @@ export type ResourceType =
| 'gateway'
| 'gateway-target'
| 'memory'
| 'identity'
| 'credential'
| 'evaluator'
| 'online-eval'
| 'policy-engine'
Expand Down
2 changes: 1 addition & 1 deletion src/cli/logging/remove-logger.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,7 @@ export interface RemoveLoggerOptions {
resourceType:
| 'agent'
| 'memory'
| 'identity'
| 'credential'
| 'gateway'
| 'gateway-target'
| 'evaluator'
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions src/cli/commands/add/__tests__/add-identity.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,7 @@ import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

describe('add identity command', () => {
describe('add credential command', () => {
let testDir: string;
let projectDir: string;

Expand All@@ -28,27 +28,27 @@ describe('add identity command', () => {

describe('validation', () => {
it('requires name flag', async () => {
const result = await runCLI(['add', 'identity', '--json'], projectDir);
const result = await runCLI(['add', 'credential', '--json'], projectDir);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
expect(json.error.includes('--name'), `Error: ${json.error}`).toBeTruthy();
});

it('requires api-key flag', async () => {
const result = await runCLI(['add', 'identity', '--name', 'test', '--json'], projectDir);
const result = await runCLI(['add', 'credential', '--name', 'test', '--json'], projectDir);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
expect(json.error.includes('--api-key'), `Error: ${json.error}`).toBeTruthy();
});
});

describe('identity creation', () => {
describe('credential creation', () => {
it('creates credential as top-level resource', async () => {
const identityName = `id${Date.now()}`;
const result = await runCLI(
['add', 'identity', '--name', identityName, '--api-key', 'test-key-123', '--json'],
['add', 'credential', '--name', identityName, '--api-key', 'test-key-123', '--json'],
projectDir
);

Expand All@@ -65,13 +65,13 @@ describe('add identity command', () => {
});
});

describe('oauth identity creation', () => {
describe('oauth credential creation', () => {
it('creates OAuth credential with discovery URL and scopes', async () => {
const identityName = `oauth-${Date.now()}`;
const result = await runCLI(
[
'add',
'identity',
'credential',
'--type',
'oauth',
'--name',
Expand Down
28 changes: 14 additions & 14 deletions src/cli/commands/add/__tests__/validate.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
import type {
AddAgentOptions,
AddCredentialOptions,
AddGatewayOptions,
AddGatewayTargetOptions,
AddIdentityOptions,
AddMemoryOptions,
} from '../types.js';
import {
validateAddAgentOptions,
validateAddCredentialOptions,
validateAddGatewayOptions,
validateAddGatewayTargetOptions,
validateAddIdentityOptions,
validateAddMemoryOptions,
} from '../validate.js';
import { existsSync, readFileSync } from 'fs';
Expand DownExpand Up@@ -75,7 +75,7 @@ const validMemoryOptions: AddMemoryOptions = {
strategies: 'SEMANTIC,SUMMARIZATION',
};

const validIdentityOptions: AddIdentityOptions = {
const validCredentialOptions: AddCredentialOptions = {
name: 'test-identity',
apiKey: 'test-key',
};
Expand DownExpand Up@@ -996,25 +996,25 @@ describe('validate', () => {
});
});

describe('validateAddIdentityOptions', () => {
describe('validateAddCredentialOptions', () => {
// AC23: Required fields validated
it('returns error for missing required fields', () => {
const requiredFields: { field: keyof AddIdentityOptions; error: string }[] = [
const requiredFields: { field: keyof AddCredentialOptions; error: string }[] = [
{ field: 'name', error: '--name is required' },
{ field: 'apiKey', error: '--api-key is required' },
];

for (const { field, error } of requiredFields) {
const opts = { ...validIdentityOptions, [field]: undefined };
const result = validateAddIdentityOptions(opts);
const opts = { ...validCredentialOptions, [field]: undefined };
const result = validateAddCredentialOptions(opts);
expect(result.valid, `Should fail for missing ${String(field)}`).toBe(false);
expect(result.error).toBe(error);
}
});

// AC25: Valid options pass
it('passes for valid options', () => {
expect(validateAddIdentityOptions(validIdentityOptions)).toEqual({ valid: true });
expect(validateAddCredentialOptions(validCredentialOptions)).toEqual({ valid: true });
});
});

Expand DownExpand Up@@ -1193,9 +1193,9 @@ describe('validate', () => {
});
});

describe('validateAddIdentityOptions OAuth', () => {
describe('validateAddCredentialOptions OAuth', () => {
it('passes for valid OAuth identity', () => {
const result = validateAddIdentityOptions({
const result = validateAddCredentialOptions({
name: 'my-oauth',
type: 'oauth',
discoveryUrl: 'https://auth.example.com/.well-known/openid-configuration',
Expand All@@ -1206,7 +1206,7 @@ describe('validate', () => {
});

it('returns error for OAuth without discovery-url', () => {
const result = validateAddIdentityOptions({
const result = validateAddCredentialOptions({
name: 'my-oauth',
type: 'oauth',
clientId: 'client123',
Expand All@@ -1217,7 +1217,7 @@ describe('validate', () => {
});

it('returns error for OAuth without client-id', () => {
const result = validateAddIdentityOptions({
const result = validateAddCredentialOptions({
name: 'my-oauth',
type: 'oauth',
discoveryUrl: 'https://auth.example.com',
Expand All@@ -1228,7 +1228,7 @@ describe('validate', () => {
});

it('returns error for OAuth without client-secret', () => {
const result = validateAddIdentityOptions({
const result = validateAddCredentialOptions({
name: 'my-oauth',
type: 'oauth',
discoveryUrl: 'https://auth.example.com',
Expand All@@ -1239,7 +1239,7 @@ describe('validate', () => {
});

it('still requires api-key for default type', () => {
const result = validateAddIdentityOptions({ name: 'my-key' });
const result = validateAddCredentialOptions({ name: 'my-key' });
expect(result.valid).toBe(false);
expect(result.error).toContain('--api-key');
});
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/add/command.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,7 +33,7 @@ export function registerAdd(program: Command): Command {
);
});

// Subcommands (agent, memory, identity, gateway, gateway-target) are registered
// Subcommands (agent, memory, credential, gateway, gateway-target) are registered
// via primitive.registerCommands() in cli.ts

return addCmd;
Expand Down
9 changes: 6 additions & 3 deletions src/cli/commands/add/types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -118,8 +118,8 @@ export interface AddMemoryResult {
error?: string;
}

// Identity types (v2: credential, no owner/user concept)
export interface AddIdentityOptions {
// Credential types (v2: credential, no owner/user concept)
export interface AddCredentialOptions {
name?: string;
type?: 'api-key' | 'oauth';
apiKey?: string;
Expand All@@ -130,7 +130,10 @@ export interface AddIdentityOptions {
json?: boolean;
}

export interface AddIdentityResult {
/** @deprecated Use AddCredentialOptions */
export type AddIdentityOptions = AddCredentialOptions;

export interface AddCredentialResult {
success: boolean;
credentialName?: string;
error?: string;
Expand Down
8 changes: 4 additions & 4 deletions src/cli/commands/add/validate.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,9 +19,9 @@ import { validateVpcOptions } from '../shared/vpc-utils';
import { validateJwtAuthorizerOptions } from './auth-options';
import type {
AddAgentOptions,
AddCredentialOptions,
AddGatewayOptions,
AddGatewayTargetOptions,
AddIdentityOptions,
AddMemoryOptions,
} from './types';
import { existsSync, readFileSync } from 'fs';
Expand DownExpand Up@@ -50,7 +50,7 @@ async function validateCredentialExists(credentialName: string): Promise<Validat
if (availableCredentials.length === 0) {
return {
valid: false,
error: `Credential "${credentialName}" not found. No credentials are configured. Add credentials using 'agentcore add identity'.`,
error: `Credential "${credentialName}" not found. No credentials are configured. Add credentials using 'agentcore add credential'.`,
};
}
return {
Expand DownExpand Up@@ -680,8 +680,8 @@ export function validateAddMemoryOptions(options: AddMemoryOptions): ValidationR
return { valid: true };
}

// Identity validation (v2: credential resource, no owner)
export function validateAddIdentityOptions(options: AddIdentityOptions): ValidationResult {
// Credential validation (v2: credential resource, no owner)
export function validateAddCredentialOptions(options: AddCredentialOptions): ValidationResult {
if (!options.name) {
return { valid: false, error: '--name is required' };
}
Expand Down
10 changes: 7 additions & 3 deletions src/cli/commands/deploy/command.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -90,7 +90,7 @@ async function handleDeployCLI(options: DeployOptions): Promise<void> {
if (options.diff) {
console.log(`\n✓ Diff complete for '${result.targetName}' (stack: ${result.stackName})`);
} else if (options.plan) {
console.log(`\n✓ Plan complete for '${result.targetName}' (stack: ${result.stackName})`);
console.log(`\n✓ Dry run complete for '${result.targetName}' (stack: ${result.stackName})`);
console.log('\nRun `agentcore deploy` to deploy.');
} else {
console.log(`\n✓ Deployed to '${result.targetName}' (stack: ${result.stackName})`);
Expand DownExpand Up@@ -136,23 +136,27 @@ export const registerDeploy = (program: Command) => {
.option('-y, --yes', 'Auto-confirm prompts, read credentials from env [non-interactive]')
.option('-v, --verbose', 'Show resource-level deployment events [non-interactive]')
.option('--json', 'Output as JSON [non-interactive]')
.option('--plan', 'Preview deployment without deploying (dry-run) [non-interactive]')
.option('--dry-run', 'Preview deployment without deploying [non-interactive]')
.option('--plan', 'Preview deployment without deploying (alias for --dry-run) [non-interactive]')
.option('--diff', 'Show CDK diff without deploying [non-interactive]')
.action(
async (cliOptions: {
target?: string;
yes?: boolean;
verbose?: boolean;
json?: boolean;
dryRun?: boolean;
plan?: boolean;
diff?: boolean;
}) => {
try {
requireProject();
if (cliOptions.json || cliOptions.target || cliOptions.plan || cliOptions.yes || cliOptions.verbose) {
const isDryRun = cliOptions.dryRun ?? cliOptions.plan;
if (cliOptions.json || cliOptions.target || isDryRun || cliOptions.yes || cliOptions.verbose) {
// CLI mode - any flag triggers non-interactive mode
const options = {
...cliOptions,
plan: isDryRun,
target: cliOptions.target ?? 'default',
progress: !cliOptions.json,
};
Expand Down
14 changes: 7 additions & 7 deletions src/cli/commands/remove/__tests__/remove-identity.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,7 @@ import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

describe('remove identity command', () => {
describe('remove credential command', () => {
let testDir: string;
let projectDir: string;
const identityName = 'TestIdentity';
Expand All@@ -24,7 +24,7 @@ describe('remove identity command', () => {

// Add identity as top-level credential
result = await runCLI(
['add', 'identity', '--name', identityName, '--api-key', 'test-key-123', '--json'],
['add', 'credential', '--name', identityName, '--api-key', 'test-key-123', '--json'],
projectDir
);
if (result.exitCode !== 0) {
Expand All@@ -38,15 +38,15 @@ describe('remove identity command', () => {

describe('validation', () => {
it('requires name flag', async () => {
const result = await runCLI(['remove', 'identity', '--json'], projectDir);
const result = await runCLI(['remove', 'credential', '--json'], projectDir);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
expect(json.error.includes('--name'), `Error: ${json.error}`).toBeTruthy();
});

it('rejects non-existent identity', async () => {
const result = await runCLI(['remove', 'identity', '--name', 'nonexistent', '--json'], projectDir);
const result = await runCLI(['remove', 'credential', '--name', 'nonexistent', '--json'], projectDir);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
Expand All@@ -58,9 +58,9 @@ describe('remove identity command', () => {
it('removes credential without dependents', async () => {
// Add a temp credential to remove
const tempId = `tempId${Date.now()}`;
await runCLI(['add', 'identity', '--name', tempId, '--api-key', 'temp-key', '--json'], projectDir);
await runCLI(['add', 'credential', '--name', tempId, '--api-key', 'temp-key', '--json'], projectDir);

const result = await runCLI(['remove', 'identity', '--name', tempId, '--json'], projectDir);
const result = await runCLI(['remove', 'credential', '--name', tempId, '--json'], projectDir);
expect(result.exitCode, `stdout: ${result.stdout}`).toBe(0);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(true);
Expand All@@ -72,7 +72,7 @@ describe('remove identity command', () => {
});

it('removes the setup credential', async () => {
const result = await runCLI(['remove', 'identity', '--name', identityName, '--json'], projectDir);
const result = await runCLI(['remove', 'credential', '--name', identityName, '--json'], projectDir);
expect(result.exitCode, `stdout: ${result.stdout}`).toBe(0);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(true);
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/remove/__tests__/validate.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,7 +21,7 @@ describe('validateRemoveOptions', () => {
});

it('returns valid with no json and no name', () => {
expect(validateRemoveOptions({ resourceType: 'identity' })).toEqual({ valid: true });
expect(validateRemoveOptions({ resourceType: 'credential' })).toEqual({ valid: true });
});
});

Expand Down
12 changes: 7 additions & 5 deletions src/cli/commands/remove/command.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -62,15 +62,17 @@ export const registerRemove = (program: Command): Command => {
removeCommand
.command('all')
.description('Reset all agentcore schemas to empty state')
.option('--force', 'Skip confirmation prompts [non-interactive]')
.option('-y, --yes', 'Skip confirmation prompts [non-interactive]')
.option('--force', 'Skip confirmation prompts (alias for --yes) [non-interactive]')
.option('--dry-run', 'Show what would be reset without actually resetting [non-interactive]')
.option('--json', 'Output as JSON [non-interactive]')
.action(async (cliOptions: { force?: boolean; dryRun?: boolean; json?: boolean }) => {
.action(async (cliOptions: { yes?: boolean; force?: boolean; dryRun?: boolean; json?: boolean }) => {
try {
const skipConfirm = cliOptions.yes ?? cliOptions.force;
// Any flag triggers non-interactive CLI mode
if (cliOptions.force || cliOptions.dryRun || cliOptions.json) {
if (skipConfirm || cliOptions.dryRun || cliOptions.json) {
await handleRemoveAllCLI({
force: cliOptions.force,
force: skipConfirm,
dryRun: cliOptions.dryRun,
json: cliOptions.json,
});
Expand All@@ -95,7 +97,7 @@ export const registerRemove = (program: Command): Command => {
}
});

// Resource subcommands (agent, memory, identity, gateway, mcp-tool) are registered
// Resource subcommands (agent, memory, credential, gateway, mcp-tool) are registered
// via primitive.registerCommands() in cli.ts

// Catch-all for TUI fallback when no subcommand is specified.
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/remove/types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,7 +3,7 @@ export type ResourceType =
| 'gateway'
| 'gateway-target'
| 'memory'
| 'identity'
| 'credential'
| 'evaluator'
| 'online-eval'
| 'policy-engine'
Expand Down
2 changes: 1 addition & 1 deletion src/cli/logging/remove-logger.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,7 @@ export interface RemoveLoggerOptions {
resourceType:
| 'agent'
| 'memory'
| 'identity'
| 'credential'
| 'gateway'
| 'gateway-target'
| 'evaluator'
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions src/cli/commands/add/__tests__/add-identity.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,7 @@ import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

describe('add identity command', () => {
describe('add credential command', () => {
let testDir: string;
let projectDir: string;

Expand All@@ -28,27 +28,27 @@ describe('add identity command', () => {

describe('validation', () => {
it('requires name flag', async () => {
const result = await runCLI(['add', 'identity', '--json'], projectDir);
const result = await runCLI(['add', 'credential', '--json'], projectDir);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
expect(json.error.includes('--name'), `Error: ${json.error}`).toBeTruthy();
});

it('requires api-key flag', async () => {
const result = await runCLI(['add', 'identity', '--name', 'test', '--json'], projectDir);
const result = await runCLI(['add', 'credential', '--name', 'test', '--json'], projectDir);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
expect(json.error.includes('--api-key'), `Error: ${json.error}`).toBeTruthy();
});
});

describe('identity creation', () => {
describe('credential creation', () => {
it('creates credential as top-level resource', async () => {
const identityName = `id${Date.now()}`;
const result = await runCLI(
['add', 'identity', '--name', identityName, '--api-key', 'test-key-123', '--json'],
['add', 'credential', '--name', identityName, '--api-key', 'test-key-123', '--json'],
projectDir
);

Expand All@@ -65,13 +65,13 @@ describe('add identity command', () => {
});
});

describe('oauth identity creation', () => {
describe('oauth credential creation', () => {
it('creates OAuth credential with discovery URL and scopes', async () => {
const identityName = `oauth-${Date.now()}`;
const result = await runCLI(
[
'add',
'identity',
'credential',
'--type',
'oauth',
'--name',
Expand Down
28 changes: 14 additions & 14 deletions src/cli/commands/add/__tests__/validate.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
import type {
AddAgentOptions,
AddCredentialOptions,
AddGatewayOptions,
AddGatewayTargetOptions,
AddIdentityOptions,
AddMemoryOptions,
} from '../types.js';
import {
validateAddAgentOptions,
validateAddCredentialOptions,
validateAddGatewayOptions,
validateAddGatewayTargetOptions,
validateAddIdentityOptions,
validateAddMemoryOptions,
} from '../validate.js';
import { existsSync, readFileSync } from 'fs';
Expand DownExpand Up@@ -75,7 +75,7 @@ const validMemoryOptions: AddMemoryOptions = {
strategies: 'SEMANTIC,SUMMARIZATION',
};

const validIdentityOptions: AddIdentityOptions = {
const validCredentialOptions: AddCredentialOptions = {
name: 'test-identity',
apiKey: 'test-key',
};
Expand DownExpand Up@@ -996,25 +996,25 @@ describe('validate', () => {
});
});

describe('validateAddIdentityOptions', () => {
describe('validateAddCredentialOptions', () => {
// AC23: Required fields validated
it('returns error for missing required fields', () => {
const requiredFields: { field: keyof AddIdentityOptions; error: string }[] = [
const requiredFields: { field: keyof AddCredentialOptions; error: string }[] = [
{ field: 'name', error: '--name is required' },
{ field: 'apiKey', error: '--api-key is required' },
];

for (const { field, error } of requiredFields) {
const opts = { ...validIdentityOptions, [field]: undefined };
const result = validateAddIdentityOptions(opts);
const opts = { ...validCredentialOptions, [field]: undefined };
const result = validateAddCredentialOptions(opts);
expect(result.valid, `Should fail for missing ${String(field)}`).toBe(false);
expect(result.error).toBe(error);
}
});

// AC25: Valid options pass
it('passes for valid options', () => {
expect(validateAddIdentityOptions(validIdentityOptions)).toEqual({ valid: true });
expect(validateAddCredentialOptions(validCredentialOptions)).toEqual({ valid: true });
});
});

Expand DownExpand Up@@ -1193,9 +1193,9 @@ describe('validate', () => {
});
});

describe('validateAddIdentityOptions OAuth', () => {
describe('validateAddCredentialOptions OAuth', () => {
it('passes for valid OAuth identity', () => {
const result = validateAddIdentityOptions({
const result = validateAddCredentialOptions({
name: 'my-oauth',
type: 'oauth',
discoveryUrl: 'https://auth.example.com/.well-known/openid-configuration',
Expand All@@ -1206,7 +1206,7 @@ describe('validate', () => {
});

it('returns error for OAuth without discovery-url', () => {
const result = validateAddIdentityOptions({
const result = validateAddCredentialOptions({
name: 'my-oauth',
type: 'oauth',
clientId: 'client123',
Expand All@@ -1217,7 +1217,7 @@ describe('validate', () => {
});

it('returns error for OAuth without client-id', () => {
const result = validateAddIdentityOptions({
const result = validateAddCredentialOptions({
name: 'my-oauth',
type: 'oauth',
discoveryUrl: 'https://auth.example.com',
Expand All@@ -1228,7 +1228,7 @@ describe('validate', () => {
});

it('returns error for OAuth without client-secret', () => {
const result = validateAddIdentityOptions({
const result = validateAddCredentialOptions({
name: 'my-oauth',
type: 'oauth',
discoveryUrl: 'https://auth.example.com',
Expand All@@ -1239,7 +1239,7 @@ describe('validate', () => {
});

it('still requires api-key for default type', () => {
const result = validateAddIdentityOptions({ name: 'my-key' });
const result = validateAddCredentialOptions({ name: 'my-key' });
expect(result.valid).toBe(false);
expect(result.error).toContain('--api-key');
});
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/add/command.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,7 +33,7 @@ export function registerAdd(program: Command): Command {
);
});

// Subcommands (agent, memory, identity, gateway, gateway-target) are registered
// Subcommands (agent, memory, credential, gateway, gateway-target) are registered
// via primitive.registerCommands() in cli.ts

return addCmd;
Expand Down
9 changes: 6 additions & 3 deletions src/cli/commands/add/types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -118,8 +118,8 @@ export interface AddMemoryResult {
error?: string;
}

// Identity types (v2: credential, no owner/user concept)
export interface AddIdentityOptions {
// Credential types (v2: credential, no owner/user concept)
export interface AddCredentialOptions {
name?: string;
type?: 'api-key' | 'oauth';
apiKey?: string;
Expand All@@ -130,7 +130,10 @@ export interface AddIdentityOptions {
json?: boolean;
}

export interface AddIdentityResult {
/** @deprecated Use AddCredentialOptions */
export type AddIdentityOptions = AddCredentialOptions;

export interface AddCredentialResult {
success: boolean;
credentialName?: string;
error?: string;
Expand Down
8 changes: 4 additions & 4 deletions src/cli/commands/add/validate.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,9 +19,9 @@ import { validateVpcOptions } from '../shared/vpc-utils';
import { validateJwtAuthorizerOptions } from './auth-options';
import type {
AddAgentOptions,
AddCredentialOptions,
AddGatewayOptions,
AddGatewayTargetOptions,
AddIdentityOptions,
AddMemoryOptions,
} from './types';
import { existsSync, readFileSync } from 'fs';
Expand DownExpand Up@@ -50,7 +50,7 @@ async function validateCredentialExists(credentialName: string): Promise<Validat
if (availableCredentials.length === 0) {
return {
valid: false,
error: `Credential "${credentialName}" not found. No credentials are configured. Add credentials using 'agentcore add identity'.`,
error: `Credential "${credentialName}" not found. No credentials are configured. Add credentials using 'agentcore add credential'.`,
};
}
return {
Expand DownExpand Up@@ -680,8 +680,8 @@ export function validateAddMemoryOptions(options: AddMemoryOptions): ValidationR
return { valid: true };
}

// Identity validation (v2: credential resource, no owner)
export function validateAddIdentityOptions(options: AddIdentityOptions): ValidationResult {
// Credential validation (v2: credential resource, no owner)
export function validateAddCredentialOptions(options: AddCredentialOptions): ValidationResult {
if (!options.name) {
return { valid: false, error: '--name is required' };
}
Expand Down
10 changes: 7 additions & 3 deletions src/cli/commands/deploy/command.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -90,7 +90,7 @@ async function handleDeployCLI(options: DeployOptions): Promise<void> {
if (options.diff) {
console.log(`\n✓ Diff complete for '${result.targetName}' (stack: ${result.stackName})`);
} else if (options.plan) {
console.log(`\n✓ Plan complete for '${result.targetName}' (stack: ${result.stackName})`);
console.log(`\n✓ Dry run complete for '${result.targetName}' (stack: ${result.stackName})`);
console.log('\nRun `agentcore deploy` to deploy.');
} else {
console.log(`\n✓ Deployed to '${result.targetName}' (stack: ${result.stackName})`);
Expand DownExpand Up@@ -136,23 +136,27 @@ export const registerDeploy = (program: Command) => {
.option('-y, --yes', 'Auto-confirm prompts, read credentials from env [non-interactive]')
.option('-v, --verbose', 'Show resource-level deployment events [non-interactive]')
.option('--json', 'Output as JSON [non-interactive]')
.option('--plan', 'Preview deployment without deploying (dry-run) [non-interactive]')
.option('--dry-run', 'Preview deployment without deploying [non-interactive]')
.option('--plan', 'Preview deployment without deploying (alias for --dry-run) [non-interactive]')
.option('--diff', 'Show CDK diff without deploying [non-interactive]')
.action(
async (cliOptions: {
target?: string;
yes?: boolean;
verbose?: boolean;
json?: boolean;
dryRun?: boolean;
plan?: boolean;
diff?: boolean;
}) => {
try {
requireProject();
if (cliOptions.json || cliOptions.target || cliOptions.plan || cliOptions.yes || cliOptions.verbose) {
const isDryRun = cliOptions.dryRun ?? cliOptions.plan;
if (cliOptions.json || cliOptions.target || isDryRun || cliOptions.yes || cliOptions.verbose) {
// CLI mode - any flag triggers non-interactive mode
const options = {
...cliOptions,
plan: isDryRun,
target: cliOptions.target ?? 'default',
progress: !cliOptions.json,
};
Expand Down
14 changes: 7 additions & 7 deletions src/cli/commands/remove/__tests__/remove-identity.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,7 @@ import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

describe('remove identity command', () => {
describe('remove credential command', () => {
let testDir: string;
let projectDir: string;
const identityName = 'TestIdentity';
Expand All@@ -24,7 +24,7 @@ describe('remove identity command', () => {

// Add identity as top-level credential
result = await runCLI(
['add', 'identity', '--name', identityName, '--api-key', 'test-key-123', '--json'],
['add', 'credential', '--name', identityName, '--api-key', 'test-key-123', '--json'],
projectDir
);
if (result.exitCode !== 0) {
Expand All@@ -38,15 +38,15 @@ describe('remove identity command', () => {

describe('validation', () => {
it('requires name flag', async () => {
const result = await runCLI(['remove', 'identity', '--json'], projectDir);
const result = await runCLI(['remove', 'credential', '--json'], projectDir);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
expect(json.error.includes('--name'), `Error: ${json.error}`).toBeTruthy();
});

it('rejects non-existent identity', async () => {
const result = await runCLI(['remove', 'identity', '--name', 'nonexistent', '--json'], projectDir);
const result = await runCLI(['remove', 'credential', '--name', 'nonexistent', '--json'], projectDir);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
Expand All@@ -58,9 +58,9 @@ describe('remove identity command', () => {
it('removes credential without dependents', async () => {
// Add a temp credential to remove
const tempId = `tempId${Date.now()}`;
await runCLI(['add', 'identity', '--name', tempId, '--api-key', 'temp-key', '--json'], projectDir);
await runCLI(['add', 'credential', '--name', tempId, '--api-key', 'temp-key', '--json'], projectDir);

const result = await runCLI(['remove', 'identity', '--name', tempId, '--json'], projectDir);
const result = await runCLI(['remove', 'credential', '--name', tempId, '--json'], projectDir);
expect(result.exitCode, `stdout: ${result.stdout}`).toBe(0);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(true);
Expand All@@ -72,7 +72,7 @@ describe('remove identity command', () => {
});

it('removes the setup credential', async () => {
const result = await runCLI(['remove', 'identity', '--name', identityName, '--json'], projectDir);
const result = await runCLI(['remove', 'credential', '--name', identityName, '--json'], projectDir);
expect(result.exitCode, `stdout: ${result.stdout}`).toBe(0);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(true);
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/remove/__tests__/validate.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,7 +21,7 @@ describe('validateRemoveOptions', () => {
});

it('returns valid with no json and no name', () => {
expect(validateRemoveOptions({ resourceType: 'identity' })).toEqual({ valid: true });
expect(validateRemoveOptions({ resourceType: 'credential' })).toEqual({ valid: true });
});
});

Expand Down
12 changes: 7 additions & 5 deletions src/cli/commands/remove/command.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -62,15 +62,17 @@ export const registerRemove = (program: Command): Command => {
removeCommand
.command('all')
.description('Reset all agentcore schemas to empty state')
.option('--force', 'Skip confirmation prompts [non-interactive]')
.option('-y, --yes', 'Skip confirmation prompts [non-interactive]')
.option('--force', 'Skip confirmation prompts (alias for --yes) [non-interactive]')
.option('--dry-run', 'Show what would be reset without actually resetting [non-interactive]')
.option('--json', 'Output as JSON [non-interactive]')
.action(async (cliOptions: { force?: boolean; dryRun?: boolean; json?: boolean }) => {
.action(async (cliOptions: { yes?: boolean; force?: boolean; dryRun?: boolean; json?: boolean }) => {
try {
const skipConfirm = cliOptions.yes ?? cliOptions.force;
// Any flag triggers non-interactive CLI mode
if (cliOptions.force || cliOptions.dryRun || cliOptions.json) {
if (skipConfirm || cliOptions.dryRun || cliOptions.json) {
await handleRemoveAllCLI({
force: cliOptions.force,
force: skipConfirm,
dryRun: cliOptions.dryRun,
json: cliOptions.json,
});
Expand All@@ -95,7 +97,7 @@ export const registerRemove = (program: Command): Command => {
}
});

// Resource subcommands (agent, memory, identity, gateway, mcp-tool) are registered
// Resource subcommands (agent, memory, credential, gateway, mcp-tool) are registered
// via primitive.registerCommands() in cli.ts

// Catch-all for TUI fallback when no subcommand is specified.
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/remove/types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,7 +3,7 @@ export type ResourceType =
| 'gateway'
| 'gateway-target'
| 'memory'
| 'identity'
| 'credential'
| 'evaluator'
| 'online-eval'
| 'policy-engine'
Expand Down
2 changes: 1 addition & 1 deletion src/cli/logging/remove-logger.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,7 @@ export interface RemoveLoggerOptions {
resourceType:
| 'agent'
| 'memory'
| 'identity'
| 'credential'
| 'gateway'
| 'gateway-target'
| 'evaluator'
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions src/cli/commands/add/__tests__/add-identity.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,7 @@ import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

describe('add identity command', () => {
describe('add credential command', () => {
let testDir: string;
let projectDir: string;

Expand All@@ -28,27 +28,27 @@ describe('add identity command', () => {

describe('validation', () => {
it('requires name flag', async () => {
const result = await runCLI(['add', 'identity', '--json'], projectDir);
const result = await runCLI(['add', 'credential', '--json'], projectDir);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
expect(json.error.includes('--name'), `Error: ${json.error}`).toBeTruthy();
});

it('requires api-key flag', async () => {
const result = await runCLI(['add', 'identity', '--name', 'test', '--json'], projectDir);
const result = await runCLI(['add', 'credential', '--name', 'test', '--json'], projectDir);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
expect(json.error.includes('--api-key'), `Error: ${json.error}`).toBeTruthy();
});
});

describe('identity creation', () => {
describe('credential creation', () => {
it('creates credential as top-level resource', async () => {
const identityName = `id${Date.now()}`;
const result = await runCLI(
['add', 'identity', '--name', identityName, '--api-key', 'test-key-123', '--json'],
['add', 'credential', '--name', identityName, '--api-key', 'test-key-123', '--json'],
projectDir
);

Expand All@@ -65,13 +65,13 @@ describe('add identity command', () => {
});
});

describe('oauth identity creation', () => {
describe('oauth credential creation', () => {
it('creates OAuth credential with discovery URL and scopes', async () => {
const identityName = `oauth-${Date.now()}`;
const result = await runCLI(
[
'add',
'identity',
'credential',
'--type',
'oauth',
'--name',
Expand Down
28 changes: 14 additions & 14 deletions src/cli/commands/add/__tests__/validate.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
import type {
AddAgentOptions,
AddCredentialOptions,
AddGatewayOptions,
AddGatewayTargetOptions,
AddIdentityOptions,
AddMemoryOptions,
} from '../types.js';
import {
validateAddAgentOptions,
validateAddCredentialOptions,
validateAddGatewayOptions,
validateAddGatewayTargetOptions,
validateAddIdentityOptions,
validateAddMemoryOptions,
} from '../validate.js';
import { existsSync, readFileSync } from 'fs';
Expand DownExpand Up@@ -75,7 +75,7 @@ const validMemoryOptions: AddMemoryOptions = {
strategies: 'SEMANTIC,SUMMARIZATION',
};

const validIdentityOptions: AddIdentityOptions = {
const validCredentialOptions: AddCredentialOptions = {
name: 'test-identity',
apiKey: 'test-key',
};
Expand DownExpand Up@@ -996,25 +996,25 @@ describe('validate', () => {
});
});

describe('validateAddIdentityOptions', () => {
describe('validateAddCredentialOptions', () => {
// AC23: Required fields validated
it('returns error for missing required fields', () => {
const requiredFields: { field: keyof AddIdentityOptions; error: string }[] = [
const requiredFields: { field: keyof AddCredentialOptions; error: string }[] = [
{ field: 'name', error: '--name is required' },
{ field: 'apiKey', error: '--api-key is required' },
];

for (const { field, error } of requiredFields) {
const opts = { ...validIdentityOptions, [field]: undefined };
const result = validateAddIdentityOptions(opts);
const opts = { ...validCredentialOptions, [field]: undefined };
const result = validateAddCredentialOptions(opts);
expect(result.valid, `Should fail for missing ${String(field)}`).toBe(false);
expect(result.error).toBe(error);
}
});

// AC25: Valid options pass
it('passes for valid options', () => {
expect(validateAddIdentityOptions(validIdentityOptions)).toEqual({ valid: true });
expect(validateAddCredentialOptions(validCredentialOptions)).toEqual({ valid: true });
});
});

Expand DownExpand Up@@ -1193,9 +1193,9 @@ describe('validate', () => {
});
});

describe('validateAddIdentityOptions OAuth', () => {
describe('validateAddCredentialOptions OAuth', () => {
it('passes for valid OAuth identity', () => {
const result = validateAddIdentityOptions({
const result = validateAddCredentialOptions({
name: 'my-oauth',
type: 'oauth',
discoveryUrl: 'https://auth.example.com/.well-known/openid-configuration',
Expand All@@ -1206,7 +1206,7 @@ describe('validate', () => {
});

it('returns error for OAuth without discovery-url', () => {
const result = validateAddIdentityOptions({
const result = validateAddCredentialOptions({
name: 'my-oauth',
type: 'oauth',
clientId: 'client123',
Expand All@@ -1217,7 +1217,7 @@ describe('validate', () => {
});

it('returns error for OAuth without client-id', () => {
const result = validateAddIdentityOptions({
const result = validateAddCredentialOptions({
name: 'my-oauth',
type: 'oauth',
discoveryUrl: 'https://auth.example.com',
Expand All@@ -1228,7 +1228,7 @@ describe('validate', () => {
});

it('returns error for OAuth without client-secret', () => {
const result = validateAddIdentityOptions({
const result = validateAddCredentialOptions({
name: 'my-oauth',
type: 'oauth',
discoveryUrl: 'https://auth.example.com',
Expand All@@ -1239,7 +1239,7 @@ describe('validate', () => {
});

it('still requires api-key for default type', () => {
const result = validateAddIdentityOptions({ name: 'my-key' });
const result = validateAddCredentialOptions({ name: 'my-key' });
expect(result.valid).toBe(false);
expect(result.error).toContain('--api-key');
});
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/add/command.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,7 +33,7 @@ export function registerAdd(program: Command): Command {
);
});

// Subcommands (agent, memory, identity, gateway, gateway-target) are registered
// Subcommands (agent, memory, credential, gateway, gateway-target) are registered
// via primitive.registerCommands() in cli.ts

return addCmd;
Expand Down
9 changes: 6 additions & 3 deletions src/cli/commands/add/types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -118,8 +118,8 @@ export interface AddMemoryResult {
error?: string;
}

// Identity types (v2: credential, no owner/user concept)
export interface AddIdentityOptions {
// Credential types (v2: credential, no owner/user concept)
export interface AddCredentialOptions {
name?: string;
type?: 'api-key' | 'oauth';
apiKey?: string;
Expand All@@ -130,7 +130,10 @@ export interface AddIdentityOptions {
json?: boolean;
}

export interface AddIdentityResult {
/** @deprecated Use AddCredentialOptions */
export type AddIdentityOptions = AddCredentialOptions;

export interface AddCredentialResult {
success: boolean;
credentialName?: string;
error?: string;
Expand Down
8 changes: 4 additions & 4 deletions src/cli/commands/add/validate.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,9 +19,9 @@ import { validateVpcOptions } from '../shared/vpc-utils';
import { validateJwtAuthorizerOptions } from './auth-options';
import type {
AddAgentOptions,
AddCredentialOptions,
AddGatewayOptions,
AddGatewayTargetOptions,
AddIdentityOptions,
AddMemoryOptions,
} from './types';
import { existsSync, readFileSync } from 'fs';
Expand DownExpand Up@@ -50,7 +50,7 @@ async function validateCredentialExists(credentialName: string): Promise<Validat
if (availableCredentials.length === 0) {
return {
valid: false,
error: `Credential "${credentialName}" not found. No credentials are configured. Add credentials using 'agentcore add identity'.`,
error: `Credential "${credentialName}" not found. No credentials are configured. Add credentials using 'agentcore add credential'.`,
};
}
return {
Expand DownExpand Up@@ -680,8 +680,8 @@ export function validateAddMemoryOptions(options: AddMemoryOptions): ValidationR
return { valid: true };
}

// Identity validation (v2: credential resource, no owner)
export function validateAddIdentityOptions(options: AddIdentityOptions): ValidationResult {
// Credential validation (v2: credential resource, no owner)
export function validateAddCredentialOptions(options: AddCredentialOptions): ValidationResult {
if (!options.name) {
return { valid: false, error: '--name is required' };
}
Expand Down
10 changes: 7 additions & 3 deletions src/cli/commands/deploy/command.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -90,7 +90,7 @@ async function handleDeployCLI(options: DeployOptions): Promise<void> {
if (options.diff) {
console.log(`\n✓ Diff complete for '${result.targetName}' (stack: ${result.stackName})`);
} else if (options.plan) {
console.log(`\n✓ Plan complete for '${result.targetName}' (stack: ${result.stackName})`);
console.log(`\n✓ Dry run complete for '${result.targetName}' (stack: ${result.stackName})`);
console.log('\nRun `agentcore deploy` to deploy.');
} else {
console.log(`\n✓ Deployed to '${result.targetName}' (stack: ${result.stackName})`);
Expand DownExpand Up@@ -136,23 +136,27 @@ export const registerDeploy = (program: Command) => {
.option('-y, --yes', 'Auto-confirm prompts, read credentials from env [non-interactive]')
.option('-v, --verbose', 'Show resource-level deployment events [non-interactive]')
.option('--json', 'Output as JSON [non-interactive]')
.option('--plan', 'Preview deployment without deploying (dry-run) [non-interactive]')
.option('--dry-run', 'Preview deployment without deploying [non-interactive]')
.option('--plan', 'Preview deployment without deploying (alias for --dry-run) [non-interactive]')
.option('--diff', 'Show CDK diff without deploying [non-interactive]')
.action(
async (cliOptions: {
target?: string;
yes?: boolean;
verbose?: boolean;
json?: boolean;
dryRun?: boolean;
plan?: boolean;
diff?: boolean;
}) => {
try {
requireProject();
if (cliOptions.json || cliOptions.target || cliOptions.plan || cliOptions.yes || cliOptions.verbose) {
const isDryRun = cliOptions.dryRun ?? cliOptions.plan;
if (cliOptions.json || cliOptions.target || isDryRun || cliOptions.yes || cliOptions.verbose) {
// CLI mode - any flag triggers non-interactive mode
const options = {
...cliOptions,
plan: isDryRun,
target: cliOptions.target ?? 'default',
progress: !cliOptions.json,
};
Expand Down
14 changes: 7 additions & 7 deletions src/cli/commands/remove/__tests__/remove-identity.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,7 @@ import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

describe('remove identity command', () => {
describe('remove credential command', () => {
let testDir: string;
let projectDir: string;
const identityName = 'TestIdentity';
Expand All@@ -24,7 +24,7 @@ describe('remove identity command', () => {

// Add identity as top-level credential
result = await runCLI(
['add', 'identity', '--name', identityName, '--api-key', 'test-key-123', '--json'],
['add', 'credential', '--name', identityName, '--api-key', 'test-key-123', '--json'],
projectDir
);
if (result.exitCode !== 0) {
Expand All@@ -38,15 +38,15 @@ describe('remove identity command', () => {

describe('validation', () => {
it('requires name flag', async () => {
const result = await runCLI(['remove', 'identity', '--json'], projectDir);
const result = await runCLI(['remove', 'credential', '--json'], projectDir);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
expect(json.error.includes('--name'), `Error: ${json.error}`).toBeTruthy();
});

it('rejects non-existent identity', async () => {
const result = await runCLI(['remove', 'identity', '--name', 'nonexistent', '--json'], projectDir);
const result = await runCLI(['remove', 'credential', '--name', 'nonexistent', '--json'], projectDir);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
Expand All@@ -58,9 +58,9 @@ describe('remove identity command', () => {
it('removes credential without dependents', async () => {
// Add a temp credential to remove
const tempId = `tempId${Date.now()}`;
await runCLI(['add', 'identity', '--name', tempId, '--api-key', 'temp-key', '--json'], projectDir);
await runCLI(['add', 'credential', '--name', tempId, '--api-key', 'temp-key', '--json'], projectDir);

const result = await runCLI(['remove', 'identity', '--name', tempId, '--json'], projectDir);
const result = await runCLI(['remove', 'credential', '--name', tempId, '--json'], projectDir);
expect(result.exitCode, `stdout: ${result.stdout}`).toBe(0);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(true);
Expand All@@ -72,7 +72,7 @@ describe('remove identity command', () => {
});

it('removes the setup credential', async () => {
const result = await runCLI(['remove', 'identity', '--name', identityName, '--json'], projectDir);
const result = await runCLI(['remove', 'credential', '--name', identityName, '--json'], projectDir);
expect(result.exitCode, `stdout: ${result.stdout}`).toBe(0);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(true);
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/remove/__tests__/validate.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,7 +21,7 @@ describe('validateRemoveOptions', () => {
});

it('returns valid with no json and no name', () => {
expect(validateRemoveOptions({ resourceType: 'identity' })).toEqual({ valid: true });
expect(validateRemoveOptions({ resourceType: 'credential' })).toEqual({ valid: true });
});
});

Expand Down
12 changes: 7 additions & 5 deletions src/cli/commands/remove/command.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -62,15 +62,17 @@ export const registerRemove = (program: Command): Command => {
removeCommand
.command('all')
.description('Reset all agentcore schemas to empty state')
.option('--force', 'Skip confirmation prompts [non-interactive]')
.option('-y, --yes', 'Skip confirmation prompts [non-interactive]')
.option('--force', 'Skip confirmation prompts (alias for --yes) [non-interactive]')
.option('--dry-run', 'Show what would be reset without actually resetting [non-interactive]')
.option('--json', 'Output as JSON [non-interactive]')
.action(async (cliOptions: { force?: boolean; dryRun?: boolean; json?: boolean }) => {
.action(async (cliOptions: { yes?: boolean; force?: boolean; dryRun?: boolean; json?: boolean }) => {
try {
const skipConfirm = cliOptions.yes ?? cliOptions.force;
// Any flag triggers non-interactive CLI mode
if (cliOptions.force || cliOptions.dryRun || cliOptions.json) {
if (skipConfirm || cliOptions.dryRun || cliOptions.json) {
await handleRemoveAllCLI({
force: cliOptions.force,
force: skipConfirm,
dryRun: cliOptions.dryRun,
json: cliOptions.json,
});
Expand All@@ -95,7 +97,7 @@ export const registerRemove = (program: Command): Command => {
}
});

// Resource subcommands (agent, memory, identity, gateway, mcp-tool) are registered
// Resource subcommands (agent, memory, credential, gateway, mcp-tool) are registered
// via primitive.registerCommands() in cli.ts

// Catch-all for TUI fallback when no subcommand is specified.
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/remove/types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,7 +3,7 @@ export type ResourceType =
| 'gateway'
| 'gateway-target'
| 'memory'
| 'identity'
| 'credential'
| 'evaluator'
| 'online-eval'
| 'policy-engine'
Expand Down
2 changes: 1 addition & 1 deletion src/cli/logging/remove-logger.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,7 @@ export interface RemoveLoggerOptions {
resourceType:
| 'agent'
| 'memory'
| 'identity'
| 'credential'
| 'gateway'
| 'gateway-target'
| 'evaluator'
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions src/cli/commands/add/__tests__/add-identity.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,7 @@ import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

describe('add identity command', () => {
describe('add credential command', () => {
let testDir: string;
let projectDir: string;

Expand All@@ -28,27 +28,27 @@ describe('add identity command', () => {

describe('validation', () => {
it('requires name flag', async () => {
const result = await runCLI(['add', 'identity', '--json'], projectDir);
const result = await runCLI(['add', 'credential', '--json'], projectDir);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
expect(json.error.includes('--name'), `Error: ${json.error}`).toBeTruthy();
});

it('requires api-key flag', async () => {
const result = await runCLI(['add', 'identity', '--name', 'test', '--json'], projectDir);
const result = await runCLI(['add', 'credential', '--name', 'test', '--json'], projectDir);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
expect(json.error.includes('--api-key'), `Error: ${json.error}`).toBeTruthy();
});
});

describe('identity creation', () => {
describe('credential creation', () => {
it('creates credential as top-level resource', async () => {
const identityName = `id${Date.now()}`;
const result = await runCLI(
['add', 'identity', '--name', identityName, '--api-key', 'test-key-123', '--json'],
['add', 'credential', '--name', identityName, '--api-key', 'test-key-123', '--json'],
projectDir
);

Expand All@@ -65,13 +65,13 @@ describe('add identity command', () => {
});
});

describe('oauth identity creation', () => {
describe('oauth credential creation', () => {
it('creates OAuth credential with discovery URL and scopes', async () => {
const identityName = `oauth-${Date.now()}`;
const result = await runCLI(
[
'add',
'identity',
'credential',
'--type',
'oauth',
'--name',
Expand Down
28 changes: 14 additions & 14 deletions src/cli/commands/add/__tests__/validate.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
import type {
AddAgentOptions,
AddCredentialOptions,
AddGatewayOptions,
AddGatewayTargetOptions,
AddIdentityOptions,
AddMemoryOptions,
} from '../types.js';
import {
validateAddAgentOptions,
validateAddCredentialOptions,
validateAddGatewayOptions,
validateAddGatewayTargetOptions,
validateAddIdentityOptions,
validateAddMemoryOptions,
} from '../validate.js';
import { existsSync, readFileSync } from 'fs';
Expand DownExpand Up@@ -75,7 +75,7 @@ const validMemoryOptions: AddMemoryOptions = {
strategies: 'SEMANTIC,SUMMARIZATION',
};

const validIdentityOptions: AddIdentityOptions = {
const validCredentialOptions: AddCredentialOptions = {
name: 'test-identity',
apiKey: 'test-key',
};
Expand DownExpand Up@@ -996,25 +996,25 @@ describe('validate', () => {
});
});

describe('validateAddIdentityOptions', () => {
describe('validateAddCredentialOptions', () => {
// AC23: Required fields validated
it('returns error for missing required fields', () => {
const requiredFields: { field: keyof AddIdentityOptions; error: string }[] = [
const requiredFields: { field: keyof AddCredentialOptions; error: string }[] = [
{ field: 'name', error: '--name is required' },
{ field: 'apiKey', error: '--api-key is required' },
];

for (const { field, error } of requiredFields) {
const opts = { ...validIdentityOptions, [field]: undefined };
const result = validateAddIdentityOptions(opts);
const opts = { ...validCredentialOptions, [field]: undefined };
const result = validateAddCredentialOptions(opts);
expect(result.valid, `Should fail for missing ${String(field)}`).toBe(false);
expect(result.error).toBe(error);
}
});

// AC25: Valid options pass
it('passes for valid options', () => {
expect(validateAddIdentityOptions(validIdentityOptions)).toEqual({ valid: true });
expect(validateAddCredentialOptions(validCredentialOptions)).toEqual({ valid: true });
});
});

Expand DownExpand Up@@ -1193,9 +1193,9 @@ describe('validate', () => {
});
});

describe('validateAddIdentityOptions OAuth', () => {
describe('validateAddCredentialOptions OAuth', () => {
it('passes for valid OAuth identity', () => {
const result = validateAddIdentityOptions({
const result = validateAddCredentialOptions({
name: 'my-oauth',
type: 'oauth',
discoveryUrl: 'https://auth.example.com/.well-known/openid-configuration',
Expand All@@ -1206,7 +1206,7 @@ describe('validate', () => {
});

it('returns error for OAuth without discovery-url', () => {
const result = validateAddIdentityOptions({
const result = validateAddCredentialOptions({
name: 'my-oauth',
type: 'oauth',
clientId: 'client123',
Expand All@@ -1217,7 +1217,7 @@ describe('validate', () => {
});

it('returns error for OAuth without client-id', () => {
const result = validateAddIdentityOptions({
const result = validateAddCredentialOptions({
name: 'my-oauth',
type: 'oauth',
discoveryUrl: 'https://auth.example.com',
Expand All@@ -1228,7 +1228,7 @@ describe('validate', () => {
});

it('returns error for OAuth without client-secret', () => {
const result = validateAddIdentityOptions({
const result = validateAddCredentialOptions({
name: 'my-oauth',
type: 'oauth',
discoveryUrl: 'https://auth.example.com',
Expand All@@ -1239,7 +1239,7 @@ describe('validate', () => {
});

it('still requires api-key for default type', () => {
const result = validateAddIdentityOptions({ name: 'my-key' });
const result = validateAddCredentialOptions({ name: 'my-key' });
expect(result.valid).toBe(false);
expect(result.error).toContain('--api-key');
});
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/add/command.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,7 +33,7 @@ export function registerAdd(program: Command): Command {
);
});

// Subcommands (agent, memory, identity, gateway, gateway-target) are registered
// Subcommands (agent, memory, credential, gateway, gateway-target) are registered
// via primitive.registerCommands() in cli.ts

return addCmd;
Expand Down
9 changes: 6 additions & 3 deletions src/cli/commands/add/types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -118,8 +118,8 @@ export interface AddMemoryResult {
error?: string;
}

// Identity types (v2: credential, no owner/user concept)
export interface AddIdentityOptions {
// Credential types (v2: credential, no owner/user concept)
export interface AddCredentialOptions {
name?: string;
type?: 'api-key' | 'oauth';
apiKey?: string;
Expand All@@ -130,7 +130,10 @@ export interface AddIdentityOptions {
json?: boolean;
}

export interface AddIdentityResult {
/** @deprecated Use AddCredentialOptions */
export type AddIdentityOptions = AddCredentialOptions;

export interface AddCredentialResult {
success: boolean;
credentialName?: string;
error?: string;
Expand Down
8 changes: 4 additions & 4 deletions src/cli/commands/add/validate.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,9 +19,9 @@ import { validateVpcOptions } from '../shared/vpc-utils';
import { validateJwtAuthorizerOptions } from './auth-options';
import type {
AddAgentOptions,
AddCredentialOptions,
AddGatewayOptions,
AddGatewayTargetOptions,
AddIdentityOptions,
AddMemoryOptions,
} from './types';
import { existsSync, readFileSync } from 'fs';
Expand DownExpand Up@@ -50,7 +50,7 @@ async function validateCredentialExists(credentialName: string): Promise<Validat
if (availableCredentials.length === 0) {
return {
valid: false,
error: `Credential "${credentialName}" not found. No credentials are configured. Add credentials using 'agentcore add identity'.`,
error: `Credential "${credentialName}" not found. No credentials are configured. Add credentials using 'agentcore add credential'.`,
};
}
return {
Expand DownExpand Up@@ -680,8 +680,8 @@ export function validateAddMemoryOptions(options: AddMemoryOptions): ValidationR
return { valid: true };
}

// Identity validation (v2: credential resource, no owner)
export function validateAddIdentityOptions(options: AddIdentityOptions): ValidationResult {
// Credential validation (v2: credential resource, no owner)
export function validateAddCredentialOptions(options: AddCredentialOptions): ValidationResult {
if (!options.name) {
return { valid: false, error: '--name is required' };
}
Expand Down
10 changes: 7 additions & 3 deletions src/cli/commands/deploy/command.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -90,7 +90,7 @@ async function handleDeployCLI(options: DeployOptions): Promise<void> {
if (options.diff) {
console.log(`\n✓ Diff complete for '${result.targetName}' (stack: ${result.stackName})`);
} else if (options.plan) {
console.log(`\n✓ Plan complete for '${result.targetName}' (stack: ${result.stackName})`);
console.log(`\n✓ Dry run complete for '${result.targetName}' (stack: ${result.stackName})`);
console.log('\nRun `agentcore deploy` to deploy.');
} else {
console.log(`\n✓ Deployed to '${result.targetName}' (stack: ${result.stackName})`);
Expand DownExpand Up@@ -136,23 +136,27 @@ export const registerDeploy = (program: Command) => {
.option('-y, --yes', 'Auto-confirm prompts, read credentials from env [non-interactive]')
.option('-v, --verbose', 'Show resource-level deployment events [non-interactive]')
.option('--json', 'Output as JSON [non-interactive]')
.option('--plan', 'Preview deployment without deploying (dry-run) [non-interactive]')
.option('--dry-run', 'Preview deployment without deploying [non-interactive]')
.option('--plan', 'Preview deployment without deploying (alias for --dry-run) [non-interactive]')
.option('--diff', 'Show CDK diff without deploying [non-interactive]')
.action(
async (cliOptions: {
target?: string;
yes?: boolean;
verbose?: boolean;
json?: boolean;
dryRun?: boolean;
plan?: boolean;
diff?: boolean;
}) => {
try {
requireProject();
if (cliOptions.json || cliOptions.target || cliOptions.plan || cliOptions.yes || cliOptions.verbose) {
const isDryRun = cliOptions.dryRun ?? cliOptions.plan;
if (cliOptions.json || cliOptions.target || isDryRun || cliOptions.yes || cliOptions.verbose) {
// CLI mode - any flag triggers non-interactive mode
const options = {
...cliOptions,
plan: isDryRun,
target: cliOptions.target ?? 'default',
progress: !cliOptions.json,
};
Expand Down
14 changes: 7 additions & 7 deletions src/cli/commands/remove/__tests__/remove-identity.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,7 @@ import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

describe('remove identity command', () => {
describe('remove credential command', () => {
let testDir: string;
let projectDir: string;
const identityName = 'TestIdentity';
Expand All@@ -24,7 +24,7 @@ describe('remove identity command', () => {

// Add identity as top-level credential
result = await runCLI(
['add', 'identity', '--name', identityName, '--api-key', 'test-key-123', '--json'],
['add', 'credential', '--name', identityName, '--api-key', 'test-key-123', '--json'],
projectDir
);
if (result.exitCode !== 0) {
Expand All@@ -38,15 +38,15 @@ describe('remove identity command', () => {

describe('validation', () => {
it('requires name flag', async () => {
const result = await runCLI(['remove', 'identity', '--json'], projectDir);
const result = await runCLI(['remove', 'credential', '--json'], projectDir);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
expect(json.error.includes('--name'), `Error: ${json.error}`).toBeTruthy();
});

it('rejects non-existent identity', async () => {
const result = await runCLI(['remove', 'identity', '--name', 'nonexistent', '--json'], projectDir);
const result = await runCLI(['remove', 'credential', '--name', 'nonexistent', '--json'], projectDir);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
Expand All@@ -58,9 +58,9 @@ describe('remove identity command', () => {
it('removes credential without dependents', async () => {
// Add a temp credential to remove
const tempId = `tempId${Date.now()}`;
await runCLI(['add', 'identity', '--name', tempId, '--api-key', 'temp-key', '--json'], projectDir);
await runCLI(['add', 'credential', '--name', tempId, '--api-key', 'temp-key', '--json'], projectDir);

const result = await runCLI(['remove', 'identity', '--name', tempId, '--json'], projectDir);
const result = await runCLI(['remove', 'credential', '--name', tempId, '--json'], projectDir);
expect(result.exitCode, `stdout: ${result.stdout}`).toBe(0);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(true);
Expand All@@ -72,7 +72,7 @@ describe('remove identity command', () => {
});

it('removes the setup credential', async () => {
const result = await runCLI(['remove', 'identity', '--name', identityName, '--json'], projectDir);
const result = await runCLI(['remove', 'credential', '--name', identityName, '--json'], projectDir);
expect(result.exitCode, `stdout: ${result.stdout}`).toBe(0);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(true);
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/remove/__tests__/validate.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,7 +21,7 @@ describe('validateRemoveOptions', () => {
});

it('returns valid with no json and no name', () => {
expect(validateRemoveOptions({ resourceType: 'identity' })).toEqual({ valid: true });
expect(validateRemoveOptions({ resourceType: 'credential' })).toEqual({ valid: true });
});
});

Expand Down
12 changes: 7 additions & 5 deletions src/cli/commands/remove/command.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -62,15 +62,17 @@ export const registerRemove = (program: Command): Command => {
removeCommand
.command('all')
.description('Reset all agentcore schemas to empty state')
.option('--force', 'Skip confirmation prompts [non-interactive]')
.option('-y, --yes', 'Skip confirmation prompts [non-interactive]')
.option('--force', 'Skip confirmation prompts (alias for --yes) [non-interactive]')
.option('--dry-run', 'Show what would be reset without actually resetting [non-interactive]')
.option('--json', 'Output as JSON [non-interactive]')
.action(async (cliOptions: { force?: boolean; dryRun?: boolean; json?: boolean }) => {
.action(async (cliOptions: { yes?: boolean; force?: boolean; dryRun?: boolean; json?: boolean }) => {
try {
const skipConfirm = cliOptions.yes ?? cliOptions.force;
// Any flag triggers non-interactive CLI mode
if (cliOptions.force || cliOptions.dryRun || cliOptions.json) {
if (skipConfirm || cliOptions.dryRun || cliOptions.json) {
await handleRemoveAllCLI({
force: cliOptions.force,
force: skipConfirm,
dryRun: cliOptions.dryRun,
json: cliOptions.json,
});
Expand All@@ -95,7 +97,7 @@ export const registerRemove = (program: Command): Command => {
}
});

// Resource subcommands (agent, memory, identity, gateway, mcp-tool) are registered
// Resource subcommands (agent, memory, credential, gateway, mcp-tool) are registered
// via primitive.registerCommands() in cli.ts

// Catch-all for TUI fallback when no subcommand is specified.
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/remove/types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,7 +3,7 @@ export type ResourceType =
| 'gateway'
| 'gateway-target'
| 'memory'
| 'identity'
| 'credential'
| 'evaluator'
| 'online-eval'
| 'policy-engine'
Expand Down
2 changes: 1 addition & 1 deletion src/cli/logging/remove-logger.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,7 @@ export interface RemoveLoggerOptions {
resourceType:
| 'agent'
| 'memory'
| 'identity'
| 'credential'
| 'gateway'
| 'gateway-target'
| 'evaluator'
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions src/cli/commands/add/__tests__/add-identity.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,7 @@ import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

describe('add identity command', () => {
describe('add credential command', () => {
let testDir: string;
let projectDir: string;

Expand All@@ -28,27 +28,27 @@ describe('add identity command', () => {

describe('validation', () => {
it('requires name flag', async () => {
const result = await runCLI(['add', 'identity', '--json'], projectDir);
const result = await runCLI(['add', 'credential', '--json'], projectDir);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
expect(json.error.includes('--name'), `Error: ${json.error}`).toBeTruthy();
});

it('requires api-key flag', async () => {
const result = await runCLI(['add', 'identity', '--name', 'test', '--json'], projectDir);
const result = await runCLI(['add', 'credential', '--name', 'test', '--json'], projectDir);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
expect(json.error.includes('--api-key'), `Error: ${json.error}`).toBeTruthy();
});
});

describe('identity creation', () => {
describe('credential creation', () => {
it('creates credential as top-level resource', async () => {
const identityName = `id${Date.now()}`;
const result = await runCLI(
['add', 'identity', '--name', identityName, '--api-key', 'test-key-123', '--json'],
['add', 'credential', '--name', identityName, '--api-key', 'test-key-123', '--json'],
projectDir
);

Expand All@@ -65,13 +65,13 @@ describe('add identity command', () => {
});
});

describe('oauth identity creation', () => {
describe('oauth credential creation', () => {
it('creates OAuth credential with discovery URL and scopes', async () => {
const identityName = `oauth-${Date.now()}`;
const result = await runCLI(
[
'add',
'identity',
'credential',
'--type',
'oauth',
'--name',
Expand Down
28 changes: 14 additions & 14 deletions src/cli/commands/add/__tests__/validate.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
import type {
AddAgentOptions,
AddCredentialOptions,
AddGatewayOptions,
AddGatewayTargetOptions,
AddIdentityOptions,
AddMemoryOptions,
} from '../types.js';
import {
validateAddAgentOptions,
validateAddCredentialOptions,
validateAddGatewayOptions,
validateAddGatewayTargetOptions,
validateAddIdentityOptions,
validateAddMemoryOptions,
} from '../validate.js';
import { existsSync, readFileSync } from 'fs';
Expand DownExpand Up@@ -75,7 +75,7 @@ const validMemoryOptions: AddMemoryOptions = {
strategies: 'SEMANTIC,SUMMARIZATION',
};

const validIdentityOptions: AddIdentityOptions = {
const validCredentialOptions: AddCredentialOptions = {
name: 'test-identity',
apiKey: 'test-key',
};
Expand DownExpand Up@@ -996,25 +996,25 @@ describe('validate', () => {
});
});

describe('validateAddIdentityOptions', () => {
describe('validateAddCredentialOptions', () => {
// AC23: Required fields validated
it('returns error for missing required fields', () => {
const requiredFields: { field: keyof AddIdentityOptions; error: string }[] = [
const requiredFields: { field: keyof AddCredentialOptions; error: string }[] = [
{ field: 'name', error: '--name is required' },
{ field: 'apiKey', error: '--api-key is required' },
];

for (const { field, error } of requiredFields) {
const opts = { ...validIdentityOptions, [field]: undefined };
const result = validateAddIdentityOptions(opts);
const opts = { ...validCredentialOptions, [field]: undefined };
const result = validateAddCredentialOptions(opts);
expect(result.valid, `Should fail for missing ${String(field)}`).toBe(false);
expect(result.error).toBe(error);
}
});

// AC25: Valid options pass
it('passes for valid options', () => {
expect(validateAddIdentityOptions(validIdentityOptions)).toEqual({ valid: true });
expect(validateAddCredentialOptions(validCredentialOptions)).toEqual({ valid: true });
});
});

Expand DownExpand Up@@ -1193,9 +1193,9 @@ describe('validate', () => {
});
});

describe('validateAddIdentityOptions OAuth', () => {
describe('validateAddCredentialOptions OAuth', () => {
it('passes for valid OAuth identity', () => {
const result = validateAddIdentityOptions({
const result = validateAddCredentialOptions({
name: 'my-oauth',
type: 'oauth',
discoveryUrl: 'https://auth.example.com/.well-known/openid-configuration',
Expand All@@ -1206,7 +1206,7 @@ describe('validate', () => {
});

it('returns error for OAuth without discovery-url', () => {
const result = validateAddIdentityOptions({
const result = validateAddCredentialOptions({
name: 'my-oauth',
type: 'oauth',
clientId: 'client123',
Expand All@@ -1217,7 +1217,7 @@ describe('validate', () => {
});

it('returns error for OAuth without client-id', () => {
const result = validateAddIdentityOptions({
const result = validateAddCredentialOptions({
name: 'my-oauth',
type: 'oauth',
discoveryUrl: 'https://auth.example.com',
Expand All@@ -1228,7 +1228,7 @@ describe('validate', () => {
});

it('returns error for OAuth without client-secret', () => {
const result = validateAddIdentityOptions({
const result = validateAddCredentialOptions({
name: 'my-oauth',
type: 'oauth',
discoveryUrl: 'https://auth.example.com',
Expand All@@ -1239,7 +1239,7 @@ describe('validate', () => {
});

it('still requires api-key for default type', () => {
const result = validateAddIdentityOptions({ name: 'my-key' });
const result = validateAddCredentialOptions({ name: 'my-key' });
expect(result.valid).toBe(false);
expect(result.error).toContain('--api-key');
});
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/add/command.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,7 +33,7 @@ export function registerAdd(program: Command): Command {
);
});

// Subcommands (agent, memory, identity, gateway, gateway-target) are registered
// Subcommands (agent, memory, credential, gateway, gateway-target) are registered
// via primitive.registerCommands() in cli.ts

return addCmd;
Expand Down
9 changes: 6 additions & 3 deletions src/cli/commands/add/types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -118,8 +118,8 @@ export interface AddMemoryResult {
error?: string;
}

// Identity types (v2: credential, no owner/user concept)
export interface AddIdentityOptions {
// Credential types (v2: credential, no owner/user concept)
export interface AddCredentialOptions {
name?: string;
type?: 'api-key' | 'oauth';
apiKey?: string;
Expand All@@ -130,7 +130,10 @@ export interface AddIdentityOptions {
json?: boolean;
}

export interface AddIdentityResult {
/** @deprecated Use AddCredentialOptions */
export type AddIdentityOptions = AddCredentialOptions;

export interface AddCredentialResult {
success: boolean;
credentialName?: string;
error?: string;
Expand Down
8 changes: 4 additions & 4 deletions src/cli/commands/add/validate.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,9 +19,9 @@ import { validateVpcOptions } from '../shared/vpc-utils';
import { validateJwtAuthorizerOptions } from './auth-options';
import type {
AddAgentOptions,
AddCredentialOptions,
AddGatewayOptions,
AddGatewayTargetOptions,
AddIdentityOptions,
AddMemoryOptions,
} from './types';
import { existsSync, readFileSync } from 'fs';
Expand DownExpand Up@@ -50,7 +50,7 @@ async function validateCredentialExists(credentialName: string): Promise<Validat
if (availableCredentials.length === 0) {
return {
valid: false,
error: `Credential "${credentialName}" not found. No credentials are configured. Add credentials using 'agentcore add identity'.`,
error: `Credential "${credentialName}" not found. No credentials are configured. Add credentials using 'agentcore add credential'.`,
};
}
return {
Expand DownExpand Up@@ -680,8 +680,8 @@ export function validateAddMemoryOptions(options: AddMemoryOptions): ValidationR
return { valid: true };
}

// Identity validation (v2: credential resource, no owner)
export function validateAddIdentityOptions(options: AddIdentityOptions): ValidationResult {
// Credential validation (v2: credential resource, no owner)
export function validateAddCredentialOptions(options: AddCredentialOptions): ValidationResult {
if (!options.name) {
return { valid: false, error: '--name is required' };
}
Expand Down
10 changes: 7 additions & 3 deletions src/cli/commands/deploy/command.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -90,7 +90,7 @@ async function handleDeployCLI(options: DeployOptions): Promise<void> {
if (options.diff) {
console.log(`\n✓ Diff complete for '${result.targetName}' (stack: ${result.stackName})`);
} else if (options.plan) {
console.log(`\n✓ Plan complete for '${result.targetName}' (stack: ${result.stackName})`);
console.log(`\n✓ Dry run complete for '${result.targetName}' (stack: ${result.stackName})`);
console.log('\nRun `agentcore deploy` to deploy.');
} else {
console.log(`\n✓ Deployed to '${result.targetName}' (stack: ${result.stackName})`);
Expand DownExpand Up@@ -136,23 +136,27 @@ export const registerDeploy = (program: Command) => {
.option('-y, --yes', 'Auto-confirm prompts, read credentials from env [non-interactive]')
.option('-v, --verbose', 'Show resource-level deployment events [non-interactive]')
.option('--json', 'Output as JSON [non-interactive]')
.option('--plan', 'Preview deployment without deploying (dry-run) [non-interactive]')
.option('--dry-run', 'Preview deployment without deploying [non-interactive]')
.option('--plan', 'Preview deployment without deploying (alias for --dry-run) [non-interactive]')
.option('--diff', 'Show CDK diff without deploying [non-interactive]')
.action(
async (cliOptions: {
target?: string;
yes?: boolean;
verbose?: boolean;
json?: boolean;
dryRun?: boolean;
plan?: boolean;
diff?: boolean;
}) => {
try {
requireProject();
if (cliOptions.json || cliOptions.target || cliOptions.plan || cliOptions.yes || cliOptions.verbose) {
const isDryRun = cliOptions.dryRun ?? cliOptions.plan;
if (cliOptions.json || cliOptions.target || isDryRun || cliOptions.yes || cliOptions.verbose) {
// CLI mode - any flag triggers non-interactive mode
const options = {
...cliOptions,
plan: isDryRun,
target: cliOptions.target ?? 'default',
progress: !cliOptions.json,
};
Expand Down
14 changes: 7 additions & 7 deletions src/cli/commands/remove/__tests__/remove-identity.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,7 @@ import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

describe('remove identity command', () => {
describe('remove credential command', () => {
let testDir: string;
let projectDir: string;
const identityName = 'TestIdentity';
Expand All@@ -24,7 +24,7 @@ describe('remove identity command', () => {

// Add identity as top-level credential
result = await runCLI(
['add', 'identity', '--name', identityName, '--api-key', 'test-key-123', '--json'],
['add', 'credential', '--name', identityName, '--api-key', 'test-key-123', '--json'],
projectDir
);
if (result.exitCode !== 0) {
Expand All@@ -38,15 +38,15 @@ describe('remove identity command', () => {

describe('validation', () => {
it('requires name flag', async () => {
const result = await runCLI(['remove', 'identity', '--json'], projectDir);
const result = await runCLI(['remove', 'credential', '--json'], projectDir);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
expect(json.error.includes('--name'), `Error: ${json.error}`).toBeTruthy();
});

it('rejects non-existent identity', async () => {
const result = await runCLI(['remove', 'identity', '--name', 'nonexistent', '--json'], projectDir);
const result = await runCLI(['remove', 'credential', '--name', 'nonexistent', '--json'], projectDir);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
Expand All@@ -58,9 +58,9 @@ describe('remove identity command', () => {
it('removes credential without dependents', async () => {
// Add a temp credential to remove
const tempId = `tempId${Date.now()}`;
await runCLI(['add', 'identity', '--name', tempId, '--api-key', 'temp-key', '--json'], projectDir);
await runCLI(['add', 'credential', '--name', tempId, '--api-key', 'temp-key', '--json'], projectDir);

const result = await runCLI(['remove', 'identity', '--name', tempId, '--json'], projectDir);
const result = await runCLI(['remove', 'credential', '--name', tempId, '--json'], projectDir);
expect(result.exitCode, `stdout: ${result.stdout}`).toBe(0);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(true);
Expand All@@ -72,7 +72,7 @@ describe('remove identity command', () => {
});

it('removes the setup credential', async () => {
const result = await runCLI(['remove', 'identity', '--name', identityName, '--json'], projectDir);
const result = await runCLI(['remove', 'credential', '--name', identityName, '--json'], projectDir);
expect(result.exitCode, `stdout: ${result.stdout}`).toBe(0);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(true);
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/remove/__tests__/validate.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,7 +21,7 @@ describe('validateRemoveOptions', () => {
});

it('returns valid with no json and no name', () => {
expect(validateRemoveOptions({ resourceType: 'identity' })).toEqual({ valid: true });
expect(validateRemoveOptions({ resourceType: 'credential' })).toEqual({ valid: true });
});
});

Expand Down
12 changes: 7 additions & 5 deletions src/cli/commands/remove/command.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -62,15 +62,17 @@ export const registerRemove = (program: Command): Command => {
removeCommand
.command('all')
.description('Reset all agentcore schemas to empty state')
.option('--force', 'Skip confirmation prompts [non-interactive]')
.option('-y, --yes', 'Skip confirmation prompts [non-interactive]')
.option('--force', 'Skip confirmation prompts (alias for --yes) [non-interactive]')
.option('--dry-run', 'Show what would be reset without actually resetting [non-interactive]')
.option('--json', 'Output as JSON [non-interactive]')
.action(async (cliOptions: { force?: boolean; dryRun?: boolean; json?: boolean }) => {
.action(async (cliOptions: { yes?: boolean; force?: boolean; dryRun?: boolean; json?: boolean }) => {
try {
const skipConfirm = cliOptions.yes ?? cliOptions.force;
// Any flag triggers non-interactive CLI mode
if (cliOptions.force || cliOptions.dryRun || cliOptions.json) {
if (skipConfirm || cliOptions.dryRun || cliOptions.json) {
await handleRemoveAllCLI({
force: cliOptions.force,
force: skipConfirm,
dryRun: cliOptions.dryRun,
json: cliOptions.json,
});
Expand All@@ -95,7 +97,7 @@ export const registerRemove = (program: Command): Command => {
}
});

// Resource subcommands (agent, memory, identity, gateway, mcp-tool) are registered
// Resource subcommands (agent, memory, credential, gateway, mcp-tool) are registered
// via primitive.registerCommands() in cli.ts

// Catch-all for TUI fallback when no subcommand is specified.
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/remove/types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,7 +3,7 @@ export type ResourceType =
| 'gateway'
| 'gateway-target'
| 'memory'
| 'identity'
| 'credential'
| 'evaluator'
| 'online-eval'
| 'policy-engine'
Expand Down
2 changes: 1 addition & 1 deletion src/cli/logging/remove-logger.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,7 @@ export interface RemoveLoggerOptions {
resourceType:
| 'agent'
| 'memory'
| 'identity'
| 'credential'
| 'gateway'
| 'gateway-target'
| 'evaluator'
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions src/cli/commands/add/__tests__/add-identity.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,7 @@ import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

describe('add identity command', () => {
describe('add credential command', () => {
let testDir: string;
let projectDir: string;

Expand All@@ -28,27 +28,27 @@ describe('add identity command', () => {

describe('validation', () => {
it('requires name flag', async () => {
const result = await runCLI(['add', 'identity', '--json'], projectDir);
const result = await runCLI(['add', 'credential', '--json'], projectDir);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
expect(json.error.includes('--name'), `Error: ${json.error}`).toBeTruthy();
});

it('requires api-key flag', async () => {
const result = await runCLI(['add', 'identity', '--name', 'test', '--json'], projectDir);
const result = await runCLI(['add', 'credential', '--name', 'test', '--json'], projectDir);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
expect(json.error.includes('--api-key'), `Error: ${json.error}`).toBeTruthy();
});
});

describe('identity creation', () => {
describe('credential creation', () => {
it('creates credential as top-level resource', async () => {
const identityName = `id${Date.now()}`;
const result = await runCLI(
['add', 'identity', '--name', identityName, '--api-key', 'test-key-123', '--json'],
['add', 'credential', '--name', identityName, '--api-key', 'test-key-123', '--json'],
projectDir
);

Expand All@@ -65,13 +65,13 @@ describe('add identity command', () => {
});
});

describe('oauth identity creation', () => {
describe('oauth credential creation', () => {
it('creates OAuth credential with discovery URL and scopes', async () => {
const identityName = `oauth-${Date.now()}`;
const result = await runCLI(
[
'add',
'identity',
'credential',
'--type',
'oauth',
'--name',
Expand Down
28 changes: 14 additions & 14 deletions src/cli/commands/add/__tests__/validate.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
import type {
AddAgentOptions,
AddCredentialOptions,
AddGatewayOptions,
AddGatewayTargetOptions,
AddIdentityOptions,
AddMemoryOptions,
} from '../types.js';
import {
validateAddAgentOptions,
validateAddCredentialOptions,
validateAddGatewayOptions,
validateAddGatewayTargetOptions,
validateAddIdentityOptions,
validateAddMemoryOptions,
} from '../validate.js';
import { existsSync, readFileSync } from 'fs';
Expand DownExpand Up@@ -75,7 +75,7 @@ const validMemoryOptions: AddMemoryOptions = {
strategies: 'SEMANTIC,SUMMARIZATION',
};

const validIdentityOptions: AddIdentityOptions = {
const validCredentialOptions: AddCredentialOptions = {
name: 'test-identity',
apiKey: 'test-key',
};
Expand DownExpand Up@@ -996,25 +996,25 @@ describe('validate', () => {
});
});

describe('validateAddIdentityOptions', () => {
describe('validateAddCredentialOptions', () => {
// AC23: Required fields validated
it('returns error for missing required fields', () => {
const requiredFields: { field: keyof AddIdentityOptions; error: string }[] = [
const requiredFields: { field: keyof AddCredentialOptions; error: string }[] = [
{ field: 'name', error: '--name is required' },
{ field: 'apiKey', error: '--api-key is required' },
];

for (const { field, error } of requiredFields) {
const opts = { ...validIdentityOptions, [field]: undefined };
const result = validateAddIdentityOptions(opts);
const opts = { ...validCredentialOptions, [field]: undefined };
const result = validateAddCredentialOptions(opts);
expect(result.valid, `Should fail for missing ${String(field)}`).toBe(false);
expect(result.error).toBe(error);
}
});

// AC25: Valid options pass
it('passes for valid options', () => {
expect(validateAddIdentityOptions(validIdentityOptions)).toEqual({ valid: true });
expect(validateAddCredentialOptions(validCredentialOptions)).toEqual({ valid: true });
});
});

Expand DownExpand Up@@ -1193,9 +1193,9 @@ describe('validate', () => {
});
});

describe('validateAddIdentityOptions OAuth', () => {
describe('validateAddCredentialOptions OAuth', () => {
it('passes for valid OAuth identity', () => {
const result = validateAddIdentityOptions({
const result = validateAddCredentialOptions({
name: 'my-oauth',
type: 'oauth',
discoveryUrl: 'https://auth.example.com/.well-known/openid-configuration',
Expand All@@ -1206,7 +1206,7 @@ describe('validate', () => {
});

it('returns error for OAuth without discovery-url', () => {
const result = validateAddIdentityOptions({
const result = validateAddCredentialOptions({
name: 'my-oauth',
type: 'oauth',
clientId: 'client123',
Expand All@@ -1217,7 +1217,7 @@ describe('validate', () => {
});

it('returns error for OAuth without client-id', () => {
const result = validateAddIdentityOptions({
const result = validateAddCredentialOptions({
name: 'my-oauth',
type: 'oauth',
discoveryUrl: 'https://auth.example.com',
Expand All@@ -1228,7 +1228,7 @@ describe('validate', () => {
});

it('returns error for OAuth without client-secret', () => {
const result = validateAddIdentityOptions({
const result = validateAddCredentialOptions({
name: 'my-oauth',
type: 'oauth',
discoveryUrl: 'https://auth.example.com',
Expand All@@ -1239,7 +1239,7 @@ describe('validate', () => {
});

it('still requires api-key for default type', () => {
const result = validateAddIdentityOptions({ name: 'my-key' });
const result = validateAddCredentialOptions({ name: 'my-key' });
expect(result.valid).toBe(false);
expect(result.error).toContain('--api-key');
});
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/add/command.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,7 +33,7 @@ export function registerAdd(program: Command): Command {
);
});

// Subcommands (agent, memory, identity, gateway, gateway-target) are registered
// Subcommands (agent, memory, credential, gateway, gateway-target) are registered
// via primitive.registerCommands() in cli.ts

return addCmd;
Expand Down
9 changes: 6 additions & 3 deletions src/cli/commands/add/types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -118,8 +118,8 @@ export interface AddMemoryResult {
error?: string;
}

// Identity types (v2: credential, no owner/user concept)
export interface AddIdentityOptions {
// Credential types (v2: credential, no owner/user concept)
export interface AddCredentialOptions {
name?: string;
type?: 'api-key' | 'oauth';
apiKey?: string;
Expand All@@ -130,7 +130,10 @@ export interface AddIdentityOptions {
json?: boolean;
}

export interface AddIdentityResult {
/** @deprecated Use AddCredentialOptions */
export type AddIdentityOptions = AddCredentialOptions;

export interface AddCredentialResult {
success: boolean;
credentialName?: string;
error?: string;
Expand Down
8 changes: 4 additions & 4 deletions src/cli/commands/add/validate.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,9 +19,9 @@ import { validateVpcOptions } from '../shared/vpc-utils';
import { validateJwtAuthorizerOptions } from './auth-options';
import type {
AddAgentOptions,
AddCredentialOptions,
AddGatewayOptions,
AddGatewayTargetOptions,
AddIdentityOptions,
AddMemoryOptions,
} from './types';
import { existsSync, readFileSync } from 'fs';
Expand DownExpand Up@@ -50,7 +50,7 @@ async function validateCredentialExists(credentialName: string): Promise<Validat
if (availableCredentials.length === 0) {
return {
valid: false,
error: `Credential "${credentialName}" not found. No credentials are configured. Add credentials using 'agentcore add identity'.`,
error: `Credential "${credentialName}" not found. No credentials are configured. Add credentials using 'agentcore add credential'.`,
};
}
return {
Expand DownExpand Up@@ -680,8 +680,8 @@ export function validateAddMemoryOptions(options: AddMemoryOptions): ValidationR
return { valid: true };
}

// Identity validation (v2: credential resource, no owner)
export function validateAddIdentityOptions(options: AddIdentityOptions): ValidationResult {
// Credential validation (v2: credential resource, no owner)
export function validateAddCredentialOptions(options: AddCredentialOptions): ValidationResult {
if (!options.name) {
return { valid: false, error: '--name is required' };
}
Expand Down
10 changes: 7 additions & 3 deletions src/cli/commands/deploy/command.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -90,7 +90,7 @@ async function handleDeployCLI(options: DeployOptions): Promise<void> {
if (options.diff) {
console.log(`\n✓ Diff complete for '${result.targetName}' (stack: ${result.stackName})`);
} else if (options.plan) {
console.log(`\n✓ Plan complete for '${result.targetName}' (stack: ${result.stackName})`);
console.log(`\n✓ Dry run complete for '${result.targetName}' (stack: ${result.stackName})`);
console.log('\nRun `agentcore deploy` to deploy.');
} else {
console.log(`\n✓ Deployed to '${result.targetName}' (stack: ${result.stackName})`);
Expand DownExpand Up@@ -136,23 +136,27 @@ export const registerDeploy = (program: Command) => {
.option('-y, --yes', 'Auto-confirm prompts, read credentials from env [non-interactive]')
.option('-v, --verbose', 'Show resource-level deployment events [non-interactive]')
.option('--json', 'Output as JSON [non-interactive]')
.option('--plan', 'Preview deployment without deploying (dry-run) [non-interactive]')
.option('--dry-run', 'Preview deployment without deploying [non-interactive]')
.option('--plan', 'Preview deployment without deploying (alias for --dry-run) [non-interactive]')
.option('--diff', 'Show CDK diff without deploying [non-interactive]')
.action(
async (cliOptions: {
target?: string;
yes?: boolean;
verbose?: boolean;
json?: boolean;
dryRun?: boolean;
plan?: boolean;
diff?: boolean;
}) => {
try {
requireProject();
if (cliOptions.json || cliOptions.target || cliOptions.plan || cliOptions.yes || cliOptions.verbose) {
const isDryRun = cliOptions.dryRun ?? cliOptions.plan;
if (cliOptions.json || cliOptions.target || isDryRun || cliOptions.yes || cliOptions.verbose) {
// CLI mode - any flag triggers non-interactive mode
const options = {
...cliOptions,
plan: isDryRun,
target: cliOptions.target ?? 'default',
progress: !cliOptions.json,
};
Expand Down
14 changes: 7 additions & 7 deletions src/cli/commands/remove/__tests__/remove-identity.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,7 @@ import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

describe('remove identity command', () => {
describe('remove credential command', () => {
let testDir: string;
let projectDir: string;
const identityName = 'TestIdentity';
Expand All@@ -24,7 +24,7 @@ describe('remove identity command', () => {

// Add identity as top-level credential
result = await runCLI(
['add', 'identity', '--name', identityName, '--api-key', 'test-key-123', '--json'],
['add', 'credential', '--name', identityName, '--api-key', 'test-key-123', '--json'],
projectDir
);
if (result.exitCode !== 0) {
Expand All@@ -38,15 +38,15 @@ describe('remove identity command', () => {

describe('validation', () => {
it('requires name flag', async () => {
const result = await runCLI(['remove', 'identity', '--json'], projectDir);
const result = await runCLI(['remove', 'credential', '--json'], projectDir);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
expect(json.error.includes('--name'), `Error: ${json.error}`).toBeTruthy();
});

it('rejects non-existent identity', async () => {
const result = await runCLI(['remove', 'identity', '--name', 'nonexistent', '--json'], projectDir);
const result = await runCLI(['remove', 'credential', '--name', 'nonexistent', '--json'], projectDir);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
Expand All@@ -58,9 +58,9 @@ describe('remove identity command', () => {
it('removes credential without dependents', async () => {
// Add a temp credential to remove
const tempId = `tempId${Date.now()}`;
await runCLI(['add', 'identity', '--name', tempId, '--api-key', 'temp-key', '--json'], projectDir);
await runCLI(['add', 'credential', '--name', tempId, '--api-key', 'temp-key', '--json'], projectDir);

const result = await runCLI(['remove', 'identity', '--name', tempId, '--json'], projectDir);
const result = await runCLI(['remove', 'credential', '--name', tempId, '--json'], projectDir);
expect(result.exitCode, `stdout: ${result.stdout}`).toBe(0);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(true);
Expand All@@ -72,7 +72,7 @@ describe('remove identity command', () => {
});

it('removes the setup credential', async () => {
const result = await runCLI(['remove', 'identity', '--name', identityName, '--json'], projectDir);
const result = await runCLI(['remove', 'credential', '--name', identityName, '--json'], projectDir);
expect(result.exitCode, `stdout: ${result.stdout}`).toBe(0);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(true);
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/remove/__tests__/validate.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,7 +21,7 @@ describe('validateRemoveOptions', () => {
});

it('returns valid with no json and no name', () => {
expect(validateRemoveOptions({ resourceType: 'identity' })).toEqual({ valid: true });
expect(validateRemoveOptions({ resourceType: 'credential' })).toEqual({ valid: true });
});
});

Expand Down
12 changes: 7 additions & 5 deletions src/cli/commands/remove/command.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -62,15 +62,17 @@ export const registerRemove = (program: Command): Command => {
removeCommand
.command('all')
.description('Reset all agentcore schemas to empty state')
.option('--force', 'Skip confirmation prompts [non-interactive]')
.option('-y, --yes', 'Skip confirmation prompts [non-interactive]')
.option('--force', 'Skip confirmation prompts (alias for --yes) [non-interactive]')
.option('--dry-run', 'Show what would be reset without actually resetting [non-interactive]')
.option('--json', 'Output as JSON [non-interactive]')
.action(async (cliOptions: { force?: boolean; dryRun?: boolean; json?: boolean }) => {
.action(async (cliOptions: { yes?: boolean; force?: boolean; dryRun?: boolean; json?: boolean }) => {
try {
const skipConfirm = cliOptions.yes ?? cliOptions.force;
// Any flag triggers non-interactive CLI mode
if (cliOptions.force || cliOptions.dryRun || cliOptions.json) {
if (skipConfirm || cliOptions.dryRun || cliOptions.json) {
await handleRemoveAllCLI({
force: cliOptions.force,
force: skipConfirm,
dryRun: cliOptions.dryRun,
json: cliOptions.json,
});
Expand All@@ -95,7 +97,7 @@ export const registerRemove = (program: Command): Command => {
}
});

// Resource subcommands (agent, memory, identity, gateway, mcp-tool) are registered
// Resource subcommands (agent, memory, credential, gateway, mcp-tool) are registered
// via primitive.registerCommands() in cli.ts

// Catch-all for TUI fallback when no subcommand is specified.
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/remove/types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,7 +3,7 @@ export type ResourceType =
| 'gateway'
| 'gateway-target'
| 'memory'
| 'identity'
| 'credential'
| 'evaluator'
| 'online-eval'
| 'policy-engine'
Expand Down
2 changes: 1 addition & 1 deletion src/cli/logging/remove-logger.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,7 @@ export interface RemoveLoggerOptions {
resourceType:
| 'agent'
| 'memory'
| 'identity'
| 'credential'
| 'gateway'
| 'gateway-target'
| 'evaluator'
Expand Down
Loading
Loading