@phnx-labs/agents-cli 1.22.13 → 1.22.14
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/CHANGELOG.md +17 -0
- package/dist/bin/agents +0 -0
- package/dist/commands/secrets.js +10 -6
- package/dist/commands/sessions-optimize.d.ts +14 -0
- package/dist/commands/sessions-optimize.js +41 -0
- package/dist/commands/sessions.js +2 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/session/db.d.ts +19 -0
- package/dist/lib/session/db.js +24 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.22.14
|
|
4
|
+
|
|
5
|
+
- **`agents secrets view <bundle> --reveal` now resolves a locked keychain bundle
|
|
6
|
+
interactively at a real terminal.** The command hardcoded `agentOnly: true` on
|
|
7
|
+
both reveal call sites (`commands/secrets.ts`), so an explicit human `--reveal`
|
|
8
|
+
on a locked bundle went through the broker-only path and errored with an unlock
|
|
9
|
+
hint instead of raising the one Touch ID sheet the human just asked for. The
|
|
10
|
+
`agentOnly` flag is now `isHeadlessSecretsContext() || !isInteractiveTerminal()`
|
|
11
|
+
— under an agent (`AGENTS_RUNTIME`) or with no TTY it stays broker-only and
|
|
12
|
+
never prompts, but a deliberate `--reveal` typed at an interactive terminal
|
|
13
|
+
resolves the value with a single biometric sheet. This mirrors the existing
|
|
14
|
+
`reveal && !isInteractiveTerminal()` guard a few lines up. `export --plaintext`
|
|
15
|
+
and `exec` are untouched — they stay intentionally silent for release/CI
|
|
16
|
+
scripts. Source: `apps/cli/src/commands/secrets.ts`.
|
|
17
|
+
|
|
18
|
+
- **`agents sessions optimize` — compact the FTS5 session search index.** The scanner delete+inserts a session's docs into the `tool_call_text` / `session_text` full-text indexes on every rescan, and FTS5 never merges the resulting segments on its own — so over thousands of sessions the `%_data` shadow tables bloat with hundreds of thousands of unmerged segments (observed on a real fleet box: 701 MB of index for ~69 MB of content, 196K segments) and `agents sessions` slows to a crawl / hangs. The new command runs FTS5 `'optimize'` (merge all segments, purge tombstones), non-destructively — no searchable content is lost. Reclaimed space frees as reusable pages inside the DB file (VACUUM with the daemon stopped returns it to disk); wireable to a weekly routine so the index never re-bloats. Source: `apps/cli/src/lib/session/db.ts` (`optimizeSessionSearchIndex`), `apps/cli/src/commands/sessions-optimize.ts`.
|
|
19
|
+
|
|
3
20
|
## 1.22.13
|
|
4
21
|
|
|
5
22
|
- **`agents sessions` accepts direct live-state flags and remains fleet-wide by default.** `--working`, `--idle`, `--waiting`, `--orphan`/`--orphaned`, `--crashed`, `--closed`, `--abandoned`, `--queued`, and `--unknown` each imply the live scan; multiple flags form a union. `--working` is narrower than `--active`: it excludes idle, waiting, and lifecycle-failure rows. Cross-device collection was already the default and stays that way; `--local` opts out, while `--all` continues to widen historical directory and time scope. Source: `apps/cli/src/commands/sessions.ts`, `apps/cli/src/commands/sessions.test.ts`.
|
package/dist/bin/agents
CHANGED
|
Binary file
|
package/dist/commands/secrets.js
CHANGED
|
@@ -18,7 +18,7 @@ import { ensureDaemonStarted, isDaemonRunning } from '../lib/daemon.js';
|
|
|
18
18
|
import { parseHostsOption, remoteResolveEnv, remoteSecretsRaw, remoteSecretsStream, resolveHostSshTarget, verifyRemoteKeychainPush, keychainWriteFailureMessage, } from '../lib/secrets/remote.js';
|
|
19
19
|
import { remoteShellFor, buildWindowsStdinImportCommand } from '../lib/hosts/remote-cmd.js';
|
|
20
20
|
import { resolveRemoteOsSync } from '../lib/hosts/remote-os.js';
|
|
21
|
-
import { bundleBackend, bundleExists, bundleItemStore, bundlePolicy, deleteBundle, describeBundle, keychainItemsForBundle, keychainRef, listBundles, healKeychainBundleMetadataAclOnce, migrateLegacyBundles, parseDotenv, readAndResolveBundleEnv, readBundle, readBundleIfDecryptable, reAclBundleItems, renameBundle, rotateBundleSecret, sanitizeProcessEnv, validateBundleName, validateEnvKey, validateExpiresFutureDated, validateSecretType, writeBundle, writeBundleWithItems, SECRET_TYPES, } from '../lib/secrets/bundles.js';
|
|
21
|
+
import { bundleBackend, bundleExists, bundleItemStore, bundlePolicy, deleteBundle, describeBundle, keychainItemsForBundle, keychainRef, listBundles, healKeychainBundleMetadataAclOnce, isHeadlessSecretsContext, migrateLegacyBundles, parseDotenv, readAndResolveBundleEnv, readBundle, readBundleIfDecryptable, reAclBundleItems, renameBundle, rotateBundleSecret, sanitizeProcessEnv, validateBundleName, validateEnvKey, validateExpiresFutureDated, validateSecretType, writeBundle, writeBundleWithItems, SECRET_TYPES, } from '../lib/secrets/bundles.js';
|
|
22
22
|
import { parseListFilters, bundleMatchesFilter, bundleExpiry, filterIsActive, describeFilter, parseSortField, sortBundles, SORT_FIELDS, REF_KINDS, DEFAULT_EXPIRING_DAYS, } from '../lib/secrets/list-filter.js';
|
|
23
23
|
import { encryptForFallback, decryptForFallback } from '../lib/secrets/filestore.js';
|
|
24
24
|
import { getKeychainToken, hasKeychainToken, secretsKeychainItem, setKeychainToken, maybeAutoRekey, } from '../lib/secrets/index.js';
|
|
@@ -1254,7 +1254,10 @@ export function registerSecretsCommands(program) {
|
|
|
1254
1254
|
const { env } = readAndResolveBundleEnv(bundle.name, {
|
|
1255
1255
|
caller: 'view --reveal --json',
|
|
1256
1256
|
keyMode: 'storage',
|
|
1257
|
-
|
|
1257
|
+
// An explicit human `--reveal` at a terminal is a deliberate value
|
|
1258
|
+
// access → resolve interactively (one Touch ID). Under an agent
|
|
1259
|
+
// (AGENTS_RUNTIME) or headless (no TTY) it stays broker-only.
|
|
1260
|
+
agentOnly: isHeadlessSecretsContext() || !isInteractiveTerminal(),
|
|
1258
1261
|
});
|
|
1259
1262
|
for (const entry of entries) {
|
|
1260
1263
|
if (entry.kind === 'keychain' && env[entry.key] !== undefined) {
|
|
@@ -1369,15 +1372,16 @@ export function registerSecretsCommands(program) {
|
|
|
1369
1372
|
console.error(chalk.red('--reveal in a non-TTY requires --plaintext.'));
|
|
1370
1373
|
process.exit(1);
|
|
1371
1374
|
}
|
|
1372
|
-
//
|
|
1373
|
-
//
|
|
1374
|
-
//
|
|
1375
|
+
// An explicit human `view --reveal` at a terminal is a deliberate value
|
|
1376
|
+
// access: resolve interactively (one Touch ID) so a locked bundle shows
|
|
1377
|
+
// its values. Under an agent (AGENTS_RUNTIME) or headless (no TTY) it
|
|
1378
|
+
// stays broker-only and errors with the unlock hint — never a sheet.
|
|
1375
1379
|
const revealedValues = new Map();
|
|
1376
1380
|
if (reveal) {
|
|
1377
1381
|
const { env } = readAndResolveBundleEnv(bundle.name, {
|
|
1378
1382
|
caller: 'view --reveal',
|
|
1379
1383
|
keyMode: 'storage',
|
|
1380
|
-
agentOnly:
|
|
1384
|
+
agentOnly: isHeadlessSecretsContext() || !isInteractiveTerminal(),
|
|
1381
1385
|
});
|
|
1382
1386
|
for (const entry of entries) {
|
|
1383
1387
|
if (entry.kind === 'keychain' && env[entry.key] !== undefined) {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents sessions optimize` — compact the FTS5 session/tool-call search index.
|
|
3
|
+
*
|
|
4
|
+
* The scanner delete+inserts a session's docs into the `tool_call_text` /
|
|
5
|
+
* `session_text` FTS5 indexes on every rescan (and on every extractor-version
|
|
6
|
+
* bump), and FTS5 never merges the resulting segments on its own. Over thousands
|
|
7
|
+
* of sessions and re-index passes the `%_data` shadow tables bloat with hundreds
|
|
8
|
+
* of thousands of unmerged segments — gigabytes of index for tens of MB of
|
|
9
|
+
* actual content — and `agents sessions` queries slow to a crawl. This runs the
|
|
10
|
+
* FTS5 `'optimize'` command to merge every segment into one and purge tombstones.
|
|
11
|
+
* Non-destructive: no searchable content is lost.
|
|
12
|
+
*/
|
|
13
|
+
import type { Command } from 'commander';
|
|
14
|
+
export declare function registerSessionsOptimizeCommand(sessionsCmd: Command): void;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { optimizeSessionSearchIndex } from '../lib/session/db.js';
|
|
3
|
+
import { setHelpSections } from '../lib/help.js';
|
|
4
|
+
export function registerSessionsOptimizeCommand(sessionsCmd) {
|
|
5
|
+
const cmd = sessionsCmd
|
|
6
|
+
.command('optimize')
|
|
7
|
+
.description('Compact the session search index (FTS5), reclaiming bloat from repeated re-indexing')
|
|
8
|
+
.option('--json', 'Emit machine-readable JSON')
|
|
9
|
+
.action((_opts, c) => {
|
|
10
|
+
const opts = c.optsWithGlobals();
|
|
11
|
+
const results = optimizeSessionSearchIndex();
|
|
12
|
+
if (opts.json) {
|
|
13
|
+
console.log(JSON.stringify(results, null, 2));
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
for (const r of results) {
|
|
17
|
+
const merged = Math.max(0, r.segmentsBefore - r.segmentsAfter);
|
|
18
|
+
console.log(` ${chalk.cyan(r.table)}: ${r.segmentsBefore} -> ${r.segmentsAfter} segments ` +
|
|
19
|
+
`(${chalk.green(merged)} merged)`);
|
|
20
|
+
}
|
|
21
|
+
console.log(chalk.gray(' Reclaimed space stays as reusable pages inside the file; VACUUM (daemon stopped) returns it to disk.'));
|
|
22
|
+
});
|
|
23
|
+
setHelpSections(cmd, {
|
|
24
|
+
examples: `
|
|
25
|
+
# Compact the session/tool search index once it has grown fragmented
|
|
26
|
+
agents sessions optimize
|
|
27
|
+
|
|
28
|
+
# Machine-readable segment counts
|
|
29
|
+
agents sessions optimize --json
|
|
30
|
+
|
|
31
|
+
# Wire it to a weekly routine so the index never re-bloats
|
|
32
|
+
agents routines add sessions-optimize --schedule "0 4 * * 0" --agent claude \\
|
|
33
|
+
--prompt "Run: agents sessions optimize"
|
|
34
|
+
`,
|
|
35
|
+
notes: `
|
|
36
|
+
- FTS5 appends a segment on every insert and tombstones every delete; the scanner delete+inserts a session's docs on each rescan and never self-merges, so \`tool_call_text_data\` / \`session_text_data\` bloat with unmerged segments — GBs of index for tens of MB of content, and queries slow down.
|
|
37
|
+
- This runs FTS5 \`'optimize'\`: it merges every segment into one and purges tombstones. Non-destructive — no searchable content is lost.
|
|
38
|
+
- Reclaimed space becomes reusable free pages inside the DB file. To return it to the OS, stop the daemon (\`agents routines stop\`) and run \`VACUUM\` against \`~/.agents/.history/sessions/sessions.db\`.
|
|
39
|
+
`,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
@@ -61,6 +61,7 @@ import { registerSessionsImportCommand } from './sessions-import.js';
|
|
|
61
61
|
import { registerSessionsMigrateCommand, registerSessionsMigrationsCommand } from './sessions-migrate.js';
|
|
62
62
|
import { registerSessionsBackfillCommand } from './sessions-backfill.js';
|
|
63
63
|
import { registerSessionsStatsCommand } from './sessions-stats.js';
|
|
64
|
+
import { registerSessionsOptimizeCommand } from './sessions-optimize.js';
|
|
64
65
|
import { runBrowserSessions } from '../lib/browser/sessions-list.js';
|
|
65
66
|
import { countToolProgramOccurrences, parseToolProgramCountClause, readToolIndexCoverage, searchToolCalls, TOOL_QUERY_MAX_CLAUSE_BYTES, TOOL_QUERY_MAX_CLAUSES, TOOL_QUERY_MAX_RESULT_SESSIONS, serializeToolSearchEnvelope, toolSearchRemoteReceiveBudget, } from '../lib/session/tool-index.js';
|
|
66
67
|
const SESSION_AGENT_FILTER_HELP = `Filter by agent, e.g. claude, codex, claude@2.0.65`;
|
|
@@ -3811,6 +3812,7 @@ export function registerSessionsCommands(program) {
|
|
|
3811
3812
|
registerSessionsMigrationsCommand(sessionsCmd);
|
|
3812
3813
|
registerSessionsBackfillCommand(sessionsCmd);
|
|
3813
3814
|
registerSessionsStatsCommand(sessionsCmd);
|
|
3815
|
+
registerSessionsOptimizeCommand(sessionsCmd);
|
|
3814
3816
|
// Observe-umbrella alias (Phase 3): roster → sessions --active.
|
|
3815
3817
|
registerSessionsObserveAliases(program);
|
|
3816
3818
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/dist/lib/session/db.d.ts
CHANGED
|
@@ -120,6 +120,25 @@ export interface QueryOptions {
|
|
|
120
120
|
export declare function getDB(): Database.Database;
|
|
121
121
|
/** Close the cached database connection. */
|
|
122
122
|
export declare function closeDB(): void;
|
|
123
|
+
export interface FtsOptimizeResult {
|
|
124
|
+
table: string;
|
|
125
|
+
segmentsBefore: number;
|
|
126
|
+
segmentsAfter: number;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Compact the session + tool-call full-text-search indexes.
|
|
130
|
+
*
|
|
131
|
+
* FTS5 appends a new segment on every insert and leaves a tombstone on every
|
|
132
|
+
* delete. The scanner delete+inserts a session's docs on each rescan (and on
|
|
133
|
+
* every extractor-version bump), and FTS5 never merges those segments on its
|
|
134
|
+
* own — so `tool_call_text_data` / `session_text_data` accumulate hundreds of
|
|
135
|
+
* thousands of unmerged segments, gigabytes of index for tens of MB of content,
|
|
136
|
+
* and `agents sessions` queries slow to a crawl. The FTS5 `'optimize'` command
|
|
137
|
+
* merges every segment into one and purges tombstones. Non-destructive: no
|
|
138
|
+
* searchable content is lost. Reclaimed space becomes reusable free pages inside
|
|
139
|
+
* the DB file; run VACUUM (with the daemon stopped) to return it to the OS.
|
|
140
|
+
*/
|
|
141
|
+
export declare function optimizeSessionSearchIndex(): FtsOptimizeResult[];
|
|
123
142
|
/**
|
|
124
143
|
* Try to claim the right to run the incremental scan. Returns true if this
|
|
125
144
|
* process should proceed with scanning, false if another live process is
|
package/dist/lib/session/db.js
CHANGED
|
@@ -820,6 +820,30 @@ export function closeDB() {
|
|
|
820
820
|
cachedStmts = {};
|
|
821
821
|
}
|
|
822
822
|
}
|
|
823
|
+
/**
|
|
824
|
+
* Compact the session + tool-call full-text-search indexes.
|
|
825
|
+
*
|
|
826
|
+
* FTS5 appends a new segment on every insert and leaves a tombstone on every
|
|
827
|
+
* delete. The scanner delete+inserts a session's docs on each rescan (and on
|
|
828
|
+
* every extractor-version bump), and FTS5 never merges those segments on its
|
|
829
|
+
* own — so `tool_call_text_data` / `session_text_data` accumulate hundreds of
|
|
830
|
+
* thousands of unmerged segments, gigabytes of index for tens of MB of content,
|
|
831
|
+
* and `agents sessions` queries slow to a crawl. The FTS5 `'optimize'` command
|
|
832
|
+
* merges every segment into one and purges tombstones. Non-destructive: no
|
|
833
|
+
* searchable content is lost. Reclaimed space becomes reusable free pages inside
|
|
834
|
+
* the DB file; run VACUUM (with the daemon stopped) to return it to the OS.
|
|
835
|
+
*/
|
|
836
|
+
export function optimizeSessionSearchIndex() {
|
|
837
|
+
const db = getDB();
|
|
838
|
+
// Hardcoded literals — never interpolate caller input into an identifier.
|
|
839
|
+
const tables = ['tool_call_text', 'session_text'];
|
|
840
|
+
const segments = (table) => db.prepare(`SELECT count(*) AS n FROM ${table}_data`).get().n;
|
|
841
|
+
return tables.map((table) => {
|
|
842
|
+
const segmentsBefore = segments(table);
|
|
843
|
+
db.prepare(`INSERT INTO ${table}(${table}) VALUES('optimize')`).run();
|
|
844
|
+
return { table, segmentsBefore, segmentsAfter: segments(table) };
|
|
845
|
+
});
|
|
846
|
+
}
|
|
823
847
|
// ---------------------------------------------------------------------------
|
|
824
848
|
// Scan coordinator — prevents concurrent full scans across processes
|
|
825
849
|
// ---------------------------------------------------------------------------
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phnx-labs/agents-cli",
|
|
3
|
-
"version": "1.22.
|
|
3
|
+
"version": "1.22.14",
|
|
4
4
|
"description": "One CLI for all your AI coding agents - versions, config, cloud dispatch, sessions, and teams (now with first-class Grok Build CLI support)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|