@phnx-labs/agents-cli 1.22.14 → 1.22.16
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 +53 -0
- package/README.md +7 -5
- package/dist/bin/agents +0 -0
- package/dist/commands/cloud.js +4 -6
- package/dist/commands/exec.js +96 -11
- package/dist/commands/humans.d.ts +10 -0
- package/dist/commands/humans.js +91 -0
- package/dist/commands/packages.js +90 -22
- package/dist/commands/projects.d.ts +2 -5
- package/dist/commands/projects.js +92 -65
- package/dist/commands/resume.d.ts +17 -0
- package/dist/commands/resume.js +80 -0
- package/dist/commands/routines.js +53 -71
- package/dist/commands/sessions.d.ts +13 -4
- package/dist/commands/sessions.js +37 -19
- package/dist/commands/setup-watchdog.d.ts +3 -0
- package/dist/commands/setup-watchdog.js +54 -0
- package/dist/commands/setup.js +8 -1
- package/dist/commands/ssh.d.ts +11 -0
- package/dist/commands/ssh.js +18 -4
- package/dist/commands/watchdog.d.ts +3 -4
- package/dist/commands/watchdog.js +40 -28
- package/dist/index.js +3 -2
- package/dist/lib/auto-dispatch.d.ts +8 -10
- package/dist/lib/auto-dispatch.js +20 -42
- package/dist/lib/channels/send.d.ts +4 -1
- package/dist/lib/channels/send.js +15 -7
- package/dist/lib/devices/doctor-findings.d.ts +14 -0
- package/dist/lib/devices/doctor-findings.js +92 -51
- package/dist/lib/exec.d.ts +6 -2
- package/dist/lib/exec.js +22 -4
- package/dist/lib/hooks.d.ts +4 -2
- package/dist/lib/hooks.js +173 -23
- package/dist/lib/humans.d.ts +25 -0
- package/dist/lib/humans.js +65 -0
- package/dist/lib/memory.js +2 -1
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/migrate.d.ts +1 -1
- package/dist/lib/migrate.js +116 -7
- package/dist/lib/notify.js +6 -2
- package/dist/lib/permissions.js +12 -10
- package/dist/lib/project-import.d.ts +7 -46
- package/dist/lib/project-import.js +9 -108
- package/dist/lib/projects.d.ts +20 -3
- package/dist/lib/projects.js +36 -18
- package/dist/lib/registry.d.ts +1 -1
- package/dist/lib/registry.js +11 -0
- package/dist/lib/routine-activation.d.ts +14 -0
- package/dist/lib/routine-activation.js +78 -0
- package/dist/lib/routines-project.d.ts +1 -1
- package/dist/lib/routines-project.js +3 -9
- package/dist/lib/routines.d.ts +3 -1
- package/dist/lib/routines.js +32 -19
- 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/secrets/bundles.d.ts +11 -0
- package/dist/lib/secrets/bundles.js +122 -5
- package/dist/lib/session/actor-sidecar.d.ts +5 -2
- package/dist/lib/session/actor-sidecar.js +4 -2
- package/dist/lib/session/db.d.ts +2 -1
- package/dist/lib/session/db.js +19 -3
- package/dist/lib/session/types.d.ts +4 -0
- package/dist/lib/staleness/writers/sources.d.ts +6 -1
- package/dist/lib/staleness/writers/sources.js +93 -7
- package/dist/lib/startup/command-registry.d.ts +2 -0
- package/dist/lib/startup/command-registry.js +5 -0
- package/dist/lib/state.d.ts +2 -0
- package/dist/lib/state.js +23 -10
- package/dist/lib/types.d.ts +64 -1
- package/dist/lib/versions.js +48 -14
- package/dist/lib/watchdog/rotate.d.ts +1 -1
- package/dist/lib/watchdog/rotate.js +1 -1
- package/package.json +1 -1
- package/dist/lib/watchdog/routine.d.ts +0 -44
- package/dist/lib/watchdog/routine.js +0 -69
package/dist/lib/state.js
CHANGED
|
@@ -60,6 +60,10 @@ const LEGACY_SYSTEM_AGENTS_DIR = path.join(HOME, '.agents-system');
|
|
|
60
60
|
const META_FILE = path.join(USER_AGENTS_DIR, 'agents.yaml');
|
|
61
61
|
/** Legacy location — used only for one-shot migration in readMeta(). */
|
|
62
62
|
const SYSTEM_META_FILE = path.join(SYSTEM_AGENTS_DIR, 'agents.yaml');
|
|
63
|
+
/** Canonical path for the humans.yaml owner-identity/channel config. */
|
|
64
|
+
const HUMANS_FILE = path.join(USER_AGENTS_DIR, 'humans.yaml');
|
|
65
|
+
/** Return the absolute path to the humans.yaml file. */
|
|
66
|
+
export function getHumansFilePath() { return process.env.AGENTS_HUMANS_FILE ?? HUMANS_FILE; }
|
|
63
67
|
// ─── System resource dirs ─────────────────────────────────────────────────────
|
|
64
68
|
const SYSTEM_COMMANDS_DIR = path.join(SYSTEM_AGENTS_DIR, 'commands');
|
|
65
69
|
const SYSTEM_HOOKS_DIR = path.join(SYSTEM_AGENTS_DIR, 'hooks');
|
|
@@ -300,7 +304,7 @@ export function getResolvedRulesDir() { return SYSTEM_RULES_DIR; }
|
|
|
300
304
|
/** Path to MCP server YAML configs — system repo. */
|
|
301
305
|
export function getMcpDir() { return SYSTEM_MCP_DIR; }
|
|
302
306
|
/** Path to permission group YAML files — system repo. */
|
|
303
|
-
export function getPermissionsDir() { return SYSTEM_PERMISSIONS_DIR; }
|
|
307
|
+
export function getPermissionsDir() { return process.env.AGENTS_SYSTEM_PERMISSIONS_DIR ?? SYSTEM_PERMISSIONS_DIR; }
|
|
304
308
|
/** Path to subagent definition directories — system repo. */
|
|
305
309
|
export function getSubagentsDir() { return SYSTEM_SUBAGENTS_DIR; }
|
|
306
310
|
/** Path to ~/.agents/.system/hooks/promptcuts.yaml (system defaults). */
|
|
@@ -359,7 +363,7 @@ export function getUserHooksDir() { return USER_HOOKS_DIR; }
|
|
|
359
363
|
export function getUserSkillsDir() { return USER_SKILLS_DIR; }
|
|
360
364
|
export function getUserRulesDir() { return USER_RULES_DIR; }
|
|
361
365
|
export function getUserMcpDir() { return USER_MCP_DIR; }
|
|
362
|
-
export function getUserPermissionsDir() { return USER_PERMISSIONS_DIR; }
|
|
366
|
+
export function getUserPermissionsDir() { return process.env.AGENTS_USER_PERMISSIONS_DIR ?? USER_PERMISSIONS_DIR; }
|
|
363
367
|
export function getUserSubagentsDir() { return USER_SUBAGENTS_DIR; }
|
|
364
368
|
export function getSystemWorkflowsDir() { return SYSTEM_WORKFLOWS_DIR; }
|
|
365
369
|
export function getUserWorkflowsDir() { return USER_WORKFLOWS_DIR; }
|
|
@@ -674,8 +678,6 @@ export function ensureAgentsDir() {
|
|
|
674
678
|
fs.mkdirSync(PACKAGES_DIR, opts);
|
|
675
679
|
if (!fs.existsSync(ROUTINES_DIR))
|
|
676
680
|
fs.mkdirSync(ROUTINES_DIR, opts);
|
|
677
|
-
if (!fs.existsSync(WEBHOOKS_DIR))
|
|
678
|
-
fs.mkdirSync(WEBHOOKS_DIR, opts);
|
|
679
681
|
if (!fs.existsSync(RUNS_DIR))
|
|
680
682
|
fs.mkdirSync(RUNS_DIR, opts);
|
|
681
683
|
if (!fs.existsSync(VERSIONS_DIR))
|
|
@@ -694,8 +696,6 @@ export function ensureAgentsDir() {
|
|
|
694
696
|
fs.mkdirSync(SYSTEM_PERMISSIONS_DIR, opts);
|
|
695
697
|
if (!fs.existsSync(SYSTEM_SUBAGENTS_DIR))
|
|
696
698
|
fs.mkdirSync(SYSTEM_SUBAGENTS_DIR, opts);
|
|
697
|
-
if (!fs.existsSync(SYSTEM_WEBHOOKS_DIR))
|
|
698
|
-
fs.mkdirSync(SYSTEM_WEBHOOKS_DIR, opts);
|
|
699
699
|
try {
|
|
700
700
|
fs.chmodSync(SYSTEM_AGENTS_DIR, 0o700);
|
|
701
701
|
}
|
|
@@ -812,6 +812,7 @@ const META_KEY_SCOPE = {
|
|
|
812
812
|
versions: 'device',
|
|
813
813
|
defaultBrowserProfile: 'device',
|
|
814
814
|
deviceConfig: 'device',
|
|
815
|
+
deviceRoutines: 'device',
|
|
815
816
|
// Central — synced via agents.yaml.
|
|
816
817
|
run: 'central',
|
|
817
818
|
model: 'central',
|
|
@@ -909,7 +910,7 @@ function serializeCentral(central) {
|
|
|
909
910
|
return isEmpty ? META_HEADER : doc.toString({ collectionStyle: 'block' });
|
|
910
911
|
}
|
|
911
912
|
function writeMetaUnlocked(meta) {
|
|
912
|
-
const { agents, isolatedAgents, versions, defaultBrowserProfile, deviceConfig, ...central } = meta;
|
|
913
|
+
const { agents, isolatedAgents, versions, defaultBrowserProfile, deviceConfig, deviceRoutines, ...central } = meta;
|
|
913
914
|
// Write the machine-local files FIRST, then strip central — so a crash mid-write
|
|
914
915
|
// never removes pins/versions from central before they're persisted elsewhere.
|
|
915
916
|
const devicePath = getDeviceMetaPath();
|
|
@@ -923,7 +924,8 @@ function writeMetaUnlocked(meta) {
|
|
|
923
924
|
// Device-scope config (`maxAgents`, `schedulerEnabled`, …) is per-machine, so it
|
|
924
925
|
// rides the device doc under `config:` — never the central doc that syncs.
|
|
925
926
|
const hasDeviceConfig = !!deviceConfig && Object.keys(deviceConfig).length > 0;
|
|
926
|
-
|
|
927
|
+
const hasDeviceRoutines = Array.isArray(deviceRoutines);
|
|
928
|
+
if (hasAgents || hasIsolatedAgents || hasDefaultBrowser || hasDeviceConfig || hasDeviceRoutines) {
|
|
927
929
|
// Device-local doc carries `agents:` pins, `defaultBrowserProfile:`, and the
|
|
928
930
|
// device-scope `config:` — all per-machine and must never land in central
|
|
929
931
|
// agents.yaml (which syncs).
|
|
@@ -936,6 +938,8 @@ function writeMetaUnlocked(meta) {
|
|
|
936
938
|
deviceDoc.defaultBrowserProfile = defaultBrowserProfile;
|
|
937
939
|
if (hasDeviceConfig)
|
|
938
940
|
deviceDoc.config = deviceConfig;
|
|
941
|
+
if (hasDeviceRoutines)
|
|
942
|
+
deviceDoc.routines = deviceRoutines;
|
|
939
943
|
fs.mkdirSync(path.dirname(devicePath), { recursive: true });
|
|
940
944
|
writeIfChanged(devicePath, META_HEADER + yaml.stringify(deviceDoc));
|
|
941
945
|
}
|
|
@@ -971,8 +975,12 @@ function writeMetaUnlocked(meta) {
|
|
|
971
975
|
function overlayMachineLocal(meta) {
|
|
972
976
|
const devicePath = getDeviceMetaPath();
|
|
973
977
|
if (fs.existsSync(devicePath)) {
|
|
978
|
+
let dm = null;
|
|
974
979
|
try {
|
|
975
|
-
|
|
980
|
+
dm = yaml.parse(fs.readFileSync(devicePath, 'utf-8'));
|
|
981
|
+
}
|
|
982
|
+
catch { /* preserve the existing tolerance for malformed legacy device YAML */ }
|
|
983
|
+
if (dm) {
|
|
976
984
|
if (dm?.agents)
|
|
977
985
|
meta.agents = { ...meta.agents, ...dm.agents };
|
|
978
986
|
if (dm?.isolatedAgents)
|
|
@@ -981,8 +989,13 @@ function overlayMachineLocal(meta) {
|
|
|
981
989
|
meta.defaultBrowserProfile = dm.defaultBrowserProfile;
|
|
982
990
|
if (dm?.config)
|
|
983
991
|
meta.deviceConfig = dm.config;
|
|
992
|
+
if (dm && Object.prototype.hasOwnProperty.call(dm, 'routines')) {
|
|
993
|
+
if (!Array.isArray(dm.routines) || dm.routines.some((name) => typeof name !== 'string')) {
|
|
994
|
+
throw new Error(`Device config corrupted at ${devicePath}: routines must be a string list.`);
|
|
995
|
+
}
|
|
996
|
+
meta.deviceRoutines = dm.routines;
|
|
997
|
+
}
|
|
984
998
|
}
|
|
985
|
-
catch { /* ignore malformed device file */ }
|
|
986
999
|
}
|
|
987
1000
|
const vrPath = getVersionResourcesPath();
|
|
988
1001
|
if (fs.existsSync(vrPath)) {
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -511,10 +511,16 @@ export interface RegistrySearchResult {
|
|
|
511
511
|
}
|
|
512
512
|
/** A package that has been resolved from a registry and is ready to install. */
|
|
513
513
|
export interface ResolvedPackage {
|
|
514
|
-
|
|
514
|
+
/** `plugin` is Phase 5 packaging: `agents install plugin:<spec>` → plugins install. */
|
|
515
|
+
type: 'mcp' | 'skill' | 'git' | 'plugin';
|
|
515
516
|
source: string;
|
|
516
517
|
mcpEntry?: McpServerEntry;
|
|
517
518
|
skillEntry?: SkillEntry;
|
|
519
|
+
/**
|
|
520
|
+
* Plugin install spec (`name@url`, path, or bare source) when `type === 'plugin'`.
|
|
521
|
+
* Same grammar as `agents plugins install <spec>`.
|
|
522
|
+
*/
|
|
523
|
+
pluginSpec?: string;
|
|
518
524
|
}
|
|
519
525
|
/** Categories of resources that can be synced into an agent version home. */
|
|
520
526
|
export type ResourceType = 'commands' | 'skills' | 'hooks' | 'memory' | 'mcp' | 'permissions' | 'subagents' | 'plugins' | 'workflows';
|
|
@@ -937,6 +943,13 @@ export interface Meta {
|
|
|
937
943
|
* device-config registry (`maxAgents`, `schedulerEnabled`, `notes`). Per-machine by design — unset = today's behavior.
|
|
938
944
|
*/
|
|
939
945
|
deviceConfig?: Record<string, unknown>;
|
|
946
|
+
/**
|
|
947
|
+
* Routine names enabled on this machine. In memory this stays distinct from
|
|
948
|
+
* portable user config; state.ts writes it as top-level `routines:` in
|
|
949
|
+
* `~/.agents/devices/<machine>/agents.yaml`. Presence in the list is the whole
|
|
950
|
+
* activation state: absent means disabled on this device.
|
|
951
|
+
*/
|
|
952
|
+
deviceRoutines?: string[];
|
|
940
953
|
/**
|
|
941
954
|
* Agent-host registry keyed by host name (`agents hosts`). Portable user
|
|
942
955
|
* config synced with `agents repo push/pull`. For `ssh-config` hosts this is
|
|
@@ -989,6 +1002,56 @@ export interface Meta {
|
|
|
989
1002
|
projects?: string[];
|
|
990
1003
|
};
|
|
991
1004
|
}
|
|
1005
|
+
/** A single delivery channel entry in humans.yaml. */
|
|
1006
|
+
export interface HumanChannel {
|
|
1007
|
+
/** Canonical channel identifier (e.g. "imessage", "call"). */
|
|
1008
|
+
id: string;
|
|
1009
|
+
/** Provider transport (e.g. "rush", "twilio"). */
|
|
1010
|
+
transport: string;
|
|
1011
|
+
/** If true the channel is watched for incoming messages. */
|
|
1012
|
+
watch?: boolean;
|
|
1013
|
+
/** Shell command to invoke (for call channels). */
|
|
1014
|
+
cmd?: string;
|
|
1015
|
+
/** Credentials bundle name (`agents secrets`). */
|
|
1016
|
+
creds?: string;
|
|
1017
|
+
/** Whether the channel is intrusive (e.g. voice call). */
|
|
1018
|
+
intrusive?: boolean;
|
|
1019
|
+
}
|
|
1020
|
+
/** Severity-to-channel-list escalation policy in humans.yaml. */
|
|
1021
|
+
export interface HumanPolicy {
|
|
1022
|
+
low?: string[];
|
|
1023
|
+
normal?: string[];
|
|
1024
|
+
critical?: string[];
|
|
1025
|
+
}
|
|
1026
|
+
/** Owner identity and contact configuration in humans.yaml. */
|
|
1027
|
+
export interface HumanOwner {
|
|
1028
|
+
/** Display name. */
|
|
1029
|
+
name?: string;
|
|
1030
|
+
/** IANA timezone string (e.g. "America/Los_Angeles"). */
|
|
1031
|
+
timezone?: string;
|
|
1032
|
+
/** Quiet hours as "HH:MM-HH:MM" in local time. */
|
|
1033
|
+
quiet_hours?: string;
|
|
1034
|
+
/** Default notification severity when unspecified. */
|
|
1035
|
+
default_severity?: 'low' | 'normal' | 'critical';
|
|
1036
|
+
/** Short-form channel config for `agents send --to owner` (channel + recipient). */
|
|
1037
|
+
notify?: {
|
|
1038
|
+
channel: string;
|
|
1039
|
+
to: string;
|
|
1040
|
+
};
|
|
1041
|
+
/** Full delivery-channel definitions. */
|
|
1042
|
+
channels?: HumanChannel[];
|
|
1043
|
+
/** Severity-to-channel escalation policy. */
|
|
1044
|
+
policy?: HumanPolicy;
|
|
1045
|
+
}
|
|
1046
|
+
/**
|
|
1047
|
+
* Versioned humans.yaml config — owner identity, channels, and notification
|
|
1048
|
+
* policy. Written to ~/.agents/humans.yaml by `migrateHumans()`.
|
|
1049
|
+
*/
|
|
1050
|
+
export interface HumansConfig {
|
|
1051
|
+
/** Schema version. Always 1. */
|
|
1052
|
+
version: 1;
|
|
1053
|
+
owner?: HumanOwner;
|
|
1054
|
+
}
|
|
992
1055
|
/** Persisted agent-host entry in agents.yaml (overlay or inline). */
|
|
993
1056
|
export interface HostEntry {
|
|
994
1057
|
/** `ssh-config`: reach via the bare name (ssh resolves). `inline`: use address/user below. */
|
package/dist/lib/versions.js
CHANGED
|
@@ -185,15 +185,24 @@ export function getAvailableResources(cwd = process.cwd()) {
|
|
|
185
185
|
}
|
|
186
186
|
}
|
|
187
187
|
result.skills = filterNamesForActiveResourceProfile('skills', Array.from(skillNames), sourceMapFromResources('skills', cwd));
|
|
188
|
-
// Hooks
|
|
189
|
-
// script
|
|
190
|
-
//
|
|
191
|
-
//
|
|
192
|
-
//
|
|
193
|
-
//
|
|
188
|
+
// Hooks:
|
|
189
|
+
// - top-level script files
|
|
190
|
+
// - one-level *group* dirs that contain top-level scripts (session-starts/*.sh)
|
|
191
|
+
// → each script is its own hook resource (install name = basename)
|
|
192
|
+
// - other directories (e.g. tests/ with fixtures only) → directory bundles,
|
|
193
|
+
// copied wholesale as one resource (pre-existing layout)
|
|
194
|
+
// Auxiliary content like README.md / promptcuts.yaml is not a hook. Older sync
|
|
195
|
+
// runs chmod 0o755'd everything, so an exec bit alone is not the signal.
|
|
194
196
|
const NON_SCRIPT_EXTS = new Set(['.md', '.markdown', '.rst', '.txt', '.yaml', '.yml', '.json', '.toml', '.ini', '.conf']);
|
|
195
197
|
const SCRIPT_EXTS = new Set(['.sh', '.bash', '.zsh', '.py', '.js', '.ts', '.mjs', '.cjs', '.rb', '.pl', '.ps1']);
|
|
198
|
+
const HOOK_GROUP_SKIP = new Set(['node_modules', '.git', '.cache']);
|
|
196
199
|
const hookNames = new Set();
|
|
200
|
+
const isHookScriptName = (fileName, mode) => {
|
|
201
|
+
const ext = path.extname(fileName).toLowerCase();
|
|
202
|
+
if (SCRIPT_EXTS.has(ext))
|
|
203
|
+
return true;
|
|
204
|
+
return (mode & 0o111) !== 0 && !NON_SCRIPT_EXTS.has(ext);
|
|
205
|
+
};
|
|
197
206
|
for (const { base } of resourceBases) {
|
|
198
207
|
const hooksDir = path.join(base, 'hooks');
|
|
199
208
|
if (!fs.existsSync(hooksDir))
|
|
@@ -202,22 +211,47 @@ export function getAvailableResources(cwd = process.cwd()) {
|
|
|
202
211
|
if (name.startsWith('.'))
|
|
203
212
|
continue;
|
|
204
213
|
try {
|
|
205
|
-
const
|
|
214
|
+
const full = path.join(hooksDir, name);
|
|
215
|
+
const stat = fs.lstatSync(full);
|
|
206
216
|
if (stat.isSymbolicLink())
|
|
207
217
|
continue;
|
|
208
|
-
if (stat.
|
|
209
|
-
|
|
218
|
+
if (stat.isFile()) {
|
|
219
|
+
if (isHookScriptName(name, stat.mode))
|
|
220
|
+
hookNames.add(name);
|
|
210
221
|
continue;
|
|
211
222
|
}
|
|
212
|
-
if (!stat.
|
|
223
|
+
if (!stat.isDirectory() || HOOK_GROUP_SKIP.has(name))
|
|
213
224
|
continue;
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
225
|
+
// Group vs bundle: if the dir has any top-level script files, expand
|
|
226
|
+
// them; otherwise treat the whole dir as one hook resource.
|
|
227
|
+
let nestedNames;
|
|
228
|
+
try {
|
|
229
|
+
nestedNames = fs.readdirSync(full);
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
217
232
|
continue;
|
|
218
233
|
}
|
|
219
|
-
|
|
234
|
+
const scripts = [];
|
|
235
|
+
for (const nested of nestedNames) {
|
|
236
|
+
if (nested.startsWith('.'))
|
|
237
|
+
continue;
|
|
238
|
+
try {
|
|
239
|
+
const nfull = path.join(full, nested);
|
|
240
|
+
const nstat = fs.lstatSync(nfull);
|
|
241
|
+
if (nstat.isSymbolicLink() || !nstat.isFile())
|
|
242
|
+
continue;
|
|
243
|
+
if (isHookScriptName(nested, nstat.mode))
|
|
244
|
+
scripts.push(nested);
|
|
245
|
+
}
|
|
246
|
+
catch { /* ignore */ }
|
|
247
|
+
}
|
|
248
|
+
if (scripts.length > 0) {
|
|
249
|
+
for (const s of scripts)
|
|
250
|
+
hookNames.add(s);
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
220
253
|
hookNames.add(name);
|
|
254
|
+
}
|
|
221
255
|
}
|
|
222
256
|
catch { /* ignore unreadable */ }
|
|
223
257
|
}
|
|
@@ -166,7 +166,7 @@ export declare function isWatchdogRotateEnabled(): boolean;
|
|
|
166
166
|
* Persist `watchdog.rotate: on|off`. Called by the `agents watchdog rotate
|
|
167
167
|
* on|off` subcommand (commands/watchdog.ts) — the rotate-only switch the
|
|
168
168
|
* Factory migration uses so a user who opted out of autoRotate keeps nudging
|
|
169
|
-
* (rather than `agents watchdog
|
|
169
|
+
* (rather than `agents watchdog off`, which disables the whole watchdog here).
|
|
170
170
|
*/
|
|
171
171
|
export declare function setWatchdogRotateEnabled(on: boolean): void;
|
|
172
172
|
export interface RotateGateResult {
|
|
@@ -291,7 +291,7 @@ export function isWatchdogRotateEnabled() {
|
|
|
291
291
|
* Persist `watchdog.rotate: on|off`. Called by the `agents watchdog rotate
|
|
292
292
|
* on|off` subcommand (commands/watchdog.ts) — the rotate-only switch the
|
|
293
293
|
* Factory migration uses so a user who opted out of autoRotate keeps nudging
|
|
294
|
-
* (rather than `agents watchdog
|
|
294
|
+
* (rather than `agents watchdog off`, which disables the whole watchdog here).
|
|
295
295
|
*/
|
|
296
296
|
export function setWatchdogRotateEnabled(on) {
|
|
297
297
|
const meta = readMeta();
|
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.16",
|
|
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",
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The watchdog, expressed as a routine.
|
|
3
|
-
*
|
|
4
|
-
* The always-on watchdog is not a bespoke subsystem — it is a plain `command`
|
|
5
|
-
* routine the daemon's existing scheduler fires: `agents watchdog --nudge`, once
|
|
6
|
-
* every couple of minutes. Enabling / disabling the watchdog is enabling /
|
|
7
|
-
* disabling that routine, so there is ONE durable, inspectable concept
|
|
8
|
-
* (`agents routines list` shows it; the scheduler catches it up if the daemon was
|
|
9
|
-
* down) instead of a private sentinel file and a hand-rolled `--watch` loop.
|
|
10
|
-
*
|
|
11
|
-
* A `command` routine — not `agent`/`workflow` — because the tick is deterministic
|
|
12
|
-
* housekeeping: no LLM, no auth, no sandbox. Each fire runs ONE bounded tick (the
|
|
13
|
-
* cron is the loop), so the daemon's per-fire model fits without a long-lived
|
|
14
|
-
* process.
|
|
15
|
-
*/
|
|
16
|
-
import { type JobConfig } from '../routines.js';
|
|
17
|
-
/** The routine name. `agents routines list` / `readJob` key on this. */
|
|
18
|
-
export declare const WATCHDOG_ROUTINE_NAME = "watchdog";
|
|
19
|
-
/**
|
|
20
|
-
* Fire every 2 minutes. Mirrors the extension's 120s tick, and is well inside the
|
|
21
|
-
* 5m stall threshold and 20m nudge cooldown, so a stall is noticed promptly
|
|
22
|
-
* without the daemon spawning a tick more often than the cooldown can matter.
|
|
23
|
-
*/
|
|
24
|
-
export declare const WATCHDOG_ROUTINE_SCHEDULE = "*/2 * * * *";
|
|
25
|
-
/** The plain shell each fire runs: one bounded, deterministic nudge tick. */
|
|
26
|
-
export declare const WATCHDOG_ROUTINE_COMMAND = "agents watchdog --nudge";
|
|
27
|
-
/**
|
|
28
|
-
* Build the watchdog routine config. Pure — touches no disk. `mode`/`effort`/
|
|
29
|
-
* `timeout` are required by JobConfig but ignored for `command` routines;
|
|
30
|
-
* writeJob() drops the default ('auto'/'10m') values so the on-disk file stays
|
|
31
|
-
* to the point (name + schedule + command + enabled).
|
|
32
|
-
*/
|
|
33
|
-
export declare function buildWatchdogRoutine(enabled: boolean): JobConfig;
|
|
34
|
-
/** Is the watchdog routine present on disk (user or system layer)? */
|
|
35
|
-
export declare function watchdogRoutineExists(): boolean;
|
|
36
|
-
/** Is the watchdog routine present AND enabled? */
|
|
37
|
-
export declare function isWatchdogRoutineEnabled(): boolean;
|
|
38
|
-
/**
|
|
39
|
-
* Ensure the watchdog routine exists with the requested enabled state, preserving
|
|
40
|
-
* any user edits. Absent → create it; present → only flip `enabled` (never clobber
|
|
41
|
-
* a schedule or command the user tuned). Idempotent: calling it twice with the
|
|
42
|
-
* same state is a no-op after the first.
|
|
43
|
-
*/
|
|
44
|
-
export declare function ensureWatchdogRoutine(enabled: boolean): void;
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The watchdog, expressed as a routine.
|
|
3
|
-
*
|
|
4
|
-
* The always-on watchdog is not a bespoke subsystem — it is a plain `command`
|
|
5
|
-
* routine the daemon's existing scheduler fires: `agents watchdog --nudge`, once
|
|
6
|
-
* every couple of minutes. Enabling / disabling the watchdog is enabling /
|
|
7
|
-
* disabling that routine, so there is ONE durable, inspectable concept
|
|
8
|
-
* (`agents routines list` shows it; the scheduler catches it up if the daemon was
|
|
9
|
-
* down) instead of a private sentinel file and a hand-rolled `--watch` loop.
|
|
10
|
-
*
|
|
11
|
-
* A `command` routine — not `agent`/`workflow` — because the tick is deterministic
|
|
12
|
-
* housekeeping: no LLM, no auth, no sandbox. Each fire runs ONE bounded tick (the
|
|
13
|
-
* cron is the loop), so the daemon's per-fire model fits without a long-lived
|
|
14
|
-
* process.
|
|
15
|
-
*/
|
|
16
|
-
import { readJob, writeJob, setJobEnabled } from '../routines.js';
|
|
17
|
-
/** The routine name. `agents routines list` / `readJob` key on this. */
|
|
18
|
-
export const WATCHDOG_ROUTINE_NAME = 'watchdog';
|
|
19
|
-
/**
|
|
20
|
-
* Fire every 2 minutes. Mirrors the extension's 120s tick, and is well inside the
|
|
21
|
-
* 5m stall threshold and 20m nudge cooldown, so a stall is noticed promptly
|
|
22
|
-
* without the daemon spawning a tick more often than the cooldown can matter.
|
|
23
|
-
*/
|
|
24
|
-
export const WATCHDOG_ROUTINE_SCHEDULE = '*/2 * * * *';
|
|
25
|
-
/** The plain shell each fire runs: one bounded, deterministic nudge tick. */
|
|
26
|
-
export const WATCHDOG_ROUTINE_COMMAND = 'agents watchdog --nudge';
|
|
27
|
-
/**
|
|
28
|
-
* Build the watchdog routine config. Pure — touches no disk. `mode`/`effort`/
|
|
29
|
-
* `timeout` are required by JobConfig but ignored for `command` routines;
|
|
30
|
-
* writeJob() drops the default ('auto'/'10m') values so the on-disk file stays
|
|
31
|
-
* to the point (name + schedule + command + enabled).
|
|
32
|
-
*/
|
|
33
|
-
export function buildWatchdogRoutine(enabled) {
|
|
34
|
-
return {
|
|
35
|
-
name: WATCHDOG_ROUTINE_NAME,
|
|
36
|
-
schedule: WATCHDOG_ROUTINE_SCHEDULE,
|
|
37
|
-
command: WATCHDOG_ROUTINE_COMMAND,
|
|
38
|
-
enabled,
|
|
39
|
-
mode: 'auto',
|
|
40
|
-
effort: 'auto',
|
|
41
|
-
timeout: '10m',
|
|
42
|
-
prompt: '',
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
/** Is the watchdog routine present on disk (user or system layer)? */
|
|
46
|
-
export function watchdogRoutineExists() {
|
|
47
|
-
return readJob(WATCHDOG_ROUTINE_NAME) !== null;
|
|
48
|
-
}
|
|
49
|
-
/** Is the watchdog routine present AND enabled? */
|
|
50
|
-
export function isWatchdogRoutineEnabled() {
|
|
51
|
-
const job = readJob(WATCHDOG_ROUTINE_NAME);
|
|
52
|
-
return job !== null && job.enabled === true;
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Ensure the watchdog routine exists with the requested enabled state, preserving
|
|
56
|
-
* any user edits. Absent → create it; present → only flip `enabled` (never clobber
|
|
57
|
-
* a schedule or command the user tuned). Idempotent: calling it twice with the
|
|
58
|
-
* same state is a no-op after the first.
|
|
59
|
-
*/
|
|
60
|
-
export function ensureWatchdogRoutine(enabled) {
|
|
61
|
-
const existing = readJob(WATCHDOG_ROUTINE_NAME);
|
|
62
|
-
if (existing === null) {
|
|
63
|
-
writeJob(buildWatchdogRoutine(enabled));
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
if (existing.enabled !== enabled) {
|
|
67
|
-
setJobEnabled(WATCHDOG_ROUTINE_NAME, enabled);
|
|
68
|
-
}
|
|
69
|
-
}
|