ai-obs-cli 0.0.0-stage → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +666 -2
- package/bin/ai-obs.js +8 -0
- package/dist/adapters/claude-code/analyze.js +189 -0
- package/dist/adapters/claude-code/analyze.js.map +1 -0
- package/dist/adapters/claude-code/detect.js +60 -0
- package/dist/adapters/claude-code/detect.js.map +1 -0
- package/dist/adapters/claude-code/handlers.js +183 -0
- package/dist/adapters/claude-code/handlers.js.map +1 -0
- package/dist/adapters/claude-code/index.js +55 -0
- package/dist/adapters/claude-code/index.js.map +1 -0
- package/dist/adapters/claude-code/run-links.js +53 -0
- package/dist/adapters/claude-code/run-links.js.map +1 -0
- package/dist/adapters/claude-code/session.js +427 -0
- package/dist/adapters/claude-code/session.js.map +1 -0
- package/dist/adapters/claude-code/settings.js +161 -0
- package/dist/adapters/claude-code/settings.js.map +1 -0
- package/dist/adapters/claude-code/state.js +66 -0
- package/dist/adapters/claude-code/state.js.map +1 -0
- package/dist/adapters/claude-code/subagents.js +246 -0
- package/dist/adapters/claude-code/subagents.js.map +1 -0
- package/dist/adapters/claude-code/transcript.js +309 -0
- package/dist/adapters/claude-code/transcript.js.map +1 -0
- package/dist/adapters/codex/accumulator.js +135 -0
- package/dist/adapters/codex/accumulator.js.map +1 -0
- package/dist/adapters/codex/detect.js +93 -0
- package/dist/adapters/codex/detect.js.map +1 -0
- package/dist/adapters/codex/hooks-config.js +158 -0
- package/dist/adapters/codex/hooks-config.js.map +1 -0
- package/dist/adapters/codex/index.js +270 -0
- package/dist/adapters/codex/index.js.map +1 -0
- package/dist/adapters/codex/locate.js +72 -0
- package/dist/adapters/codex/locate.js.map +1 -0
- package/dist/adapters/codex/records.js +194 -0
- package/dist/adapters/codex/records.js.map +1 -0
- package/dist/adapters/codex/rollout.js +72 -0
- package/dist/adapters/codex/rollout.js.map +1 -0
- package/dist/adapters/codex/session.js +212 -0
- package/dist/adapters/codex/session.js.map +1 -0
- package/dist/adapters/codex/subagents.js +139 -0
- package/dist/adapters/codex/subagents.js.map +1 -0
- package/dist/adapters/codex/summarize.js +84 -0
- package/dist/adapters/codex/summarize.js.map +1 -0
- package/dist/adapters/codex/tools.js +145 -0
- package/dist/adapters/codex/tools.js.map +1 -0
- package/dist/adapters/hook-command.js +94 -0
- package/dist/adapters/hook-command.js.map +1 -0
- package/dist/adapters/index.js +12 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/types.js +2 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/analysis/corrections.js +79 -0
- package/dist/analysis/corrections.js.map +1 -0
- package/dist/analysis/digest.js +16 -0
- package/dist/analysis/digest.js.map +1 -0
- package/dist/analysis/outcome.js +76 -0
- package/dist/analysis/outcome.js.map +1 -0
- package/dist/analysis/phases.js +203 -0
- package/dist/analysis/phases.js.map +1 -0
- package/dist/analysis/summaries.js +350 -0
- package/dist/analysis/summaries.js.map +1 -0
- package/dist/analysis/tokens.js +67 -0
- package/dist/analysis/tokens.js.map +1 -0
- package/dist/analysis/usage.js +229 -0
- package/dist/analysis/usage.js.map +1 -0
- package/dist/cc-cache.js +81 -0
- package/dist/cc-cache.js.map +1 -0
- package/dist/cli.js +128 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/common.js +235 -0
- package/dist/commands/common.js.map +1 -0
- package/dist/commands/doctor.js +193 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/flush.js +131 -0
- package/dist/commands/flush.js.map +1 -0
- package/dist/commands/init.js +119 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/link.js +171 -0
- package/dist/commands/link.js.map +1 -0
- package/dist/commands/live.js +130 -0
- package/dist/commands/live.js.map +1 -0
- package/dist/commands/login.js +397 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/status.js +155 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/token.js +92 -0
- package/dist/commands/token.js.map +1 -0
- package/dist/commands/uninstall.js +56 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/config.js +305 -0
- package/dist/config.js.map +1 -0
- package/dist/context.js +109 -0
- package/dist/context.js.map +1 -0
- package/dist/delivery.js +151 -0
- package/dist/delivery.js.map +1 -0
- package/dist/dotfile.js +187 -0
- package/dist/dotfile.js.map +1 -0
- package/dist/events.js +259 -0
- package/dist/events.js.map +1 -0
- package/dist/fsutil.js +50 -0
- package/dist/fsutil.js.map +1 -0
- package/dist/git.js +139 -0
- package/dist/git.js.map +1 -0
- package/dist/hook.js +147 -0
- package/dist/hook.js.map +1 -0
- package/dist/http.js +35 -0
- package/dist/http.js.map +1 -0
- package/dist/jobs.js +53 -0
- package/dist/jobs.js.map +1 -0
- package/dist/live/claude-code.js +226 -0
- package/dist/live/claude-code.js.map +1 -0
- package/dist/live/codex.js +91 -0
- package/dist/live/codex.js.map +1 -0
- package/dist/live/monitor.js +229 -0
- package/dist/live/monitor.js.map +1 -0
- package/dist/live/render.js +92 -0
- package/dist/live/render.js.map +1 -0
- package/dist/lock.js +93 -0
- package/dist/lock.js.map +1 -0
- package/dist/log.js +36 -0
- package/dist/log.js.map +1 -0
- package/dist/migrate.js +109 -0
- package/dist/migrate.js.map +1 -0
- package/dist/nudge.js +231 -0
- package/dist/nudge.js.map +1 -0
- package/dist/paths.js +73 -0
- package/dist/paths.js.map +1 -0
- package/dist/pk-cache.js +110 -0
- package/dist/pk-cache.js.map +1 -0
- package/dist/prices.js +45 -0
- package/dist/prices.js.map +1 -0
- package/dist/project-key.js +144 -0
- package/dist/project-key.js.map +1 -0
- package/dist/protocol-types.js +30 -0
- package/dist/protocol-types.js.map +1 -0
- package/dist/queue.js +649 -0
- package/dist/queue.js.map +1 -0
- package/dist/routing.js +122 -0
- package/dist/routing.js.map +1 -0
- package/dist/runtime.js +54 -0
- package/dist/runtime.js.map +1 -0
- package/dist/sender.js +559 -0
- package/dist/sender.js.map +1 -0
- package/dist/state.js +59 -0
- package/dist/state.js.map +1 -0
- package/dist/ulid.js +73 -0
- package/dist/ulid.js.map +1 -0
- package/dist/version.js +26 -0
- package/dist/version.js.map +1 -0
- package/dist/warnings.js +71 -0
- package/dist/warnings.js.map +1 -0
- package/dist/worker-main.js +14 -0
- package/dist/worker-main.js.map +1 -0
- package/dist/worker.js +295 -0
- package/dist/worker.js.map +1 -0
- package/package.json +25 -4
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { ADAPTERS } from '../adapters/index.js';
|
|
2
|
+
import { hookCommandFactory, isVersionManagedNode } from '../adapters/hook-command.js';
|
|
3
|
+
import { loadConfig, normalizeEndpoint } from '../config.js';
|
|
4
|
+
import { errorMessage } from '../fsutil.js';
|
|
5
|
+
import { gitTopLevel } from '../git.js';
|
|
6
|
+
import { createLogger } from '../log.js';
|
|
7
|
+
import { migrateHome } from '../migrate.js';
|
|
8
|
+
import { installRuntime, isDefaultHome } from '../runtime.js';
|
|
9
|
+
import { nowOf, parseOptions, UsageError, withWriteLock } from './common.js';
|
|
10
|
+
import { checkTokenShape, registerServer, serverOf } from './login.js';
|
|
11
|
+
export const INIT_OPTIONS = {
|
|
12
|
+
token: { type: 'string' },
|
|
13
|
+
endpoint: { type: 'string' },
|
|
14
|
+
label: { type: 'string' },
|
|
15
|
+
project: { type: 'boolean', default: false },
|
|
16
|
+
'project-dir': { type: 'string' },
|
|
17
|
+
harness: { type: 'string' },
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* `ai-obs init [--token <t> --endpoint <url> [--label <name>]] [--project] [--project-dir d] [--harness a,b]`:
|
|
21
|
+
* machine setup: copies the hook runtime and installs the hooks of every detected harness. With --token it is
|
|
22
|
+
* first the non-interactive form of `ai-obs login --server <url> --token <t>`.
|
|
23
|
+
*/
|
|
24
|
+
export async function initCommand(args, io, deps) {
|
|
25
|
+
const { values } = parseOptions(args, INIT_OPTIONS);
|
|
26
|
+
const only = values.harness?.split(',').map((s) => s.trim()).filter(Boolean);
|
|
27
|
+
const adapters = (deps.adapters ?? ADAPTERS).filter((a) => !only || only.includes(a.name));
|
|
28
|
+
if (only && !adapters.length)
|
|
29
|
+
throw new UsageError(`unknown harness: ${values.harness}`);
|
|
30
|
+
if (!values.token && (values.endpoint !== undefined || values.label !== undefined)) {
|
|
31
|
+
throw new UsageError('--endpoint and --label need --token (or run `ai-obs login --server <url>` for the browser login)');
|
|
32
|
+
}
|
|
33
|
+
const cwd = deps.cwd ?? process.cwd();
|
|
34
|
+
const scope = values.project ? 'project' : 'user';
|
|
35
|
+
const projectDir = values['project-dir'] ?? (values.project ? ((await gitTopLevel(cwd)) ?? cwd) : undefined);
|
|
36
|
+
// 1. Token (non-interactive login), then a fresh runtime for the hooks (C25, under the lock).
|
|
37
|
+
if (values.token) {
|
|
38
|
+
const { endpoint: raw } = serverOf(values.endpoint, deps);
|
|
39
|
+
let endpoint;
|
|
40
|
+
try {
|
|
41
|
+
endpoint = normalizeEndpoint(raw);
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
io.err(`ai-obs: invalid endpoint: ${errorMessage(err)}`);
|
|
45
|
+
return 2;
|
|
46
|
+
}
|
|
47
|
+
if (!checkTokenShape(values.token, io))
|
|
48
|
+
return 2;
|
|
49
|
+
const code = await registerServer({ endpoint, token: values.token, source: 'manual', ...(values.label ? { label: values.label } : {}) }, io, deps, { fromInit: true });
|
|
50
|
+
if (code !== 0)
|
|
51
|
+
return code;
|
|
52
|
+
}
|
|
53
|
+
let runtime = null;
|
|
54
|
+
const notes = [];
|
|
55
|
+
const locked = await withWriteLock(deps, io, () => {
|
|
56
|
+
const refresh = deps.installRuntime ?? installRuntime;
|
|
57
|
+
runtime = refresh(deps.paths); // first: the hooks' runtime must read the config the migration writes
|
|
58
|
+
notes.push(...migrateHome(deps.paths, { log: createLogger(deps.paths, 'cli'), now: new Date(nowOf(deps)), refreshRuntime: refresh }));
|
|
59
|
+
});
|
|
60
|
+
if (!locked)
|
|
61
|
+
return 1;
|
|
62
|
+
for (const n of notes)
|
|
63
|
+
io.err(`ai-obs: ${n}`);
|
|
64
|
+
const rt = runtime;
|
|
65
|
+
if (!rt)
|
|
66
|
+
throw new Error('the hook runtime was not installed');
|
|
67
|
+
const home = isDefaultHome(deps.paths) ? undefined : deps.paths.root;
|
|
68
|
+
const config = loadConfig(deps.paths);
|
|
69
|
+
if (!values.token) {
|
|
70
|
+
if (config?.servers.length)
|
|
71
|
+
io.out(`using the registered servers: ${config.servers.map((s) => s.endpoint).join(', ')}`);
|
|
72
|
+
else
|
|
73
|
+
io.out('! not logged in to any ai-obs server yet: nothing is recorded until you run `ai-obs login --server <url>`');
|
|
74
|
+
}
|
|
75
|
+
// 2. Harness hooks (re-installed every time, so changes such as the SessionStart timeout reach old installs).
|
|
76
|
+
let installed = 0;
|
|
77
|
+
const nextSteps = [];
|
|
78
|
+
for (const adapter of adapters) {
|
|
79
|
+
const det = await adapter.detect(projectDir ? { projectDir } : {});
|
|
80
|
+
if (!det.installed) {
|
|
81
|
+
io.out(`– ${adapter.displayName}: not found`);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
await adapter.installHooks({
|
|
86
|
+
scope,
|
|
87
|
+
...(projectDir ? { projectDir } : {}),
|
|
88
|
+
hookCommand: hookCommandFactory({ node: rt.node, entry: rt.entry, harness: adapter.name, ...(home ? { home } : {}) }),
|
|
89
|
+
});
|
|
90
|
+
installed++;
|
|
91
|
+
const ver = det.version ? ` ${det.version}` : '';
|
|
92
|
+
const warn = det.versionSupported === false ? ' (version not tested — run `ai-obs doctor`)' : '';
|
|
93
|
+
io.out(`✓ ${adapter.displayName}${ver}: hooks installed (${scope === 'project' ? `project ${projectDir}` : 'user'})${warn}`);
|
|
94
|
+
nextSteps.push(...(adapter.nextSteps ?? []).map((s) => `${adapter.displayName}: ${s}`));
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
io.err(`✗ ${adapter.displayName}: installing hooks failed: ${errorMessage(err)}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (!installed)
|
|
101
|
+
io.out('! no supported harness found (Claude Code, Codex); install one and re-run `ai-obs init`');
|
|
102
|
+
if (installed) {
|
|
103
|
+
// Hooks bake in the absolute path of the node that ran init.
|
|
104
|
+
if (isVersionManagedNode(rt.node)) {
|
|
105
|
+
io.out(`! hooks run ${rt.node}, a version-managed Node (nvm, fnm, Volta, asdf, Homebrew Cellar …): if you remove or upgrade that version the hooks stop working. \`ai-obs doctor\` detects it; run \`ai-obs init\` again then, or init under a stable Node.`);
|
|
106
|
+
}
|
|
107
|
+
if (scope === 'project') {
|
|
108
|
+
io.out(`! project hooks contain absolute paths of this machine (${rt.node}, ${rt.entry}). They do not work for teammates or on CI: keep the file out of git (e.g. in .git/info/exclude) or let each teammate run \`ai-obs init --project\`.`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
for (const step of nextSteps)
|
|
112
|
+
io.out(`→ ${step}`);
|
|
113
|
+
if (installed && config?.servers.length)
|
|
114
|
+
io.out('ai-obs is set up. Usage appears in the dashboard within minutes; check with `ai-obs doctor`.');
|
|
115
|
+
else if (installed)
|
|
116
|
+
io.out('hooks installed. Run `ai-obs login --server <url>` to start recording.');
|
|
117
|
+
return 0;
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { appendFileSync, existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
|
+
import { findServer, loadConfig, tryNormalizeEndpoint } from '../config.js';
|
|
4
|
+
import { DOT_FILE, LOCAL_DOT_FILE, lookupDotFile, parseDotFile } from '../dotfile.js';
|
|
5
|
+
import { errorMessage, writeFileAtomic } from '../fsutil.js';
|
|
6
|
+
import { gitTopLevel } from '../git.js';
|
|
7
|
+
import { defaultSpawnWorker } from '../hook.js';
|
|
8
|
+
import { resolveProjectKey, RESOLVE_TIMEOUT_MS } from '../project-key.js';
|
|
9
|
+
import { TEAM_SLUG_RE } from '../protocol-types.js';
|
|
10
|
+
import { getCollectorConfig, syncServerEntry } from '../sender.js';
|
|
11
|
+
import { openStore, parseOptions, terminalSafe, UsageError, withWriteLock } from './common.js';
|
|
12
|
+
export const LINK_OPTIONS = {
|
|
13
|
+
team: { type: 'string' },
|
|
14
|
+
server: { type: 'string' },
|
|
15
|
+
local: { type: 'boolean', default: false },
|
|
16
|
+
'project-dir': { type: 'string' },
|
|
17
|
+
};
|
|
18
|
+
const looksLikeToken = (v) => /aiobs_[a-z]_/i.test(v);
|
|
19
|
+
const isObject = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
20
|
+
/** Adds `name` to `<dir>/.gitignore` unless a line already ignores it. Returns true when it appended. */
|
|
21
|
+
export function ensureGitignored(dir, name) {
|
|
22
|
+
const file = join(dir, '.gitignore');
|
|
23
|
+
let text = '';
|
|
24
|
+
try {
|
|
25
|
+
text = readFileSync(file, 'utf8');
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
text = '';
|
|
29
|
+
}
|
|
30
|
+
const lines = text.split(/\r?\n/).map((l) => l.trim());
|
|
31
|
+
if (lines.includes(name) || lines.includes(`/${name}`))
|
|
32
|
+
return false;
|
|
33
|
+
appendFileSync(file, `${text && !text.endsWith('\n') ? '\n' : ''}${name}\n`, { mode: 0o644 });
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* `ai-obs link --team <slug> [--server URL] [--local] [--project-dir <dir>]`: writes the repository's dot file
|
|
38
|
+
* at its git top level (`.ai-obs.json`, to commit; with --local `.ai-obs.local.json`, git-ignored), naming
|
|
39
|
+
* the server and team its sessions report to. The team is checked against the server's collector-config when
|
|
40
|
+
* this machine is logged in to that server. Never writes a token. Events of this repository recorded while no
|
|
41
|
+
* server was chosen move to the linked server.
|
|
42
|
+
*/
|
|
43
|
+
export async function linkCommand(args, io, deps) {
|
|
44
|
+
const { values } = parseOptions(args, LINK_OPTIONS);
|
|
45
|
+
const team = values.team;
|
|
46
|
+
if (!team)
|
|
47
|
+
throw new UsageError('--team is required (the team slug, see `ai-obs status` or the dashboard)');
|
|
48
|
+
if (looksLikeToken(team) || (values.server && looksLikeToken(values.server))) {
|
|
49
|
+
io.err('✗ that looks like a token: tokens never go into a dot file (they stay in ~/.ai-obs/config.json). Nothing was written.');
|
|
50
|
+
return 2;
|
|
51
|
+
}
|
|
52
|
+
if (!TEAM_SLUG_RE.test(team))
|
|
53
|
+
throw new UsageError('--team must be a team slug: 1–63 characters of a-z, 0-9 and -');
|
|
54
|
+
const cwd = deps.cwd ?? process.cwd();
|
|
55
|
+
// the dot file lives at the git top level, also when --project-dir names a subdirectory (the nudge passes the session's cwd)
|
|
56
|
+
const given = values['project-dir'] ? resolve(values['project-dir']) : null;
|
|
57
|
+
const dir = given ? ((await gitTopLevel(given)) ?? given) : await gitTopLevel(cwd);
|
|
58
|
+
if (!dir) {
|
|
59
|
+
io.err('✗ not inside a git repository: run it in the repository, or pass --project-dir <its root>. Nothing was written.');
|
|
60
|
+
return 1;
|
|
61
|
+
}
|
|
62
|
+
// the server: --server, else the one the repository's dot file names, else the only registered one
|
|
63
|
+
const config = loadConfig(deps.paths);
|
|
64
|
+
const current = lookupDotFile(dir);
|
|
65
|
+
let server;
|
|
66
|
+
if (values.server) {
|
|
67
|
+
server = tryNormalizeEndpoint(values.server);
|
|
68
|
+
if (!server)
|
|
69
|
+
throw new UsageError(`--server must be an http(s) URL: ${values.server}`);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
server = current.server ?? (config?.servers.length === 1 ? config.servers[0].endpoint : null);
|
|
73
|
+
if (!server) {
|
|
74
|
+
throw new UsageError(config?.servers.length
|
|
75
|
+
? `--server is required: ${config.servers.length} servers are registered (${config.servers.map((s) => s.endpoint).join(', ')})`
|
|
76
|
+
: '--server is required (no server is registered yet: run `ai-obs login --server <url>` first)');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// check the team on that server
|
|
80
|
+
const entry = config ? findServer(config, server) : undefined;
|
|
81
|
+
if (entry) {
|
|
82
|
+
const cc = await getCollectorConfig({ server: entry, paths: deps.paths, ...(deps.transport ? { transport: deps.transport } : {}), ...(deps.now ? { now: deps.now } : {}) }, { force: true });
|
|
83
|
+
if (cc.source === 'fresh')
|
|
84
|
+
syncServerEntry(deps.paths, entry, cc.value);
|
|
85
|
+
const teams = cc.source !== 'default' ? cc.value.teams : undefined;
|
|
86
|
+
if (cc.status === 401 || cc.status === 403) {
|
|
87
|
+
io.out(`! ${server} rejected this machine's token (expired or revoked): the team is not checked. Run \`ai-obs login --server ${server}\``);
|
|
88
|
+
}
|
|
89
|
+
else if (!teams) {
|
|
90
|
+
io.out(`! could not read your teams from ${server} (${cc.error ?? 'older server'}): the team is not checked`);
|
|
91
|
+
}
|
|
92
|
+
else if (!teams.some((t) => t.slug === team)) {
|
|
93
|
+
const list = teams.map((t) => t.slug).join(', ');
|
|
94
|
+
io.err(`✗ team "${team}" is not one of your teams on ${server}${list ? ` (${terminalSafe(list)})` : ' (you are in no team)'}. Nothing was written.`);
|
|
95
|
+
return 1;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
const name = teams.find((t) => t.slug === team).name;
|
|
99
|
+
io.out(`✓ team ${team} (${terminalSafe(name)}) on ${server}${cc.source === 'cache' ? ' (from the cached server answer)' : ''}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
io.out(`! this machine is not logged in to ${server}: the team is not checked, and data waits on this machine until you run \`ai-obs login --server ${server}\``);
|
|
104
|
+
}
|
|
105
|
+
// write the file, keeping unknown keys of an existing one; never a token
|
|
106
|
+
const file = join(dir, values.local ? LOCAL_DOT_FILE : DOT_FILE);
|
|
107
|
+
let obj = {};
|
|
108
|
+
if (existsSync(file)) {
|
|
109
|
+
try {
|
|
110
|
+
const raw = JSON.parse(readFileSync(file, 'utf8'));
|
|
111
|
+
if (isObject(raw))
|
|
112
|
+
obj = raw;
|
|
113
|
+
else
|
|
114
|
+
io.out(`! ${file} was not a JSON object: replaced`);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
io.out(`! ${file} was not valid JSON: replaced`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
121
|
+
if (k === 'token' || (typeof v === 'string' && looksLikeToken(v))) {
|
|
122
|
+
delete obj[k];
|
|
123
|
+
io.out(`! removed "${k}" from ${file}: tokens never go into a dot file`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
obj = { ...obj, server, team };
|
|
127
|
+
const text = `${JSON.stringify(obj, null, 2)}\n`;
|
|
128
|
+
if (!parseDotFile(text).ok || looksLikeToken(text)) {
|
|
129
|
+
io.err(`✗ refusing to write ${file}. Nothing was written.`);
|
|
130
|
+
return 1;
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
writeFileAtomic(file, text, 0o644);
|
|
134
|
+
}
|
|
135
|
+
catch (err) {
|
|
136
|
+
io.err(`✗ cannot write ${file}: ${errorMessage(err)}`);
|
|
137
|
+
return 1;
|
|
138
|
+
}
|
|
139
|
+
io.out(`✓ wrote ${file}: server ${server}, team ${team}`);
|
|
140
|
+
if (values.local) {
|
|
141
|
+
try {
|
|
142
|
+
if (ensureGitignored(dir, LOCAL_DOT_FILE))
|
|
143
|
+
io.out(`✓ added ${LOCAL_DOT_FILE} to ${join(dir, '.gitignore')}`);
|
|
144
|
+
}
|
|
145
|
+
catch (err) {
|
|
146
|
+
io.out(`! could not add ${LOCAL_DOT_FILE} to .gitignore (${errorMessage(err)}): keep it out of git yourself`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
io.out(` commit ${DOT_FILE} so everyone's sessions in this repository report to this team`);
|
|
151
|
+
}
|
|
152
|
+
const after = lookupDotFile(dir);
|
|
153
|
+
if (after.root && (after.server !== server || after.team !== team)) {
|
|
154
|
+
io.out(`! ${LOCAL_DOT_FILE} overrides it here: sessions report to ${after.server ?? '(no server)'}, team ${after.team ?? '(none)'}`);
|
|
155
|
+
}
|
|
156
|
+
io.out(' sessions that are already running keep the server and team they started with');
|
|
157
|
+
// events recorded while no server was chosen follow the link
|
|
158
|
+
if (!config?.servers.length || !after.server)
|
|
159
|
+
return 0;
|
|
160
|
+
const pk = await (deps.resolveProjectKey ?? ((c, o) => resolveProjectKey(c, o)))(dir, { timeoutMs: RESOLVE_TIMEOUT_MS });
|
|
161
|
+
if (pk === null)
|
|
162
|
+
return 0;
|
|
163
|
+
const target = { endpoint: after.server, team: after.team ?? '' };
|
|
164
|
+
const locked = await withWriteLock(deps, io, () => openStore(deps.paths).retarget(pk, target));
|
|
165
|
+
if (locked?.value) {
|
|
166
|
+
io.out(`✓ ${locked.value} queued events of this repository recorded without a server now go to ${after.server}`);
|
|
167
|
+
(deps.spawnWorker ?? defaultSpawnWorker)(deps.paths);
|
|
168
|
+
}
|
|
169
|
+
return 0;
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=link.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link.js","sourceRoot":"","sources":["../../src/commands/link.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { claudeDir } from '../adapters/claude-code/settings.js';
|
|
3
|
+
import { gitTopLevel } from '../git.js';
|
|
4
|
+
import { inScope, LiveMonitor } from '../live/monitor.js';
|
|
5
|
+
import { renderFrame } from '../live/render.js';
|
|
6
|
+
import { nowOf, parseOptions } from './common.js';
|
|
7
|
+
export const LIVE_REFRESH_MS = 1000;
|
|
8
|
+
const ENTER = '\x1b[?1049h\x1b[?25l';
|
|
9
|
+
const LEAVE = '\x1b[?25h\x1b[?1049l';
|
|
10
|
+
/** Output that is not a terminal (a pipe, a script) is not cut to 80 columns. */
|
|
11
|
+
const PIPE_WIDTH = 200;
|
|
12
|
+
function stdTerminal() {
|
|
13
|
+
const out = process.stdout;
|
|
14
|
+
const inp = process.stdin;
|
|
15
|
+
return {
|
|
16
|
+
isTTY: Boolean(out.isTTY && inp.isTTY),
|
|
17
|
+
columns: () => out.columns || 80,
|
|
18
|
+
rows: () => out.rows || 24,
|
|
19
|
+
write: (s) => void out.write(s),
|
|
20
|
+
onKey(fn) {
|
|
21
|
+
const onData = (d) => fn(String(d));
|
|
22
|
+
try {
|
|
23
|
+
inp.setRawMode?.(true);
|
|
24
|
+
inp.setEncoding('utf8');
|
|
25
|
+
inp.resume();
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
try {
|
|
29
|
+
inp.setRawMode?.(false);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
/* not a usable terminal */
|
|
33
|
+
}
|
|
34
|
+
throw err;
|
|
35
|
+
}
|
|
36
|
+
inp.on('data', onData);
|
|
37
|
+
return () => {
|
|
38
|
+
inp.off('data', onData);
|
|
39
|
+
inp.setRawMode?.(false);
|
|
40
|
+
inp.pause();
|
|
41
|
+
};
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* `ai-obs live [--cwd] [--once]` (ADR-0027): the sessions running on this machine, redrawn in place about once a
|
|
47
|
+
* second (q quits). Local only: no network, nothing written. `--cwd` narrows to the current repository; `--once`
|
|
48
|
+
* (or a stdin / stdout that is not a terminal) prints one frame and exits.
|
|
49
|
+
*/
|
|
50
|
+
export async function liveCommand(args, io, deps) {
|
|
51
|
+
const { values } = parseOptions(args, { cwd: { type: 'boolean' }, once: { type: 'boolean' } });
|
|
52
|
+
const cwd = deps.cwd ?? process.cwd();
|
|
53
|
+
const scope = values.cwd ? ((await gitTopLevel(cwd, 2000)) ?? resolve(cwd)) : null;
|
|
54
|
+
const terminal = deps.terminal ?? stdTerminal();
|
|
55
|
+
const monitor = new LiveMonitor({ paths: deps.paths, claudeDir: deps.claudeDir ?? claudeDir(deps.env ?? process.env), now: () => nowOf(deps) });
|
|
56
|
+
const frame = async (inPlace) => {
|
|
57
|
+
const all = await monitor.refresh();
|
|
58
|
+
const rows = scope ? all.filter((r) => inScope(r.cwd, scope)) : all;
|
|
59
|
+
if (!inPlace)
|
|
60
|
+
return renderFrame(rows, { now: nowOf(deps), width: terminal.isTTY ? terminal.columns() : PIPE_WIDTH, scope });
|
|
61
|
+
// Redrawn in place: one column short (a full-width line followed by erase-to-end would lose its last character),
|
|
62
|
+
// and no taller than the terminal (it would scroll the header away).
|
|
63
|
+
const height = terminal.rows?.();
|
|
64
|
+
return renderFrame(rows, { now: nowOf(deps), width: terminal.columns() - 1, scope, ...(height ? { rows: height } : {}) });
|
|
65
|
+
};
|
|
66
|
+
if (values.once || !terminal.isTTY) {
|
|
67
|
+
io.out((await frame(false)).join('\n'));
|
|
68
|
+
return 0;
|
|
69
|
+
}
|
|
70
|
+
terminal.write(ENTER);
|
|
71
|
+
return new Promise((done, fail) => {
|
|
72
|
+
let stopped = false;
|
|
73
|
+
let timer;
|
|
74
|
+
let off = () => { };
|
|
75
|
+
const detach = () => {
|
|
76
|
+
process.off('SIGTERM', stop);
|
|
77
|
+
process.off('SIGINT', stop);
|
|
78
|
+
process.off('exit', onExit);
|
|
79
|
+
};
|
|
80
|
+
// the terminal is restored whatever happens: a key, a signal, a throw, or the process exiting under us
|
|
81
|
+
const onExit = () => {
|
|
82
|
+
if (!stopped)
|
|
83
|
+
terminal.write(LEAVE);
|
|
84
|
+
};
|
|
85
|
+
const stop = () => {
|
|
86
|
+
if (stopped)
|
|
87
|
+
return;
|
|
88
|
+
stopped = true;
|
|
89
|
+
clearTimeout(timer);
|
|
90
|
+
try {
|
|
91
|
+
off();
|
|
92
|
+
}
|
|
93
|
+
finally {
|
|
94
|
+
detach();
|
|
95
|
+
terminal.write(LEAVE);
|
|
96
|
+
done(0);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
process.once('SIGTERM', stop);
|
|
100
|
+
process.once('SIGINT', stop);
|
|
101
|
+
process.once('exit', onExit);
|
|
102
|
+
try {
|
|
103
|
+
off = terminal.onKey((key) => {
|
|
104
|
+
if (key === 'q' || key === 'Q' || key === '\u0003')
|
|
105
|
+
stop();
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
stopped = true;
|
|
110
|
+
detach();
|
|
111
|
+
terminal.write(LEAVE);
|
|
112
|
+
fail(err);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const tick = async () => {
|
|
116
|
+
try {
|
|
117
|
+
const lines = await frame(true);
|
|
118
|
+
if (!stopped)
|
|
119
|
+
terminal.write(`\x1b[H${lines.join('\x1b[K\r\n')}\x1b[J`);
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
/* a failed read shows the previous frame; the next tick retries */
|
|
123
|
+
}
|
|
124
|
+
if (!stopped)
|
|
125
|
+
timer = setTimeout(() => void tick(), deps.refreshMs ?? LIVE_REFRESH_MS);
|
|
126
|
+
};
|
|
127
|
+
void tick();
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=live.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"live.js","sourceRoot":"","sources":["../../src/commands/live.ts"],"names":[],"mappings":"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"}
|