@phnx-labs/agents-cli 1.20.71 → 1.20.73
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 +325 -1
- package/README.md +40 -4
- package/dist/bin/agents +0 -0
- package/dist/commands/activity.d.ts +12 -0
- package/dist/commands/activity.js +68 -0
- package/dist/commands/apply.d.ts +13 -0
- package/dist/commands/apply.js +28 -3
- package/dist/commands/browser.js +1 -1
- package/dist/commands/cloud.js +21 -11
- package/dist/commands/commands.js +2 -0
- package/dist/commands/doctor.js +8 -4
- package/dist/commands/events.d.ts +11 -10
- package/dist/commands/events.js +30 -21
- package/dist/commands/exec.d.ts +34 -0
- package/dist/commands/exec.js +337 -39
- package/dist/commands/feed.d.ts +13 -0
- package/dist/commands/feed.js +130 -29
- package/dist/commands/hq.d.ts +2 -0
- package/dist/commands/hq.js +58 -0
- package/dist/commands/lease.d.ts +19 -0
- package/dist/commands/lease.js +138 -11
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.js +123 -0
- package/dist/commands/logs.js +39 -10
- package/dist/commands/mcp.js +2 -0
- package/dist/commands/mine.d.ts +27 -0
- package/dist/commands/mine.js +207 -0
- package/dist/commands/monitors.js +79 -49
- package/dist/commands/plugins.js +1 -0
- package/dist/commands/profiles.js +158 -0
- package/dist/commands/repo.js +2 -60
- package/dist/commands/resource-view.d.ts +2 -0
- package/dist/commands/resource-view.js +8 -0
- package/dist/commands/resources.d.ts +5 -0
- package/dist/commands/resources.js +95 -0
- package/dist/commands/routines.d.ts +2 -0
- package/dist/commands/routines.js +139 -37
- package/dist/commands/secrets.js +234 -47
- package/dist/commands/send.d.ts +15 -0
- package/dist/commands/send.js +62 -0
- package/dist/commands/sessions-tail.d.ts +1 -0
- package/dist/commands/sessions-tail.js +20 -9
- package/dist/commands/sessions.d.ts +6 -0
- package/dist/commands/sessions.js +26 -3
- package/dist/commands/setup-mine.d.ts +18 -0
- package/dist/commands/setup-mine.js +106 -0
- package/dist/commands/setup-share.js +2 -2
- package/dist/commands/setup.js +3 -1
- package/dist/commands/share.d.ts +8 -1
- package/dist/commands/share.js +122 -37
- package/dist/commands/skills.js +2 -0
- package/dist/commands/ssh.d.ts +9 -0
- package/dist/commands/ssh.js +196 -26
- package/dist/commands/status.js +8 -2
- package/dist/commands/subagents.js +3 -1
- package/dist/commands/sync.js +1 -1
- package/dist/commands/teams.js +11 -2
- package/dist/commands/uninstall.d.ts +11 -0
- package/dist/commands/uninstall.js +158 -0
- package/dist/commands/utils.d.ts +29 -0
- package/dist/commands/utils.js +24 -0
- package/dist/commands/versions.js +120 -11
- package/dist/index.js +83 -12
- package/dist/lib/activity.d.ts +124 -0
- package/dist/lib/activity.js +542 -0
- package/dist/lib/agents.js +16 -5
- package/dist/lib/artifact-actions.d.ts +1 -1
- package/dist/lib/artifact-actions.js +1 -1
- package/dist/lib/ask-classifier.d.ts +2 -1
- package/dist/lib/ask-classifier.js +3 -3
- package/dist/lib/auth-health.d.ts +32 -4
- package/dist/lib/auth-health.js +40 -1
- package/dist/lib/auto-pull.js +8 -1
- package/dist/lib/brand.d.ts +40 -0
- package/dist/lib/brand.js +122 -0
- package/dist/lib/browser/drivers/ssh.js +4 -7
- package/dist/lib/browser/service.js +6 -8
- package/dist/lib/channels/providers/index.d.ts +2 -0
- package/dist/lib/channels/providers/index.js +20 -0
- package/dist/lib/channels/providers/mailbox.d.ts +2 -0
- package/dist/lib/channels/providers/mailbox.js +26 -0
- package/dist/lib/channels/providers/openclaw-telegram.d.ts +2 -0
- package/dist/lib/channels/providers/openclaw-telegram.js +34 -0
- package/dist/lib/channels/providers/rush.d.ts +6 -0
- package/dist/lib/channels/providers/rush.js +55 -0
- package/dist/lib/channels/registry.d.ts +42 -0
- package/dist/lib/channels/registry.js +20 -0
- package/dist/lib/channels/resolve.d.ts +11 -0
- package/dist/lib/channels/resolve.js +12 -0
- package/dist/lib/cli-resources.d.ts +18 -0
- package/dist/lib/cli-resources.js +53 -4
- package/dist/lib/cloud/codex.js +6 -3
- package/dist/lib/cloud/factory.d.ts +1 -0
- package/dist/lib/cloud/factory.js +10 -4
- package/dist/lib/cloud/rush.d.ts +3 -1
- package/dist/lib/cloud/rush.js +6 -1
- package/dist/lib/cloud/types.d.ts +2 -0
- package/dist/lib/codex-home.d.ts +35 -0
- package/dist/lib/codex-home.js +136 -0
- package/dist/lib/crabbox/cli.d.ts +44 -0
- package/dist/lib/crabbox/cli.js +120 -20
- package/dist/lib/crabbox/lease.d.ts +52 -1
- package/dist/lib/crabbox/lease.js +117 -16
- package/dist/lib/crabbox/progress.d.ts +31 -0
- package/dist/lib/crabbox/progress.js +76 -0
- package/dist/lib/crabbox/runtimes.d.ts +4 -0
- package/dist/lib/crabbox/runtimes.js +33 -8
- package/dist/lib/crabbox/setup-copy.d.ts +87 -0
- package/dist/lib/crabbox/setup-copy.js +127 -0
- package/dist/lib/daemon.d.ts +14 -5
- package/dist/lib/daemon.js +52 -9
- package/dist/lib/drive-sync.js +6 -3
- package/dist/lib/event-stream.d.ts +36 -0
- package/dist/lib/event-stream.js +68 -0
- package/dist/lib/events.d.ts +1 -1
- package/dist/lib/exec.d.ts +11 -0
- package/dist/lib/exec.js +23 -2
- package/dist/lib/feed-policy.d.ts +1 -1
- package/dist/lib/feed-policy.js +14 -9
- package/dist/lib/feed-ranking.d.ts +32 -0
- package/dist/lib/feed-ranking.js +224 -0
- package/dist/lib/feed.d.ts +35 -2
- package/dist/lib/feed.js +66 -3
- package/dist/lib/fleet/auth-sync.d.ts +53 -0
- package/dist/lib/fleet/auth-sync.js +92 -0
- package/dist/lib/fleet/remote-login.d.ts +187 -0
- package/dist/lib/fleet/remote-login.js +556 -0
- package/dist/lib/format.d.ts +30 -3
- package/dist/lib/format.js +34 -5
- package/dist/lib/fs-atomic.d.ts +7 -2
- package/dist/lib/fs-atomic.js +8 -12
- package/dist/lib/git.d.ts +16 -0
- package/dist/lib/git.js +79 -2
- package/dist/lib/heal.js +11 -3
- package/dist/lib/hosts/logs.d.ts +12 -0
- package/dist/lib/hosts/logs.js +18 -0
- package/dist/lib/hosts/progress.d.ts +28 -10
- package/dist/lib/hosts/progress.js +79 -22
- package/dist/lib/hosts/remote-cmd.js +4 -0
- package/dist/lib/hq/floor.d.ts +87 -0
- package/dist/lib/hq/floor.js +226 -0
- package/dist/lib/menubar/install-menubar.js +14 -20
- package/dist/lib/notify.d.ts +1 -0
- package/dist/lib/notify.js +3 -3
- package/dist/lib/open-url.d.ts +2 -0
- package/dist/lib/open-url.js +19 -0
- package/dist/lib/openclaw-keychain.d.ts +56 -0
- package/dist/lib/openclaw-keychain.js +236 -0
- package/dist/lib/overdue.js +10 -0
- package/dist/lib/permissions.d.ts +44 -1
- package/dist/lib/permissions.js +284 -7
- package/dist/lib/project-launch.d.ts +6 -12
- package/dist/lib/project-launch.js +13 -228
- package/dist/lib/project-resources.d.ts +7 -0
- package/dist/lib/project-resources.js +291 -0
- package/dist/lib/pty-client.d.ts +27 -0
- package/dist/lib/pty-client.js +136 -10
- package/dist/lib/refresh.js +14 -10
- package/dist/lib/resource-profiles.d.ts +26 -0
- package/dist/lib/resource-profiles.js +157 -0
- package/dist/lib/resources/permissions.js +7 -1
- package/dist/lib/resources/types.d.ts +1 -1
- package/dist/lib/resources.d.ts +1 -1
- package/dist/lib/resources.js +32 -3
- package/dist/lib/routines.d.ts +11 -0
- package/dist/lib/routines.js +56 -15
- package/dist/lib/runner.d.ts +1 -0
- package/dist/lib/runner.js +66 -5
- package/dist/lib/secrets/bundles.d.ts +16 -3
- package/dist/lib/secrets/bundles.js +206 -37
- package/dist/lib/secrets/icloud-import.d.ts +2 -2
- package/dist/lib/secrets/icloud-import.js +9 -6
- package/dist/lib/secrets/mcp.js +1 -1
- package/dist/lib/secrets/vault-age-helper.d.ts +1 -0
- package/dist/lib/secrets/vault-age-helper.js +34 -0
- package/dist/lib/secrets/vault.d.ts +49 -0
- package/dist/lib/secrets/vault.js +397 -0
- package/dist/lib/self-heal/checks/path.js +3 -1
- package/dist/lib/self-heal/checks/shadowing.js +10 -2
- package/dist/lib/self-heal/checks/shims.js +19 -11
- package/dist/lib/session/cloud.d.ts +2 -2
- package/dist/lib/session/cloud.js +2 -2
- package/dist/lib/session/db.d.ts +4 -0
- package/dist/lib/session/db.js +44 -7
- package/dist/lib/session/discover.d.ts +2 -0
- package/dist/lib/session/discover.js +114 -3
- package/dist/lib/session/stream-render.d.ts +3 -0
- package/dist/lib/session/stream-render.js +130 -0
- package/dist/lib/session/types.d.ts +6 -0
- package/dist/lib/share/analytics.d.ts +13 -0
- package/dist/lib/share/analytics.js +45 -0
- package/dist/lib/share/config.d.ts +19 -5
- package/dist/lib/share/config.js +44 -8
- package/dist/lib/share/provision.d.ts +58 -6
- package/dist/lib/share/provision.js +97 -22
- package/dist/lib/share/publish.d.ts +36 -13
- package/dist/lib/share/publish.js +55 -8
- package/dist/lib/share/worker-template.js +83 -17
- package/dist/lib/shims.d.ts +36 -0
- package/dist/lib/shims.js +120 -16
- package/dist/lib/ssh-exec.d.ts +14 -0
- package/dist/lib/ssh-exec.js +57 -0
- package/dist/lib/staleness/detectors/hooks.js +25 -1
- package/dist/lib/staleness/detectors/permissions.js +66 -0
- package/dist/lib/staleness/detectors/workflows.js +20 -0
- package/dist/lib/staleness/writers/hooks.js +58 -4
- package/dist/lib/staleness/writers/permissions.js +2 -2
- package/dist/lib/staleness/writers/skills.js +1 -1
- package/dist/lib/staleness/writers/sources.d.ts +10 -1
- package/dist/lib/staleness/writers/sources.js +68 -1
- package/dist/lib/star-nudge.d.ts +45 -0
- package/dist/lib/star-nudge.js +91 -0
- package/dist/lib/startup/command-registry.d.ts +7 -1
- package/dist/lib/startup/command-registry.js +19 -3
- package/dist/lib/state.d.ts +2 -0
- package/dist/lib/state.js +3 -0
- package/dist/lib/subagents-registry.d.ts +2 -0
- package/dist/lib/subagents-registry.js +3 -0
- package/dist/lib/teams/parsers.js +214 -6
- package/dist/lib/teams/supervisor.d.ts +7 -0
- package/dist/lib/teams/supervisor.js +2 -1
- package/dist/lib/template.d.ts +1 -1
- package/dist/lib/template.js +1 -1
- package/dist/lib/triggers/webhook.js +36 -3
- package/dist/lib/types.d.ts +73 -0
- package/dist/lib/uninstall.d.ts +84 -0
- package/dist/lib/uninstall.js +347 -0
- package/dist/lib/usage.d.ts +13 -1
- package/dist/lib/usage.js +32 -14
- package/dist/lib/version.d.ts +40 -0
- package/dist/lib/version.js +94 -16
- package/dist/lib/versions.d.ts +27 -0
- package/dist/lib/versions.js +248 -79
- package/dist/lib/workflows.d.ts +2 -0
- package/dist/lib/workflows.js +88 -0
- package/package.json +2 -1
- package/dist/commands/daemon.d.ts +0 -10
- package/dist/commands/daemon.js +0 -121
|
@@ -8,18 +8,12 @@
|
|
|
8
8
|
* (+ per-agent symlinks). Delegates to compileRulesForProject, which is
|
|
9
9
|
* the same helper management-side `agents sync` uses.
|
|
10
10
|
*
|
|
11
|
-
* 2.
|
|
12
|
-
* into the agent's workspace-local discovery
|
|
13
|
-
* `<cwd>/.
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* uses `~/.config/amp`, antigravity uses `~/.gemini/antigravity-cli`,
|
|
18
|
-
* codex/gemini/cursor lack subagent support entirely) — they're follow-up
|
|
19
|
-
* material. NOTE: `.mcp.json` is intentionally NOT auto-symlinked from
|
|
20
|
-
* the launch path — that's a supply-chain surface (cloning a hostile
|
|
21
|
-
* repo would auto-register an attacker MCP server). Belongs to full
|
|
22
|
-
* `agents sync` with explicit opt-in, not the hot path.
|
|
11
|
+
* 2. Copy project resources from `<cwd>/.agents/{commands,skills,subagents,workflows}`
|
|
12
|
+
* into the agent's workspace-local discovery dir (`<cwd>/.claude/`,
|
|
13
|
+
* `<cwd>/.codex/`, etc.) with an ownership manifest at
|
|
14
|
+
* `<cwd>/.{agent}/.agents-managed.json`. The manifest is the only clobber
|
|
15
|
+
* authority: paths it lists are removed and refreshed, pre-existing paths it
|
|
16
|
+
* does not list are skipped as user-owned.
|
|
23
17
|
*
|
|
24
18
|
* 3. Synthesize four scope-grouped plugin marketplaces under the version's
|
|
25
19
|
* `<versionHome>/.{agent}/plugins/marketplaces/` (for plugin-capable agents):
|
|
@@ -48,8 +42,8 @@ import { supports } from './capabilities.js';
|
|
|
48
42
|
import { getEnabledExtraRepos, getExtraPluginsDir, getPluginsDir, getProjectAgentsDir, getProjectPluginsDir, getSystemPluginsDir, } from './state.js';
|
|
49
43
|
import { getVersionHomePath } from './versions.js';
|
|
50
44
|
import { toPortableKey } from './platform/index.js';
|
|
51
|
-
import { transformSubagentForClaude } from './subagents.js';
|
|
52
45
|
import { compileRulesForProject } from './rules/compile.js';
|
|
46
|
+
import { syncProjectResourcesToAgent } from './project-resources.js';
|
|
53
47
|
import { discoverPluginsInDir, hasPluginExecSurfaces, inspectPluginCapabilities } from './plugins.js';
|
|
54
48
|
import { MARKETPLACE_NAME, PROJECT_MARKETPLACE_NAME, SYSTEM_MARKETPLACE_NAME, addPluginToSettings, copyPluginToMarketplace, marketplaceNameFor, marketplaceRoot, pluginInstallDir, registerMarketplace, removePluginFromSettings, syncMarketplaceManifest, } from './plugin-marketplace.js';
|
|
55
49
|
/**
|
|
@@ -77,9 +71,12 @@ export function runLaunchSync(opts) {
|
|
|
77
71
|
// Don't fail launch on a malformed project rules.yaml.
|
|
78
72
|
}
|
|
79
73
|
// Step 2: workspace resource mirror
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
74
|
+
const projectAgentsDir = getProjectAgentsDir(opts.cwd);
|
|
75
|
+
if (projectAgentsDir) {
|
|
76
|
+
const projectResources = syncProjectResourcesToAgent(opts.agent, opts.version, projectAgentsDir);
|
|
77
|
+
result.workspaceLinks = projectResources.synced.length;
|
|
78
|
+
result.workspaceSkipped = projectResources.skipped;
|
|
79
|
+
}
|
|
83
80
|
// Step 3: scoped plugin marketplaces
|
|
84
81
|
result.marketplaces = synthesizeScopedMarketplaces(opts.agent, opts.version, opts.cwd);
|
|
85
82
|
// Touch the shim's skip-fast sentinel. Best-effort — if this fails the
|
|
@@ -117,218 +114,6 @@ function touchLaunchSentinel(agent, version, cwd) {
|
|
|
117
114
|
// best-effort
|
|
118
115
|
}
|
|
119
116
|
}
|
|
120
|
-
const CLAUDE_MIRROR_PLANS = [
|
|
121
|
-
{ srcSubdir: 'subagents', destSubdir: 'agents', mode: 'subagent-write' },
|
|
122
|
-
{ srcSubdir: 'commands', destSubdir: 'commands', mode: 'file-symlink' },
|
|
123
|
-
{ srcSubdir: 'skills', destSubdir: 'skills', mode: 'dir-symlink' },
|
|
124
|
-
];
|
|
125
|
-
/**
|
|
126
|
-
* Marker prepended-as-trailing-comment to every subagent file WE generate.
|
|
127
|
-
* It's an HTML comment — invisible to the markdown the agent reads — placed on
|
|
128
|
-
* the last line so it never disturbs the leading `---` frontmatter block.
|
|
129
|
-
*
|
|
130
|
-
* Ownership rule (the one don't-clobber decision for written, non-symlink
|
|
131
|
-
* files): we only overwrite a `.claude/agents/<name>.md` whose content carries
|
|
132
|
-
* this marker. A user-authored file (no marker) or a symlink at the dest is
|
|
133
|
-
* left untouched. A marker beats an mtime/sidecar sentinel because it travels
|
|
134
|
-
* with the file across copies and git, and needs no out-of-band state.
|
|
135
|
-
*/
|
|
136
|
-
const GENERATED_SUBAGENT_MARKER = '<!-- agents-cli:generated-subagent';
|
|
137
|
-
function mirrorWorkspaceResources(cwd, agent) {
|
|
138
|
-
// v1: claude-only. Other agents have workspace conventions we haven't
|
|
139
|
-
// mapped (amp: ~/.config/amp; antigravity: ~/.gemini/antigravity-cli;
|
|
140
|
-
// codex/gemini/cursor: no subagent support per capabilities). Adding them
|
|
141
|
-
// requires per-agent workspaceDirName + capability gates — follow-up.
|
|
142
|
-
if (agent !== 'claude')
|
|
143
|
-
return { links: 0, skipped: [] };
|
|
144
|
-
const projectAgentsDir = getProjectAgentsDir(cwd);
|
|
145
|
-
if (!projectAgentsDir)
|
|
146
|
-
return { links: 0, skipped: [] };
|
|
147
|
-
const agentWorkspaceDir = path.join(cwd, '.claude');
|
|
148
|
-
const projectAgentsResolved = (() => {
|
|
149
|
-
try {
|
|
150
|
-
return fs.realpathSync(projectAgentsDir);
|
|
151
|
-
}
|
|
152
|
-
catch {
|
|
153
|
-
return path.resolve(projectAgentsDir);
|
|
154
|
-
}
|
|
155
|
-
})();
|
|
156
|
-
let links = 0;
|
|
157
|
-
const skipped = [];
|
|
158
|
-
// Mirror subagents / commands / skills. mcp.json is intentionally excluded
|
|
159
|
-
// — see header doc, it's a supply-chain surface.
|
|
160
|
-
for (const plan of CLAUDE_MIRROR_PLANS) {
|
|
161
|
-
const srcDir = path.join(projectAgentsDir, plan.srcSubdir);
|
|
162
|
-
if (!fs.existsSync(srcDir))
|
|
163
|
-
continue;
|
|
164
|
-
const destDir = path.join(agentWorkspaceDir, plan.destSubdir);
|
|
165
|
-
fs.mkdirSync(destDir, { recursive: true });
|
|
166
|
-
// Subagents flatten N source files into one written .md — not a symlink.
|
|
167
|
-
if (plan.mode === 'subagent-write') {
|
|
168
|
-
const r = writeProjectSubagents(srcDir, destDir, cwd);
|
|
169
|
-
links += r.links;
|
|
170
|
-
skipped.push(...r.skipped);
|
|
171
|
-
continue;
|
|
172
|
-
}
|
|
173
|
-
const entries = fs.readdirSync(srcDir, { withFileTypes: true });
|
|
174
|
-
for (const entry of entries) {
|
|
175
|
-
if (entry.name.startsWith('.'))
|
|
176
|
-
continue;
|
|
177
|
-
if (plan.mode === 'dir-symlink' && !entry.isDirectory())
|
|
178
|
-
continue;
|
|
179
|
-
if (plan.mode === 'file-symlink' && !entry.isFile() && !entry.isSymbolicLink())
|
|
180
|
-
continue;
|
|
181
|
-
const srcPath = path.join(srcDir, entry.name);
|
|
182
|
-
const destPath = path.join(destDir, entry.name);
|
|
183
|
-
if (replaceWithSymlinkIfOwned(srcPath, destPath, projectAgentsResolved)) {
|
|
184
|
-
links += 1;
|
|
185
|
-
}
|
|
186
|
-
else {
|
|
187
|
-
skipped.push(path.relative(cwd, destPath));
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
return { links, skipped };
|
|
192
|
-
}
|
|
193
|
-
/**
|
|
194
|
-
* Mirror project subagents into `<cwd>/.claude/agents/`. The canonical source
|
|
195
|
-
* shape is a DIRECTORY containing AGENT.md (e.g. `.agents/subagents/probe/AGENT.md`)
|
|
196
|
-
* — confirmed by the detector (versions.ts) and lister (subagents.ts). Each
|
|
197
|
-
* such directory is flattened via transformSubagentForClaude (the exact writer
|
|
198
|
-
* the version-home sync uses) into a single `<name>.md`, then written under an
|
|
199
|
-
* ownership marker so a re-launch refreshes our file but never clobbers a
|
|
200
|
-
* user-authored one.
|
|
201
|
-
*
|
|
202
|
-
* Returns the same {links, skipped} shape the symlink path reports, so the
|
|
203
|
-
* caller's accounting is uniform across resource kinds.
|
|
204
|
-
*/
|
|
205
|
-
function writeProjectSubagents(srcDir, destDir, cwd) {
|
|
206
|
-
let links = 0;
|
|
207
|
-
const skipped = [];
|
|
208
|
-
for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) {
|
|
209
|
-
if (entry.name.startsWith('.'))
|
|
210
|
-
continue;
|
|
211
|
-
if (!entry.isDirectory())
|
|
212
|
-
continue;
|
|
213
|
-
const subagentDir = path.join(srcDir, entry.name);
|
|
214
|
-
if (!fs.existsSync(path.join(subagentDir, 'AGENT.md')))
|
|
215
|
-
continue;
|
|
216
|
-
const destPath = path.join(destDir, `${entry.name}.md`);
|
|
217
|
-
if (writeSubagentIfOwned(subagentDir, destPath)) {
|
|
218
|
-
links += 1;
|
|
219
|
-
}
|
|
220
|
-
else {
|
|
221
|
-
skipped.push(path.relative(cwd, destPath));
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
return { links, skipped };
|
|
225
|
-
}
|
|
226
|
-
/**
|
|
227
|
-
* Write a flattened subagent file at `destPath`, refusing to clobber user state.
|
|
228
|
-
*
|
|
229
|
-
* - dest missing → write fresh.
|
|
230
|
-
* - dest is our generation → overwrite (refresh; carries GENERATED_SUBAGENT_MARKER).
|
|
231
|
-
* - dest is a symlink / any
|
|
232
|
-
* non-regular file → SKIP (user state we don't own).
|
|
233
|
-
* - dest is a regular file
|
|
234
|
-
* without our marker → SKIP (hand-authored .claude/agents/<name>.md).
|
|
235
|
-
*
|
|
236
|
-
* Returns true when our file is present (written now or already current),
|
|
237
|
-
* false when we left a user-owned dest alone.
|
|
238
|
-
*/
|
|
239
|
-
function writeSubagentIfOwned(subagentDir, destPath) {
|
|
240
|
-
let existing = null;
|
|
241
|
-
let destLstat = null;
|
|
242
|
-
try {
|
|
243
|
-
destLstat = fs.lstatSync(destPath);
|
|
244
|
-
}
|
|
245
|
-
catch { /* missing — write fresh */ }
|
|
246
|
-
if (destLstat) {
|
|
247
|
-
if (!destLstat.isFile())
|
|
248
|
-
return false; // symlink/dir/etc. — user state
|
|
249
|
-
try {
|
|
250
|
-
existing = fs.readFileSync(destPath, 'utf-8');
|
|
251
|
-
}
|
|
252
|
-
catch {
|
|
253
|
-
return false;
|
|
254
|
-
}
|
|
255
|
-
if (!existing.includes(GENERATED_SUBAGENT_MARKER))
|
|
256
|
-
return false; // hand-authored
|
|
257
|
-
}
|
|
258
|
-
let body;
|
|
259
|
-
try {
|
|
260
|
-
body = transformSubagentForClaude(subagentDir);
|
|
261
|
-
}
|
|
262
|
-
catch {
|
|
263
|
-
return false; // malformed AGENT.md — don't write a broken file
|
|
264
|
-
}
|
|
265
|
-
const content = `${body}\n\n${GENERATED_SUBAGENT_MARKER} — edit .agents/subagents/${path.basename(subagentDir)}/ instead -->\n`;
|
|
266
|
-
// Skip-fast: identical content already on disk → no write (keeps mtime stable).
|
|
267
|
-
if (existing === content)
|
|
268
|
-
return true;
|
|
269
|
-
try {
|
|
270
|
-
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
271
|
-
fs.writeFileSync(destPath, content);
|
|
272
|
-
}
|
|
273
|
-
catch {
|
|
274
|
-
return false;
|
|
275
|
-
}
|
|
276
|
-
return true;
|
|
277
|
-
}
|
|
278
|
-
/**
|
|
279
|
-
* Create or refresh a symlink at `destPath` pointing at `srcPath`. Returns
|
|
280
|
-
* true if we wrote (or already had) the link, false if we skipped because
|
|
281
|
-
* the destination is user-owned (regular file, directory, or symlink pointing
|
|
282
|
-
* outside the project's `.agents/` tree, or a dangling symlink — treated as
|
|
283
|
-
* user state we don't yet understand).
|
|
284
|
-
*
|
|
285
|
-
* Skip-fast: if the destination is already a symlink resolving to the
|
|
286
|
-
* project-agents tree AND its target matches srcPath, no write happens.
|
|
287
|
-
*/
|
|
288
|
-
function replaceWithSymlinkIfOwned(srcPath, destPath, projectAgentsResolved) {
|
|
289
|
-
let destLstat = null;
|
|
290
|
-
try {
|
|
291
|
-
destLstat = fs.lstatSync(destPath);
|
|
292
|
-
}
|
|
293
|
-
catch { /* missing — write fresh */ }
|
|
294
|
-
if (destLstat) {
|
|
295
|
-
if (!destLstat.isSymbolicLink()) {
|
|
296
|
-
return false;
|
|
297
|
-
}
|
|
298
|
-
let destTargetReal = null;
|
|
299
|
-
try {
|
|
300
|
-
destTargetReal = fs.realpathSync(destPath);
|
|
301
|
-
}
|
|
302
|
-
catch { /* dangling */ }
|
|
303
|
-
// Dangling symlink → user-owned in-progress state; do not clobber.
|
|
304
|
-
if (destTargetReal === null) {
|
|
305
|
-
return false;
|
|
306
|
-
}
|
|
307
|
-
if (destTargetReal !== projectAgentsResolved && !destTargetReal.startsWith(projectAgentsResolved + path.sep)) {
|
|
308
|
-
return false;
|
|
309
|
-
}
|
|
310
|
-
let srcReal = null;
|
|
311
|
-
try {
|
|
312
|
-
srcReal = fs.realpathSync(srcPath);
|
|
313
|
-
}
|
|
314
|
-
catch { /* src vanished mid-launch */ }
|
|
315
|
-
if (srcReal !== null && destTargetReal === srcReal) {
|
|
316
|
-
return true; // already correct
|
|
317
|
-
}
|
|
318
|
-
try {
|
|
319
|
-
fs.unlinkSync(destPath);
|
|
320
|
-
}
|
|
321
|
-
catch { /* ignore */ }
|
|
322
|
-
}
|
|
323
|
-
try {
|
|
324
|
-
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
325
|
-
fs.symlinkSync(srcPath, destPath);
|
|
326
|
-
}
|
|
327
|
-
catch {
|
|
328
|
-
return false;
|
|
329
|
-
}
|
|
330
|
-
return true;
|
|
331
|
-
}
|
|
332
117
|
function makeScope(spec, pluginsDir, autoEnableExecSurfaces, precedence) {
|
|
333
118
|
return { spec, marketplaceName: marketplaceNameFor(spec), pluginsDir, autoEnableExecSurfaces, precedence };
|
|
334
119
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AgentId } from './types.js';
|
|
2
|
+
export interface ProjectResourceSyncResult {
|
|
3
|
+
synced: string[];
|
|
4
|
+
skipped: string[];
|
|
5
|
+
}
|
|
6
|
+
export declare function projectAgentRoot(projectRoot: string, agent: AgentId): string;
|
|
7
|
+
export declare function syncProjectResourcesToAgent(agent: AgentId, version: string, projectAgentsDir: string): ProjectResourceSyncResult;
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { AGENTS, agentConfigDirName } from './agents.js';
|
|
4
|
+
import { supports } from './capabilities.js';
|
|
5
|
+
import { buildCommandSkillContent, commandSkillName, readSkillSourceCommandMarker, shouldInstallCommandAsSkill } from './command-skills.js';
|
|
6
|
+
import { commandAppliesTo, parseCommandMetadata } from './commands.js';
|
|
7
|
+
import { markdownToToml } from './convert.js';
|
|
8
|
+
import { safeJoin } from './paths.js';
|
|
9
|
+
import { subagentTarget } from './subagents-registry.js';
|
|
10
|
+
import { parseSubagentFrontmatter } from './subagents.js';
|
|
11
|
+
import { syncWorkflowToVersion } from './workflows.js';
|
|
12
|
+
const MANIFEST_FILE = '.agents-managed.json';
|
|
13
|
+
const MANIFEST_VERSION = 1;
|
|
14
|
+
const COPY_IGNORE = new Set(['.DS_Store', '.git', '.gitignore', '.venv', '__pycache__', 'node_modules']);
|
|
15
|
+
export function projectAgentRoot(projectRoot, agent) {
|
|
16
|
+
return path.join(projectRoot, agentConfigDirName(agent));
|
|
17
|
+
}
|
|
18
|
+
export function syncProjectResourcesToAgent(agent, version, projectAgentsDir) {
|
|
19
|
+
const projectRoot = path.dirname(projectAgentsDir);
|
|
20
|
+
const agentRoot = projectAgentRoot(projectRoot, agent);
|
|
21
|
+
const manifest = loadProjectManifest(agentRoot);
|
|
22
|
+
const result = { synced: [], skipped: [] };
|
|
23
|
+
const next = new Set();
|
|
24
|
+
if (manifest) {
|
|
25
|
+
for (const rel of manifest.paths)
|
|
26
|
+
removeManagedPath(agentRoot, rel);
|
|
27
|
+
}
|
|
28
|
+
syncProjectCommands(agent, version, projectAgentsDir, agentRoot, result, next);
|
|
29
|
+
syncProjectSkills(agent, version, projectAgentsDir, agentRoot, result, next);
|
|
30
|
+
syncProjectSubagents(agent, version, projectAgentsDir, projectRoot, agentRoot, manifest, result, next);
|
|
31
|
+
syncProjectWorkflows(agent, version, projectAgentsDir, projectRoot, agentRoot, result, next);
|
|
32
|
+
if (next.size > 0 || manifest) {
|
|
33
|
+
writeProjectManifest(agentRoot, Array.from(next).sort());
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
function loadProjectManifest(agentRoot) {
|
|
38
|
+
try {
|
|
39
|
+
const raw = JSON.parse(fs.readFileSync(path.join(agentRoot, MANIFEST_FILE), 'utf-8'));
|
|
40
|
+
if (raw.v !== MANIFEST_VERSION || !Array.isArray(raw.paths))
|
|
41
|
+
return null;
|
|
42
|
+
if (!raw.paths.every((p) => typeof p === 'string' && p.length > 0))
|
|
43
|
+
return null;
|
|
44
|
+
return raw;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function writeProjectManifest(agentRoot, paths) {
|
|
51
|
+
fs.mkdirSync(agentRoot, { recursive: true });
|
|
52
|
+
const p = path.join(agentRoot, MANIFEST_FILE);
|
|
53
|
+
const tmp = p + '.tmp';
|
|
54
|
+
fs.writeFileSync(tmp, JSON.stringify({ v: MANIFEST_VERSION, paths }, null, 2));
|
|
55
|
+
fs.renameSync(tmp, p);
|
|
56
|
+
}
|
|
57
|
+
function removeManagedPath(agentRoot, rel) {
|
|
58
|
+
if (path.isAbsolute(rel) || rel.includes('..'))
|
|
59
|
+
return;
|
|
60
|
+
const target = path.resolve(agentRoot, rel);
|
|
61
|
+
const root = path.resolve(agentRoot);
|
|
62
|
+
if (target !== root && !target.startsWith(root + path.sep))
|
|
63
|
+
return;
|
|
64
|
+
removePath(target);
|
|
65
|
+
}
|
|
66
|
+
function removePath(p) {
|
|
67
|
+
try {
|
|
68
|
+
const st = fs.lstatSync(p);
|
|
69
|
+
if (st.isSymbolicLink() || st.isFile())
|
|
70
|
+
fs.unlinkSync(p);
|
|
71
|
+
else if (st.isDirectory())
|
|
72
|
+
fs.rmSync(p, { recursive: true, force: true });
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// already absent
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function pathExists(p) {
|
|
79
|
+
try {
|
|
80
|
+
fs.lstatSync(p);
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function copyDir(src, dest) {
|
|
88
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
89
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
90
|
+
if (entry.isSymbolicLink() || COPY_IGNORE.has(entry.name))
|
|
91
|
+
continue;
|
|
92
|
+
const s = safeJoin(src, entry.name);
|
|
93
|
+
const d = safeJoin(dest, entry.name);
|
|
94
|
+
if (entry.isDirectory())
|
|
95
|
+
copyDir(s, d);
|
|
96
|
+
else if (entry.isFile())
|
|
97
|
+
fs.copyFileSync(s, d);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function projectEntries(projectAgentsDir, kind) {
|
|
101
|
+
const dir = path.join(projectAgentsDir, kind);
|
|
102
|
+
try {
|
|
103
|
+
return fs.readdirSync(dir, { withFileTypes: true }).filter((entry) => !entry.name.startsWith('.'));
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return [];
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function record(kind, name, relPaths, result, manifestPaths) {
|
|
110
|
+
result.synced.push(`${kind}/${name}`);
|
|
111
|
+
for (const rel of relPaths)
|
|
112
|
+
manifestPaths.add(rel);
|
|
113
|
+
}
|
|
114
|
+
function skip(dest, projectRoot, result) {
|
|
115
|
+
const rel = path.relative(projectRoot, dest);
|
|
116
|
+
result.skipped.push(rel);
|
|
117
|
+
console.warn(`Skipping project resource target ${rel}: already exists and is user-owned`);
|
|
118
|
+
}
|
|
119
|
+
function syncProjectCommands(agent, version, projectAgentsDir, agentRoot, result, manifestPaths) {
|
|
120
|
+
const cfg = AGENTS[agent];
|
|
121
|
+
const commandsAsSkills = shouldInstallCommandAsSkill(agent, version);
|
|
122
|
+
const supportsCommands = supports(agent, 'commands', version).ok;
|
|
123
|
+
if (!commandsAsSkills && !supportsCommands)
|
|
124
|
+
return;
|
|
125
|
+
const projectRoot = path.dirname(projectAgentsDir);
|
|
126
|
+
for (const entry of projectEntries(projectAgentsDir, 'commands')) {
|
|
127
|
+
if (!entry.isFile() || !entry.name.endsWith('.md'))
|
|
128
|
+
continue;
|
|
129
|
+
const name = entry.name.slice(0, -'.md'.length);
|
|
130
|
+
const srcFile = path.join(projectAgentsDir, 'commands', entry.name);
|
|
131
|
+
const metadata = parseCommandMetadata(srcFile);
|
|
132
|
+
if (!commandAppliesTo(agent, version, metadata).ok)
|
|
133
|
+
continue;
|
|
134
|
+
if (commandsAsSkills) {
|
|
135
|
+
const sourceMarker = readSkillSourceCommandMarker(name, [path.join(projectAgentsDir, 'skills')]);
|
|
136
|
+
if (pathExists(path.join(projectAgentsDir, 'skills', name)) && sourceMarker !== name)
|
|
137
|
+
continue;
|
|
138
|
+
const skillName = commandSkillName(name);
|
|
139
|
+
const rel = path.join('skills', skillName);
|
|
140
|
+
const destDir = path.join(agentRoot, rel);
|
|
141
|
+
if (pathExists(destDir)) {
|
|
142
|
+
skip(destDir, projectRoot, result);
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
146
|
+
fs.writeFileSync(path.join(destDir, 'SKILL.md'), buildCommandSkillContent(name, srcFile), 'utf-8');
|
|
147
|
+
record('commands', name, [rel], result, manifestPaths);
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
const ext = cfg.format === 'toml' ? '.toml' : '.md';
|
|
151
|
+
const rel = path.join(cfg.commandsSubdir, `${name}${ext}`);
|
|
152
|
+
const destFile = path.join(agentRoot, rel);
|
|
153
|
+
if (pathExists(destFile)) {
|
|
154
|
+
skip(destFile, projectRoot, result);
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
fs.mkdirSync(path.dirname(destFile), { recursive: true });
|
|
158
|
+
if (cfg.format === 'toml') {
|
|
159
|
+
fs.writeFileSync(destFile, markdownToToml(name, fs.readFileSync(srcFile, 'utf-8')), 'utf-8');
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
fs.copyFileSync(srcFile, destFile);
|
|
163
|
+
}
|
|
164
|
+
record('commands', name, [rel], result, manifestPaths);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function syncProjectSkills(agent, version, projectAgentsDir, agentRoot, result, manifestPaths) {
|
|
168
|
+
if (!supports(agent, 'skills', version).ok)
|
|
169
|
+
return;
|
|
170
|
+
const projectRoot = path.dirname(projectAgentsDir);
|
|
171
|
+
for (const entry of projectEntries(projectAgentsDir, 'skills')) {
|
|
172
|
+
if (!entry.isDirectory())
|
|
173
|
+
continue;
|
|
174
|
+
const srcDir = path.join(projectAgentsDir, 'skills', entry.name);
|
|
175
|
+
if (!fs.existsSync(path.join(srcDir, 'SKILL.md')))
|
|
176
|
+
continue;
|
|
177
|
+
const rel = path.join('skills', entry.name);
|
|
178
|
+
const destDir = path.join(agentRoot, rel);
|
|
179
|
+
if (pathExists(destDir)) {
|
|
180
|
+
skip(destDir, projectRoot, result);
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
copyDir(srcDir, destDir);
|
|
184
|
+
record('skills', entry.name, [rel], result, manifestPaths);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
function readProjectSubagents(projectAgentsDir) {
|
|
188
|
+
const map = new Map();
|
|
189
|
+
for (const entry of projectEntries(projectAgentsDir, 'subagents')) {
|
|
190
|
+
if (!entry.isDirectory())
|
|
191
|
+
continue;
|
|
192
|
+
const dir = path.join(projectAgentsDir, 'subagents', entry.name);
|
|
193
|
+
const agentMd = path.join(dir, 'AGENT.md');
|
|
194
|
+
if (!fs.existsSync(agentMd))
|
|
195
|
+
continue;
|
|
196
|
+
const frontmatter = parseSubagentFrontmatter(agentMd);
|
|
197
|
+
if (!frontmatter)
|
|
198
|
+
continue;
|
|
199
|
+
const files = fs.readdirSync(dir).filter((f) => f.endsWith('.md')).sort();
|
|
200
|
+
map.set(entry.name, { name: entry.name, path: dir, files, frontmatter });
|
|
201
|
+
}
|
|
202
|
+
return map;
|
|
203
|
+
}
|
|
204
|
+
function syncProjectSubagents(agent, version, projectAgentsDir, projectRoot, agentRoot, manifest, result, manifestPaths) {
|
|
205
|
+
if (!supports(agent, 'subagents', version).ok)
|
|
206
|
+
return;
|
|
207
|
+
const target = subagentTarget(agent);
|
|
208
|
+
if (!target)
|
|
209
|
+
return;
|
|
210
|
+
const all = readProjectSubagents(projectAgentsDir);
|
|
211
|
+
const dir = target.dir(projectRoot);
|
|
212
|
+
const targetDirRel = path.relative(agentRoot, dir);
|
|
213
|
+
const hadManagedTarget = manifest?.paths.some((rel) => rel === targetDirRel || rel.startsWith(targetDirRel + path.sep)) ?? false;
|
|
214
|
+
for (const sub of all.values()) {
|
|
215
|
+
const occupied = target.occupied(dir, sub.name);
|
|
216
|
+
const existing = occupied.find((entry) => pathExists(entry.path));
|
|
217
|
+
if (existing) {
|
|
218
|
+
skip(existing.path, projectRoot, result);
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
try {
|
|
222
|
+
target.write(dir, sub);
|
|
223
|
+
record('subagents', sub.name, occupied.map((entry) => path.relative(agentRoot, entry.path)), result, manifestPaths);
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
// Malformed source or unsupported transform; skip this item.
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
const syncedNames = result.synced
|
|
230
|
+
.filter((name) => name.startsWith('subagents/'))
|
|
231
|
+
.map((name) => name.slice('subagents/'.length))
|
|
232
|
+
.filter((name) => all.has(name))
|
|
233
|
+
.map((name) => all.get(name));
|
|
234
|
+
if (target.finalize && (syncedNames.length > 0 || hadManagedTarget)) {
|
|
235
|
+
target.finalize(dir, syncedNames);
|
|
236
|
+
for (const entry of target.finalizeOccupied?.(dir) ?? []) {
|
|
237
|
+
manifestPaths.add(path.relative(agentRoot, entry.path));
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
function workflowManagedRelPaths(agent, projectRoot, name, workflowDir) {
|
|
242
|
+
if (agent === 'kimi')
|
|
243
|
+
return [path.join('.kimi-code', 'skills', name)];
|
|
244
|
+
if (agent === 'goose') {
|
|
245
|
+
const rels = [path.join('.config', 'goose', 'recipes', `${name}.yaml`)];
|
|
246
|
+
const subagentsDir = path.join(workflowDir, 'subagents');
|
|
247
|
+
let hasSubagents = false;
|
|
248
|
+
try {
|
|
249
|
+
hasSubagents = fs.readdirSync(subagentsDir).some((f) => f.endsWith('.md'));
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
hasSubagents = false;
|
|
253
|
+
}
|
|
254
|
+
if (hasSubagents)
|
|
255
|
+
rels.push(path.join('.config', 'goose', 'recipes', `${name}.subrecipes`));
|
|
256
|
+
return rels;
|
|
257
|
+
}
|
|
258
|
+
if (agent === 'openclaw')
|
|
259
|
+
return [path.join('.openclaw', 'workflows', `${name}.lobster`)];
|
|
260
|
+
return [path.join(agentConfigDirName(agent), 'workflows', name)];
|
|
261
|
+
}
|
|
262
|
+
function syncProjectWorkflows(agent, version, projectAgentsDir, projectRoot, agentRoot, result, manifestPaths) {
|
|
263
|
+
if (!supports(agent, 'workflows', version).ok)
|
|
264
|
+
return;
|
|
265
|
+
if (agent === 'antigravity')
|
|
266
|
+
return;
|
|
267
|
+
for (const entry of projectEntries(projectAgentsDir, 'workflows')) {
|
|
268
|
+
if (!entry.isDirectory())
|
|
269
|
+
continue;
|
|
270
|
+
const workflowDir = path.join(projectAgentsDir, 'workflows', entry.name);
|
|
271
|
+
if (!fs.existsSync(path.join(workflowDir, 'WORKFLOW.md')))
|
|
272
|
+
continue;
|
|
273
|
+
const rels = workflowManagedRelPaths(agent, projectRoot, entry.name, workflowDir);
|
|
274
|
+
const existing = rels.map((rel) => path.join(projectRoot, rel)).find((dest) => pathExists(dest));
|
|
275
|
+
if (existing) {
|
|
276
|
+
skip(existing, projectRoot, result);
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
let success = false;
|
|
280
|
+
if (agent === 'kimi' || agent === 'goose' || agent === 'openclaw') {
|
|
281
|
+
success = syncWorkflowToVersion(workflowDir, entry.name, agent, projectRoot).success;
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
copyDir(workflowDir, path.join(agentRoot, 'workflows', entry.name));
|
|
285
|
+
success = true;
|
|
286
|
+
}
|
|
287
|
+
if (success) {
|
|
288
|
+
record('workflows', entry.name, rels.map((rel) => path.relative(agentRoot, path.join(projectRoot, rel))), result, manifestPaths);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
package/dist/lib/pty-client.d.ts
CHANGED
|
@@ -4,6 +4,14 @@
|
|
|
4
4
|
* Thin client that connects to the PTY sidecar server over unix socket.
|
|
5
5
|
* Each call opens a connection, sends a JSON request, reads the JSON response, and closes.
|
|
6
6
|
*/
|
|
7
|
+
export interface ServerSpawnArgs {
|
|
8
|
+
bin: string;
|
|
9
|
+
args: string[];
|
|
10
|
+
}
|
|
11
|
+
export interface ServerExit {
|
|
12
|
+
code: number | null;
|
|
13
|
+
signal: NodeJS.Signals | null;
|
|
14
|
+
}
|
|
7
15
|
/** JSON response envelope from the PTY server. */
|
|
8
16
|
export interface PtyResponse {
|
|
9
17
|
ok: boolean;
|
|
@@ -15,6 +23,25 @@ export interface PtyResponse {
|
|
|
15
23
|
* Auto-starts the server if not running.
|
|
16
24
|
*/
|
|
17
25
|
export declare function ptyRequest(action: string, id?: string, params?: Record<string, any>): Promise<PtyResponse>;
|
|
26
|
+
export declare function getServerSpawnArgs(options?: {
|
|
27
|
+
isStandaloneExecutable?: boolean;
|
|
28
|
+
/** Physical binary path (test seam). Defaults to `process.execPath`. */
|
|
29
|
+
execPath?: string;
|
|
30
|
+
/** Locate a real `node` (test seam). Defaults to `which/where node`. */
|
|
31
|
+
resolveNode?: () => string | undefined;
|
|
32
|
+
/** File-existence check (test seam). Defaults to `fs.existsSync`. */
|
|
33
|
+
fileExists?: (p: string) => boolean;
|
|
34
|
+
}): ServerSpawnArgs;
|
|
35
|
+
export declare function isBunStandaloneExecutable(moduleUrl?: string): boolean;
|
|
36
|
+
export declare function readRecentLogLines(logPath: string, maxLines?: number): string[];
|
|
37
|
+
export declare function buildPtyStartFailureMessage(details: {
|
|
38
|
+
timeoutMs: number;
|
|
39
|
+
spawn: ServerSpawnArgs;
|
|
40
|
+
exit?: ServerExit;
|
|
41
|
+
spawnError?: Error;
|
|
42
|
+
lastReadinessError?: Error;
|
|
43
|
+
logPath: string;
|
|
44
|
+
}): string;
|
|
18
45
|
/**
|
|
19
46
|
* Parse escape sequences in user input strings.
|
|
20
47
|
* Handles: \n \r \t \e \xHH \\
|