Skip to content
Merged
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
46 changes: 45 additions & 1 deletion packages/cli/src/deploy-command.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ test('runLogin uses cloud SDK auth, mints a workspace token, and stores it', asy
}
});

test('runLogout clears cloud auth and workspace token even when a workspace is passed', async () => {
test('runLogout preserves shared cloud auth and clears only the workspace token by default', async () => {
const calls: string[] = [];
const restoreDeps = configureDeployCommandForTest({
clearStoredAuth: async () => {
Expand All @@ -124,6 +124,28 @@ test('runLogout clears cloud auth and workspace token even when a workspace is p
try {
await runLogout(['--workspace', 'acme']);
assert.deepEqual(trap.exits, [0]);
assert.deepEqual(calls, ['clear-workspace:acme']);
assert.match(trap.stdout, /workspace login cleared/);
} finally {
trap.restore();
restoreDeps();
}
});

test('runLogout clears shared cloud auth when explicitly requested', async () => {
const calls: string[] = [];
const restoreDeps = configureDeployCommandForTest({
clearStoredAuth: async () => {
calls.push('clear-auth');
},
clearStoredWorkspaceToken: async (workspace?: string) => {
calls.push(`clear-workspace:${workspace ?? ''}`);
}
});
const trap = trapExit(false);
try {
await runLogout(['--workspace', 'acme', '--cloud-auth']);
assert.deepEqual(trap.exits, [0]);
assert.deepEqual(calls, ['clear-auth', 'clear-workspace:acme']);
assert.match(trap.stdout, /logged out/);
} finally {
Expand All @@ -132,6 +154,28 @@ test('runLogout clears cloud auth and workspace token even when a workspace is p
}
});

test('runLogout treats --all as an alias for clearing shared cloud auth', async () => {
const calls: string[] = [];
const restoreDeps = configureDeployCommandForTest({
clearStoredAuth: async () => {
calls.push('clear-auth');
},
clearStoredWorkspaceToken: async (workspace?: string) => {
calls.push(`clear-workspace:${workspace ?? ''}`);
}
});
const trap = trapExit(false);
try {
await runLogout(['--all']);
assert.deepEqual(trap.exits, [0]);
assert.deepEqual(calls, ['clear-auth', 'clear-workspace:']);
assert.match(trap.stdout, /logged out/);
} finally {
trap.restore();
restoreDeps();
}
});

test('parseDeployArgs: single --input parses and forwards', () => {
const parsed = parseDeployArgs(['./persona.json', '--input', 'TOPIC=Deploy v1']);

Expand Down
24 changes: 17 additions & 7 deletions packages/cli/src/deploy-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,11 @@ export async function runLogout(args: readonly string[]): Promise<void> {
}
const opts = parseLogoutArgs(args);
try {
await deployCommandDeps.clearStoredAuth();
if (opts.cloudAuth) {
await deployCommandDeps.clearStoredAuth();
}
await deployCommandDeps.clearStoredWorkspaceToken(opts.workspace);
process.stdout.write('logged out\n');
process.stdout.write(opts.cloudAuth ? 'logged out\n' : 'workspace login cleared\n');
process.exit(0);
} catch (err) {
process.stderr.write(
Expand Down Expand Up @@ -201,8 +203,9 @@ Flags:
const LOGIN_USAGE = `usage: agentworkforce login [flags]

Connect this machine to a workforce workspace using the browser OAuth flow.
The resulting workspace token is stored in the OS keychain when available,
falling back to ~/.agentworkforce/login.json.
If an Agent Relay Cloud login already exists, it is reused and the workforce
workspace token is stored beside it. Set WORKFORCE_LOGIN_FILE to force the
legacy ~/.agentworkforce/login.json-style fallback instead.

Flags:
--workspace <name> Workforce workspace; defaults to WORKFORCE_WORKSPACE_ID or prompt
Expand All @@ -212,10 +215,13 @@ Flags:

const LOGOUT_USAGE = `usage: agentworkforce logout [flags]

Clear the browser OAuth login and the stored workspace token.
Clear the stored workforce workspace token. Agent Relay Cloud browser auth is
shared with agent-relay and is preserved unless --cloud-auth is passed.

Flags:
--workspace <name> Optional workspace token entry to clear
--cloud-auth Also clear the shared Agent Relay Cloud login
--all Alias for --cloud-auth
-h, --help Print this message
`;

Expand Down Expand Up @@ -386,8 +392,9 @@ function parseLoginArgs(args: readonly string[]): { workspace?: string; cloudUrl
};
}

function parseLogoutArgs(args: readonly string[]): { workspace?: string } {
function parseLogoutArgs(args: readonly string[]): { workspace?: string; cloudAuth?: boolean } {
let workspace: string | undefined;
let cloudAuth = false;
for (let i = 0; i < args.length; i += 1) {
const a = args[i];
if (a === '-h' || a === '--help') {
Expand All @@ -397,12 +404,15 @@ function parseLogoutArgs(args: readonly string[]): { workspace?: string } {
workspace = expectValue('--workspace', args[++i]);
} else if (a.startsWith('--workspace=')) {
workspace = expectInlineValue('--workspace', a.slice('--workspace='.length));
} else if (a === '--cloud-auth' || a === '--all') {
cloudAuth = true;
} else {
die(`logout: unknown argument "${a}"`);
}
}
return {
...(workspace ? { workspace } : {})
...(workspace ? { workspace } : {}),
...(cloudAuth ? { cloudAuth } : {})
};
}

Expand Down
Loading