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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ $ ably-interactive
* [`ably logs push subscribe`](#ably-logs-push-subscribe)
* [`ably logs subscribe`](#ably-logs-subscribe)
* [`ably push`](#ably-push)
* [`ably push batch-publish [PAYLOAD]`](#ably-push-batch-publish)
* [`ably push batch-publish [PAYLOAD]`](#ably-push-batch-publish-payload)
* [`ably push channels`](#ably-push-channels)
* [`ably push channels list`](#ably-push-channels-list)
* [`ably push channels list-channels`](#ably-push-channels-list-channels)
Expand Down
14 changes: 6 additions & 8 deletions src/commands/accounts/current.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,9 @@ export default class AccountsCurrent extends ControlBaseCommand {
{
account: {
cached: true,
name: currentAccount.accountName || "Unknown",
id: currentAccount.accountId || "Unknown ID",
user: { email: currentAccount.userEmail || null },
name: currentAccount.accountName,
id: currentAccount.accountId,
user: { email: currentAccount.userEmail },
warning:
"Unable to verify account information. Your access token may have expired.",
},
Expand All @@ -142,11 +142,9 @@ export default class AccountsCurrent extends ControlBaseCommand {
);

// Show cached information
if (currentAccount.accountName || currentAccount.accountId) {
this.log(
`${formatLabel("Account (cached)")} ${chalk.cyan.bold(currentAccount.accountName || "Unknown")} ${chalk.gray(`(${currentAccount.accountId || "Unknown ID"})`)}`,
);
}
this.log(
`${formatLabel("Account (cached)")} ${chalk.cyan.bold(currentAccount.accountName)} ${chalk.gray(`(${currentAccount.accountId})`)}`,
);

if (currentAccount.userEmail) {
this.log(
Expand Down
10 changes: 5 additions & 5 deletions src/commands/accounts/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ export default class AccountsList extends ControlBaseCommand {
account.currentAppId,
}
: undefined,
id: account.accountId || "Unknown",
id: account.accountId,
isCurrent: alias === currentAlias,
name: account.accountName || "Unknown",
user: account.userEmail || "Unknown",
name: account.accountName,
user: account.userEmail,
})),
currentAccount: currentAlias,
},
Expand All @@ -72,9 +72,9 @@ export default class AccountsList extends ControlBaseCommand {
(isCurrent ? chalk.green(" (current)") : ""),
);
this.log(
` ${formatLabel("Name")} ${account.accountName || "Unknown"} (${account.accountId || "Unknown"})`,
` ${formatLabel("Name")} ${account.accountName} (${account.accountId})`,
);
this.log(` ${formatLabel("User")} ${account.userEmail || "Unknown"}`);
this.log(` ${formatLabel("User")} ${account.userEmail}`);

// Count number of apps configured for this account
const appCount = account.apps ? Object.keys(account.apps).length : 0;
Expand Down
10 changes: 5 additions & 5 deletions src/commands/accounts/switch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ export default class AccountsSwitch extends ControlBaseCommand {
{
availableAccounts: accounts.map(({ account, alias }) => ({
alias,
id: account.accountId || "Unknown",
name: account.accountName || "Unknown",
id: account.accountId,
name: account.accountName,
})),
},
);
Expand All @@ -83,7 +83,7 @@ export default class AccountsSwitch extends ControlBaseCommand {
private async switchToAccount(
alias: string,
accounts: Array<{
account: { accountId?: string; accountName?: string };
account: { accountId: string; accountName: string };
alias: string;
}>,
flags: Record<string, unknown>,
Expand All @@ -99,8 +99,8 @@ export default class AccountsSwitch extends ControlBaseCommand {
{
availableAccounts: accounts.map(({ account, alias }) => ({
alias,
id: account.accountId || "Unknown",
name: account.accountName || "Unknown",
id: account.accountId,
name: account.accountName,
})),
},
);
Expand Down
8 changes: 5 additions & 3 deletions src/commands/apps/current.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export default class AppsCurrent extends ControlBaseCommand {
);
} else {
this.log(
`${formatLabel("Account")} ${chalk.cyan.bold(currentAccount.accountName || currentAccountAlias)} ${chalk.gray(`(${currentAccount.accountId || "Unknown ID"})`)}`,
`${formatLabel("Account")} ${chalk.cyan.bold(currentAccount.accountName)} ${chalk.gray(`(${currentAccount.accountId})`)}`,
);
this.log(
`${formatLabel("App")} ${chalk.green.bold(appName)} ${chalk.gray(`(${currentAppId})`)}`,
Expand Down Expand Up @@ -161,7 +161,9 @@ export default class AppsCurrent extends ControlBaseCommand {
flags,
);
} else {
// Get account info if possible
// Web CLI mode: account info comes from the Control API, not local config.
// The API call may fail (e.g. token lacks account-level scope), so we
// fall back to generic defaults — accountId stays empty, hiding the "(id)" suffix.
let accountName = "Web CLI Account";
let accountId = "";

Expand All @@ -170,7 +172,7 @@ export default class AppsCurrent extends ControlBaseCommand {
accountName = account.name;
accountId = account.id;
} catch {
// If we can't get account details, just use default values
// Control API unavailable — use defaults above
}

this.log(
Expand Down
1 change: 1 addition & 0 deletions src/commands/apps/rules/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export default class RulesListCommand extends ControlBaseCommand {
(rule: Namespace): ChannelRuleOutput => ({
authenticated: rule.authenticated || false,
batchingEnabled: rule.batchingEnabled || false,
// Control API may omit optional fields; emit null for consistent JSON schema
batchingInterval: rule.batchingInterval ?? null,
conflationEnabled: rule.conflationEnabled || false,
conflationInterval: rule.conflationInterval ?? null,
Expand Down
33 changes: 24 additions & 9 deletions src/commands/auth/issue-ably-token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ import { randomUUID } from "node:crypto";

import { AblyBaseCommand } from "../../base-command.js";
import { productApiFlags } from "../../flags.js";
import {
formatClientId,
formatLabel,
formatSuccess,
} from "../../utils/output.js";

export default class IssueAblyTokenCommand extends AblyBaseCommand {
static description = "Create an Ably Token with capabilities";
Expand Down Expand Up @@ -118,22 +123,32 @@ export default class IssueAblyTokenCommand extends AblyBaseCommand {
value: tokenDetails.token,
issuedAt: new Date(tokenDetails.issued).toISOString(),
expiresAt: new Date(tokenDetails.expires).toISOString(),
clientId: tokenDetails.clientId || null,
...(tokenDetails.clientId
? { clientId: tokenDetails.clientId }
: {}),
capability: tokenDetails.capability,
},
},
flags,
);
} else {
this.log("Generated Ably Token:");
this.log(`Token: ${tokenDetails.token}`);
this.log(`Type: Ably`);
this.log(`Issued: ${new Date(tokenDetails.issued).toISOString()}`);
this.log(`Expires: ${new Date(tokenDetails.expires).toISOString()}`);
this.log(`TTL: ${flags.ttl} seconds`);
this.log(`Client ID: ${tokenDetails.clientId || "None"}`);
this.log(formatSuccess("Ably token generated."));
this.log(`${formatLabel("Token")} ${tokenDetails.token}`);
this.log(`${formatLabel("Type")} Ably`);
this.log(
`Capability: ${this.formatJsonOutput({ capability: tokenDetails.capability }, flags)}`,
`${formatLabel("Issued")} ${new Date(tokenDetails.issued).toISOString()}`,
);
this.log(
`${formatLabel("Expires")} ${new Date(tokenDetails.expires).toISOString()}`,
);
this.log(`${formatLabel("TTL")} ${flags.ttl} seconds`);
if (tokenDetails.clientId) {
this.log(
`${formatLabel("Client ID")} ${formatClientId(tokenDetails.clientId)}`,
);
}
this.log(
`${formatLabel("Capability")} ${this.formatJsonOutput({ capability: tokenDetails.capability }, flags)}`,
);
}
} catch (error) {
Expand Down
35 changes: 24 additions & 11 deletions src/commands/auth/issue-jwt-token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ import { randomUUID } from "node:crypto";

import { AblyBaseCommand } from "../../base-command.js";
import { productApiFlags } from "../../flags.js";
import {
formatClientId,
formatLabel,
formatSuccess,
} from "../../utils/output.js";

interface JwtPayload {
exp: number;
Expand Down Expand Up @@ -133,7 +138,7 @@ export default class IssueJwtTokenCommand extends AblyBaseCommand {
token: {
appId,
capability: capabilities,
clientId,
...(clientId ? { clientId } : {}),
expires: new Date(jwtPayload.exp * 1000).toISOString(),
issued: new Date(jwtPayload.iat * 1000).toISOString(),
keyId,
Expand All @@ -145,16 +150,24 @@ export default class IssueJwtTokenCommand extends AblyBaseCommand {
flags,
);
} else {
this.log("Generated Ably JWT Token:");
this.log(`Token: ${token}`);
this.log(`Type: JWT`);
this.log(`Issued: ${new Date(jwtPayload.iat * 1000).toISOString()}`);
this.log(`Expires: ${new Date(jwtPayload.exp * 1000).toISOString()}`);
this.log(`TTL: ${flags.ttl} seconds`);
this.log(`App ID: ${appId}`);
this.log(`Key ID: ${keyId}`);
this.log(`Client ID: ${clientId || "None"}`);
this.log(`Capability: ${this.formatJsonOutput(capabilities, flags)}`);
this.log(formatSuccess("Ably JWT token generated."));
this.log(`${formatLabel("Token")} ${token}`);
this.log(`${formatLabel("Type")} JWT`);
this.log(
`${formatLabel("Issued")} ${new Date(jwtPayload.iat * 1000).toISOString()}`,
);
this.log(
`${formatLabel("Expires")} ${new Date(jwtPayload.exp * 1000).toISOString()}`,
);
this.log(`${formatLabel("TTL")} ${flags.ttl} seconds`);
this.log(`${formatLabel("App ID")} ${appId}`);
this.log(`${formatLabel("Key ID")} ${keyId}`);
if (clientId) {
this.log(`${formatLabel("Client ID")} ${formatClientId(clientId)}`);
}
this.log(
`${formatLabel("Capability")} ${this.formatJsonOutput(capabilities, flags)}`,
);
}
} catch (error) {
this.fail(error, flags, "issueJwtToken");
Expand Down
8 changes: 5 additions & 3 deletions src/commands/auth/keys/current.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export default class KeysCurrentCommand extends ControlBaseCommand {
const currentAccountAlias = this.configManager.getCurrentAccountAlias();

this.log(
`${formatLabel("Account")} ${chalk.cyan.bold(currentAccount?.accountName || currentAccountAlias)} ${chalk.gray(`(${currentAccount?.accountId || "Unknown ID"})`)}`,
`${formatLabel("Account")} ${chalk.cyan.bold(currentAccount?.accountName || currentAccountAlias)}${currentAccount ? chalk.gray(` (${currentAccount.accountId})`) : ""}`,
);
this.log(
`${formatLabel("App")} ${chalk.green.bold(appName)} ${chalk.gray(`(${appId})`)}`,
Expand Down Expand Up @@ -118,7 +118,9 @@ export default class KeysCurrentCommand extends ControlBaseCommand {
flags,
);
} else {
// Get account info if possible
// Web CLI mode: account info comes from the Control API, not local config.
// The API call may fail (e.g. token lacks account-level scope), so we
// fall back to generic defaults — accountId stays empty, hiding the "(id)" suffix.
let accountName = "Web CLI Account";
let accountId = "";

Expand All @@ -128,7 +130,7 @@ export default class KeysCurrentCommand extends ControlBaseCommand {
accountName = account.name;
accountId = account.id;
} catch {
// If we can't get account details, just use default values
// Control API unavailable — use defaults above
}

this.log(
Expand Down
2 changes: 1 addition & 1 deletion src/commands/channels/presence/enter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export default class ChannelsPresenceEnter extends AblyBaseCommand {
? `${formatIndex(this.sequenceCounter)}`
: "";
this.log(
`${formatTimestamp(timestamp)}${sequencePrefix} ${formatResource(`Channel: ${channelName}`)} | Action: ${formatEventType(String(presenceMessage.action))} | Client: ${formatClientId(presenceMessage.clientId || "N/A")}`,
`${formatTimestamp(timestamp)}${sequencePrefix} ${formatResource(`Channel: ${channelName}`)} | Action: ${formatEventType(String(presenceMessage.action))} | Client: ${formatClientId(presenceMessage.clientId)}`,
);

if (
Expand Down
1 change: 1 addition & 0 deletions src/commands/channels/presence/get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export default class ChannelsPresenceGet extends AblyBaseCommand {
clientId: member.clientId,
connectionId: member.connectionId,
action: member.action,
// SDK presence data may be undefined; emit null for consistent JSON schema
data: (member.data as unknown) ?? null,
timestamp: formatMessageTimestamp(member.timestamp),
id: member.id,
Expand Down
2 changes: 1 addition & 1 deletion src/commands/push/channels/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export default class PushChannelsList extends AblyBaseCommand {

for (const sub of subscriptions) {
const type = sub.deviceId ? "device" : "client";
const id = sub.deviceId || sub.clientId || "unknown";
const id = sub.deviceId || sub.clientId;
this.log(formatHeading(`${type}: ${id}`));
this.log(` ${formatLabel("Channel")} ${formatResource(sub.channel)}`);
if (sub.deviceId)
Expand Down
2 changes: 1 addition & 1 deletion src/commands/rooms/messages/history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ export default class MessagesHistory extends ChatBaseCommand {
const messagesInOrder = [...items];
for (const [i, message] of messagesInOrder.entries()) {
const timestamp = formatMessageTimestamp(message.timestamp);
const author = message.clientId || "Unknown";
const author = message.clientId;

this.log(`${formatIndex(i + 1)} ${formatTimestamp(timestamp)}`);
this.log(
Expand Down
2 changes: 1 addition & 1 deletion src/commands/rooms/messages/reactions/subscribe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ export default class MessagesReactionsSubscribe extends ChatBaseCommand {
this.logJsonEvent({ reaction: eventData }, flags);
} else {
this.log(
`${formatTimestamp(timestamp)} ${chalk.green("⚡")} ${formatClientId(event.reaction.clientId || "Unknown")} [${event.reaction.type}] ${event.type}: ${formatEventType(event.reaction.name || "unknown")} to message ${formatResource(event.reaction.messageSerial)}`,
`${formatTimestamp(timestamp)} ${chalk.green("⚡")} ${formatClientId(event.reaction.clientId)} [${event.reaction.type}] ${event.type}: ${formatEventType(event.reaction.name || "unknown")} to message ${formatResource(event.reaction.messageSerial)}`,
);
}
},
Expand Down
2 changes: 1 addition & 1 deletion src/commands/rooms/messages/subscribe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export default class MessagesSubscribe extends ChatBaseCommand {
} else {
// Format message with timestamp, author and content
const timestamp = formatMessageTimestamp(message.timestamp);
const author = message.clientId || "Unknown";
const author = message.clientId;

// Prefix with room name when multiple rooms
const roomPrefix =
Expand Down
2 changes: 1 addition & 1 deletion src/commands/rooms/presence/enter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ export default class RoomsPresenceEnter extends ChatBaseCommand {
? `${formatIndex(this.sequenceCounter)}`
: "";
this.log(
`${formatTimestamp(timestamp)}${sequencePrefix} ${formatResource(`Room: ${this.roomName!}`)} | Action: ${formatEventType(event.type)} | Client: ${formatClientId(member.clientId || "N/A")}`,
`${formatTimestamp(timestamp)}${sequencePrefix} ${formatResource(`Room: ${this.roomName!}`)} | Action: ${formatEventType(event.type)} | Client: ${formatClientId(member.clientId)}`,
);

if (member.data !== null && member.data !== undefined) {
Expand Down
2 changes: 1 addition & 1 deletion src/commands/rooms/reactions/subscribe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ export default class RoomsReactionsSubscribe extends ChatBaseCommand {
);
} else {
this.log(
`${formatTimestamp(timestamp)} ${chalk.green("⚡")} ${formatClientId(reaction.clientId || "Unknown")} reacted with ${chalk.yellow(reaction.name || "unknown")}`,
`${formatTimestamp(timestamp)} ${chalk.green("⚡")} ${formatClientId(reaction.clientId)} reacted with ${chalk.yellow(reaction.name || "unknown")}`,
);

// Show any additional metadata in the reaction
Expand Down
4 changes: 2 additions & 2 deletions src/commands/spaces/members/subscribe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ export default class SpacesMembersSubscribe extends SpacesBaseCommand {

// Determine the action from the member's lastEvent
const action = member.lastEvent.name;
const clientId = member.clientId || "Unknown";
const connectionId = member.connectionId || "Unknown";
const clientId = member.clientId;
const connectionId = member.connectionId;

// Create a unique key for this client+connection combination
const clientKey = `${clientId}:${connectionId}`;
Expand Down
4 changes: 2 additions & 2 deletions src/commands/spaces/subscribe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ export default class SpacesSubscribe extends SpacesBaseCommand {
const now = Date.now();

const action = member.lastEvent.name;
const clientId = member.clientId || "Unknown";
const connectionId = member.connectionId || "Unknown";
const clientId = member.clientId;
const connectionId = member.connectionId;

// Dedup within 500ms window
const clientKey = `${clientId}:${connectionId}`;
Expand Down
Loading
Loading