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
22 changes: 15 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,22 +91,30 @@ indexing-co agent doctor --session <id>

Resolution order for the session id:

1. `--session`
2. `INDEXING_CO_SESSION_ID`
3. `~/.indexing-co/session-id`
1. `--console-session` or legacy `--session`
2. `INDEXING_CO_CONSOLE_SESSION_ID` or legacy `INDEXING_CO_SESSION_ID`
3. Active session file written by `indexing-co agent watch` for the current project directory
4. Legacy `~/.indexing-co/session-id`

Console URL resolution order:

1. `--console-url`
2. `INDEXING_CO_CONSOLE_URL`
3. `https://console.indexing.co`
3. Active session file written by `indexing-co agent watch` for the current project directory
4. `https://console.indexing.co`

`agent watch` maintains Console presence and refreshes an expiring scoped session file under
`~/.indexing-co/console-sessions/`. Later mutating commands run from the same project directory
automatically reuse that session for Console activity reporting and attach `X-Session-Id` to API
requests, giving the API a canonical hook for server-side rail events. If the API mutation succeeds
but Console activity sync fails, the CLI prints a warning while still returning the mutation result.

For staging or local development, pass an explicit override:

```bash
indexing-co agent watch --session <id> --console-url https://staging.console.indexing.co
indexing-co agent doctor --session <id> --console-url https://staging.console.indexing.co --json
INDEXING_CO_CONSOLE_URL=http://localhost:5173 indexing-co agent watch --session <id>
indexing-co agent watch --console-session <id> --console-url https://staging.console.indexing.co
indexing-co agent doctor --console-session <id> --console-url https://staging.console.indexing.co --json
INDEXING_CO_CONSOLE_URL=http://localhost:5173 indexing-co agent watch --console-session <id>
```

Library usage:
Expand Down
88 changes: 74 additions & 14 deletions src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const fs = require("node:fs");
const path = require("node:path");

import { ensureConfigDirectory, promptForApiKey, removeCredentialsFile, writeCredentialsFile } from "../lib/auth";
import { readActiveConsoleSession } from "../lib/console-session";
import { DEFAULT_BASE_URL, DEFAULT_CONSOLE_URL } from "../lib/constants";
import {
getAgentPairingHealth,
Expand All @@ -10,6 +11,7 @@ import {
requestAccountKeyHandoff,
resolveAgentSource,
resolveConsoleSessionId,
resolveOptionalActivitySessionId,
subscribeConsoleState,
type AgentPairingHealth,
type AgentActivityEventInput,
Expand Down Expand Up @@ -148,15 +150,28 @@ async function recordAgentActivity(
context: CommandContext,
event: AgentActivityEventInput,
): Promise<void> {
await reportAgentActivity({
const explicitSessionId = context.options.consoleSession || context.options.session
? String(context.options.consoleSession || context.options.session)
: undefined;
const resolvedSessionId = resolveOptionalActivitySessionId(explicitSessionId, context.env, context.cwd);
const reported = await reportAgentActivity({
...event,
sessionId: context.options.session ? String(context.options.session) : undefined,
sessionId: explicitSessionId,
consoleUrl: context.options.consoleUrl ? String(context.options.consoleUrl) : context.env.INDEXING_CO_CONSOLE_URL,
source: context.options.source ? String(context.options.source) : undefined,
apiKey: context.config.apiKey,
cwd: context.cwd,
env: context.env,
fetchImpl: context.fetchImpl,
});
if (resolvedSessionId && !reported) {
context.stderr.write("Warning: Console activity sync failed; the API mutation already succeeded.\n");
}
}

function resolveConsoleUrl(context: CommandContext): string | undefined {
const activeSession = readActiveConsoleSession({ cwd: context.cwd, env: context.env });
return (context.options.consoleUrl as string | undefined) || context.env.INDEXING_CO_CONSOLE_URL || activeSession?.consoleUrl;
}

async function resolvePipelineTable(context: CommandContext, pipelineName: string): Promise<string> {
Expand Down Expand Up @@ -506,6 +521,18 @@ export function createRootCommand(): CommandDefinition {
}

const response = await context.http.post(`/pipelines/${encodeURIComponent(context.args[0])}/backfill`, body);
await recordAgentActivity(context, {
type: "update_pipeline",
target: { id: context.args[0], name: context.args[0], type: "pipeline" },
metadata: compactObject({
action: "backfill",
network: body.network,
value: body.value,
beatStart: body.beatStart,
beatEnd: body.beatEnd,
beatCount: Array.isArray(body.beats) ? body.beats.length : undefined,
}),
});
return renderRecord(`Backfill started for ${context.args[0]}`, response.data as Record<string, unknown>);
},
},
Expand Down Expand Up @@ -1101,13 +1128,18 @@ order by ordinal_position
summary: "Request this console account's API key via in-console approval.",
requiresAuth: false,
options: [
{ name: "console-session", description: "Explicit console session id.", type: "string" },
{ name: "session", description: "Explicit console session id.", type: "string" },
{ name: "console-url", description: "Override the console base URL.", type: "string" },
{ name: "source", description: "Agent name shown in the approval prompt.", type: "string" },
],
execute: async (context) => {
const sessionId = resolveConsoleSessionId(context.options.session as string | undefined, context.env);
const consoleUrl = (context.options.consoleUrl as string | undefined) || context.env.INDEXING_CO_CONSOLE_URL;
const sessionId = resolveConsoleSessionId(
(context.options.consoleSession || context.options.session) as string | undefined,
context.env,
context.cwd,
);
const consoleUrl = resolveConsoleUrl(context);
const source = resolveAgentSource(context.options.source as string | undefined, context.env);

context.stderr.write(
Expand Down Expand Up @@ -1191,15 +1223,20 @@ order by ordinal_position
summary: "Subscribe to the console state stream.",
requiresAuth: false,
options: [
{ name: "console-session", description: "Explicit console session id.", type: "string" },
{ name: "session", description: "Explicit console session id.", type: "string" },
{ name: "console-url", description: "Override the console base URL.", type: "string" },
{ name: "source", description: "Agent source shown in the console presence indicator.", type: "string" },
{ name: "verbose", description: "Print full state snapshot payloads.", type: "boolean" },
{ name: "once", description: "Print the next event and exit.", type: "boolean" },
],
execute: async (context) => {
const sessionId = resolveConsoleSessionId(context.options.session as string | undefined, context.env);
const consoleUrl = (context.options.consoleUrl as string | undefined) || context.env.INDEXING_CO_CONSOLE_URL;
const sessionId = resolveConsoleSessionId(
(context.options.consoleSession || context.options.session) as string | undefined,
context.env,
context.cwd,
);
const consoleUrl = resolveConsoleUrl(context);
const source = resolveAgentSource(context.options.source as string | undefined, context.env);
const verbose = Boolean(context.options.verbose);
const once = Boolean(context.options.once);
Expand All @@ -1215,6 +1252,9 @@ order by ordinal_position
consoleUrl,
source,
apiKey: context.config.apiKey,
cwd: context.cwd,
env: context.env,
cliVersion: context.config.packageVersion,
fetchImpl: context.fetchImpl,
onEvent: (event) => {
if (context.format === "json") {
Expand Down Expand Up @@ -1254,12 +1294,17 @@ order by ordinal_position
summary: "Fetch the current console state snapshot.",
requiresAuth: false,
options: [
{ name: "console-session", description: "Explicit console session id.", type: "string" },
{ name: "session", description: "Explicit console session id.", type: "string" },
{ name: "console-url", description: "Override the console base URL.", type: "string" },
],
execute: async (context) => {
const sessionId = resolveConsoleSessionId(context.options.session as string | undefined, context.env);
const consoleUrl = (context.options.consoleUrl as string | undefined) || context.env.INDEXING_CO_CONSOLE_URL;
const sessionId = resolveConsoleSessionId(
(context.options.consoleSession || context.options.session) as string | undefined,
context.env,
context.cwd,
);
const consoleUrl = resolveConsoleUrl(context);
const snapshot = await getCurrentUserState({ sessionId, consoleUrl, fetchImpl: context.fetchImpl });
return {
data: snapshot,
Expand All @@ -1274,12 +1319,17 @@ order by ordinal_position
summary: "Check whether Console pairing is healthy.",
requiresAuth: false,
options: [
{ name: "console-session", description: "Explicit console session id.", type: "string" },
{ name: "session", description: "Explicit console session id.", type: "string" },
{ name: "console-url", description: "Override the console base URL.", type: "string" },
],
execute: async (context) => {
const sessionId = resolveConsoleSessionId(context.options.session as string | undefined, context.env);
const consoleUrl = (context.options.consoleUrl as string | undefined) || context.env.INDEXING_CO_CONSOLE_URL;
const sessionId = resolveConsoleSessionId(
(context.options.consoleSession || context.options.session) as string | undefined,
context.env,
context.cwd,
);
const consoleUrl = resolveConsoleUrl(context);
const health = await getAgentPairingHealth({ sessionId, consoleUrl, fetchImpl: context.fetchImpl });

return {
Expand Down Expand Up @@ -1311,17 +1361,24 @@ order by ordinal_position
{ name: "target-kind", description: "Target kind (pipeline | filter | transformation).", type: "string" },
{ name: "target-id", description: "Target identifier within the chosen kind.", type: "string" },
{ name: "field", description: "Specific field on the target.", type: "string" },
{ name: "console-session", description: "Explicit console session id.", type: "string" },
{ name: "note", description: "Short human note describing what changed (shown in the BYO Agent feed).", type: "string" },
{ name: "ttl-ms", description: "How long the hint should remain visible (ms).", type: "number" },
{ name: "agent", description: "Agent name (e.g. claude-code, codex-cli).", type: "string" },
{ name: "url", description: "Override the hints endpoint URL.", type: "string" },
],
requiresAuth: false,
execute: async (context: CommandContext) => {
const sessionId = resolveOptionalActivitySessionId(
(context.options.consoleSession || context.options.session) as string | undefined,
context.env,
context.cwd,
);
const consoleUrl = resolveConsoleUrl(context) || DEFAULT_CONSOLE_URL;
const url =
(context.options.url as string | undefined) ||
process.env.INDEXING_CO_HINTS_URL ||
`${DEFAULT_CONSOLE_URL}/api/hints/emit`;
context.env.INDEXING_CO_HINTS_URL ||
`${consoleUrl.replace(/\/$/, "")}/api/hints/emit`;
const body: Record<string, unknown> = {
type: context.args[0],
ts: new Date().toISOString(),
Expand All @@ -1343,9 +1400,12 @@ order by ordinal_position
const note = context.options.note as string | undefined;
if (note) body.note = note;

const response = await fetch(url, {
const response = await context.fetchImpl(url, {
method: "POST",
headers: { "content-type": "application/json" },
headers: compactObject({
"content-type": "application/json",
"X-Session-Id": sessionId,
}) as Record<string, string>,
body: JSON.stringify(body),
});
const text = await response.text();
Expand Down
13 changes: 12 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
export {
getAgentPairingHealth,
getCurrentUserState,
readStoredSessionId,
reportAgentActivity,
resolveConsoleSessionId,
resolveOptionalActivitySessionId,
subscribeConsoleState,
} from "./lib/console-state";
export {
getActiveConsoleSessionPath,
getConsoleSessionScope,
readActiveConsoleSession,
readStoredSessionId,
resolveOptionalConsoleSessionContext,
writeActiveConsoleSession,
} from "./lib/console-session";
export type {
AgentActivityEventInput,
AgentActivityReportOptions,
Expand All @@ -18,3 +25,7 @@ export type {
ConsoleStateSubscription,
ConsoleStateSubscriptionOptions,
} from "./lib/console-state";
export type {
ActiveConsoleSession,
ConsoleSessionContext,
} from "./lib/console-session";
Loading
Loading