@camerontaylor/paseo-cli 0.11.0-beta.3.fork.1 → 0.11.1-fork.1
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/dist/commands/agent/attach.js +5 -23
- package/dist/commands/agent/delete.js +20 -12
- package/dist/commands/agent/logs.js +3 -9
- package/dist/commands/daemon/restart.js +2 -1
- package/dist/commands/plugin/index.js +16 -4
- package/dist/commands/plugin/scaffold.js +35 -1
- package/dist/commands/schedule/update.d.ts +2 -4
- package/dist/commands/schedule/update.js +4 -4
- package/dist/run.js +12 -0
- package/dist/utils/wait-for-stop.d.ts +2 -0
- package/dist/utils/wait-for-stop.js +24 -0
- package/package.json +4 -4
|
@@ -4,6 +4,7 @@ export function addAttachOptions(cmd) {
|
|
|
4
4
|
.argument("<id>", "Agent ID (or prefix)");
|
|
5
5
|
}
|
|
6
6
|
import { connectToDaemon } from "../../utils/client.js";
|
|
7
|
+
import { waitForStop } from "../../utils/wait-for-stop.js";
|
|
7
8
|
import { fetchProjectedTimelineItems, LIVE_HISTORY_FETCH_TIMEOUT_MS, } from "../../utils/timeline.js";
|
|
8
9
|
/**
|
|
9
10
|
* Format and print a timeline item to the terminal
|
|
@@ -123,29 +124,10 @@ export async function runAttachCommand(id, options, _command) {
|
|
|
123
124
|
});
|
|
124
125
|
await unsubscribe.ready;
|
|
125
126
|
console.log(`Attached to agent ${resolvedId.substring(0, 7)}.`);
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
return;
|
|
131
|
-
detached = true;
|
|
132
|
-
console.log("\n\nDetaching from agent...");
|
|
133
|
-
unsubscribe();
|
|
134
|
-
client
|
|
135
|
-
.close()
|
|
136
|
-
.then(() => {
|
|
137
|
-
process.exit(0);
|
|
138
|
-
})
|
|
139
|
-
.catch(() => {
|
|
140
|
-
process.exit(1);
|
|
141
|
-
});
|
|
142
|
-
};
|
|
143
|
-
process.on("SIGINT", detach);
|
|
144
|
-
process.on("SIGTERM", detach);
|
|
145
|
-
// Keep the process alive
|
|
146
|
-
await new Promise(() => {
|
|
147
|
-
// Wait indefinitely until interrupted
|
|
148
|
-
});
|
|
127
|
+
await waitForStop();
|
|
128
|
+
console.log("\n\nDetaching from agent...");
|
|
129
|
+
unsubscribe();
|
|
130
|
+
await client.close();
|
|
149
131
|
}
|
|
150
132
|
catch (err) {
|
|
151
133
|
await client.close().catch(() => { });
|
|
@@ -11,6 +11,19 @@ export const deleteSchema = {
|
|
|
11
11
|
idField: (item) => item.agentIds.join("\n"),
|
|
12
12
|
columns: [{ header: "DELETED", field: "deletedCount" }],
|
|
13
13
|
};
|
|
14
|
+
// History includes archived agents and agents whose provider is unavailable.
|
|
15
|
+
async function fetchHistoryAgents(client) {
|
|
16
|
+
const agents = [];
|
|
17
|
+
let cursor;
|
|
18
|
+
do {
|
|
19
|
+
const payload = await client.fetchAgentHistory({
|
|
20
|
+
page: { limit: 200, ...(cursor ? { cursor } : {}) },
|
|
21
|
+
});
|
|
22
|
+
agents.push(...payload.entries.map((entry) => entry.agent));
|
|
23
|
+
cursor = payload.pageInfo.nextCursor ?? undefined;
|
|
24
|
+
} while (cursor);
|
|
25
|
+
return agents;
|
|
26
|
+
}
|
|
14
27
|
export async function runDeleteCommand(id, options, _command) {
|
|
15
28
|
if (!id && !options.all && !options.cwd) {
|
|
16
29
|
const error = {
|
|
@@ -22,20 +35,15 @@ export async function runDeleteCommand(id, options, _command) {
|
|
|
22
35
|
}
|
|
23
36
|
const client = await connectToDaemon({ target: options.daemonTarget });
|
|
24
37
|
try {
|
|
25
|
-
const fetchPayload = await client.fetchAgents({ filter: { includeArchived: true } });
|
|
26
|
-
let agents = fetchPayload.entries.map((entry) => entry.agent);
|
|
27
38
|
const deletedIds = [];
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return false;
|
|
35
|
-
return isSameOrDescendantPath(options.cwd, a.cwd);
|
|
36
|
-
});
|
|
39
|
+
let agents;
|
|
40
|
+
if (options.all || options.cwd) {
|
|
41
|
+
agents = await fetchHistoryAgents(client);
|
|
42
|
+
if (!options.all) {
|
|
43
|
+
agents = agents.filter((a) => isSameOrDescendantPath(options.cwd, a.cwd));
|
|
44
|
+
}
|
|
37
45
|
}
|
|
38
|
-
else
|
|
46
|
+
else {
|
|
39
47
|
const fetchResult = await client.fetchAgent({ agentId: id });
|
|
40
48
|
if (!fetchResult) {
|
|
41
49
|
const error = {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { connectToDaemon } from "../../utils/client.js";
|
|
2
|
+
import { waitForStop } from "../../utils/wait-for-stop.js";
|
|
2
3
|
import { fetchProjectedTimelineItems, LIVE_HISTORY_FETCH_TIMEOUT_MS, } from "../../utils/timeline.js";
|
|
3
4
|
import { curateAgentActivity } from "@camerontaylor/paseo-server/agent-activity";
|
|
4
5
|
export function addLogsOptions(cmd) {
|
|
@@ -166,15 +167,8 @@ async function runFollowMode(client, agentId, options) {
|
|
|
166
167
|
});
|
|
167
168
|
await unsubscribe.ready;
|
|
168
169
|
console.log(`\n--- Following logs (${tailLabel}; Ctrl+C to stop) ---\n`);
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const cleanup = () => {
|
|
172
|
-
unsubscribe();
|
|
173
|
-
resolve();
|
|
174
|
-
};
|
|
175
|
-
process.on("SIGINT", cleanup);
|
|
176
|
-
process.on("SIGTERM", cleanup);
|
|
177
|
-
});
|
|
170
|
+
await waitForStop();
|
|
171
|
+
unsubscribe();
|
|
178
172
|
await client.close();
|
|
179
173
|
}
|
|
180
174
|
//# sourceMappingURL=logs.js.map
|
|
@@ -59,7 +59,8 @@ export async function runRestartCommand(options, _command) {
|
|
|
59
59
|
try {
|
|
60
60
|
if (replacement.getLastServerInfoMessage()?.serverId !== serverId)
|
|
61
61
|
throw new Error("Connected peer identity changed");
|
|
62
|
-
|
|
62
|
+
// Status probes every provider, so it can take seconds on a healthy worker.
|
|
63
|
+
const status = await replacement.getDaemonStatus({ timeout: remaining() });
|
|
63
64
|
if (status.pid !== workerPid) {
|
|
64
65
|
await checkSupervisor();
|
|
65
66
|
return {
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { expandUserPath } from "../../classify.js";
|
|
1
3
|
import { createInterface } from "node:readline/promises";
|
|
2
4
|
import { reviewPluginUpdates } from "./update.js";
|
|
3
5
|
import { Command } from "commander";
|
|
4
|
-
import { formatPluginSourceReference, formatPluginIdentity, } from "@camerontaylor/paseo-protocol/plugin-source-reference";
|
|
6
|
+
import { formatPluginSourceReference, parsePluginSourceReference, formatPluginIdentity, } from "@camerontaylor/paseo-protocol/plugin-source-reference";
|
|
5
7
|
import { withOutput } from "../../output/index.js";
|
|
6
8
|
import { addJsonAndDaemonHostOptions, addJsonOption } from "../../utils/command-options.js";
|
|
7
9
|
import { scaffoldPluginDirectory } from "./scaffold.js";
|
|
@@ -73,7 +75,17 @@ export async function runPluginLogsCommand(pluginId, options, _command) {
|
|
|
73
75
|
}
|
|
74
76
|
export async function runPluginInstallCommand(source, options, _command) {
|
|
75
77
|
process.stderr.write("Trusting plugin code: server code and preparation commands run unsandboxed on the daemon host; client code runs inside Paseo. Dependencies and future updates are part of the codebase you trust.\n");
|
|
76
|
-
const
|
|
78
|
+
const reference = parsePluginSourceReference(source);
|
|
79
|
+
let resolvedSource = source;
|
|
80
|
+
if (reference.kind === "directory") {
|
|
81
|
+
const directory = expandUserPath(reference.source);
|
|
82
|
+
// An absolute path can target a daemon on a different operating system.
|
|
83
|
+
const absoluteDirectory = path.posix.isAbsolute(directory) || path.win32.isAbsolute(directory)
|
|
84
|
+
? directory
|
|
85
|
+
: path.resolve(directory);
|
|
86
|
+
resolvedSource = formatPluginSourceReference(absoluteDirectory, reference.pluginPath);
|
|
87
|
+
}
|
|
88
|
+
const sourceReference = formatPluginSourceReference(resolvedSource, options.path);
|
|
77
89
|
const data = await withPluginSourceClient(options.daemonTarget, (client) => client.installPluginSource({
|
|
78
90
|
source: sourceReference,
|
|
79
91
|
...(options.id ? { id: options.id } : {}),
|
|
@@ -131,8 +143,8 @@ export function createPluginCommand() {
|
|
|
131
143
|
addJsonAndDaemonHostOptions(plugin
|
|
132
144
|
.command("install")
|
|
133
145
|
.alias("add")
|
|
134
|
-
.description("Trust and install a plugin from a directory, Git repository, or npm package")
|
|
135
|
-
.argument("<source>", "
|
|
146
|
+
.description("Trust and install a plugin from a registry, directory, Git repository, or npm package")
|
|
147
|
+
.argument("<source>", "Registry owner/slug or host/owner/slug, host directory, explicit git:owner/repo or Git URL, or npm: source")
|
|
136
148
|
.option("--id <id>", "Runtime plugin ID (defaults to paseo-plugin.json id)")
|
|
137
149
|
.option("--ref <ref>", "Git branch, tag, or commit")
|
|
138
150
|
.option("--path <path>", "Legacy form of the :plugin/path source suffix")).action(withOutput(runPluginInstallCommand));
|
|
@@ -2,6 +2,33 @@ import { mkdir, readdir, writeFile } from "node:fs/promises";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { PluginIdSchema } from "@camerontaylor/paseo-protocol/messages";
|
|
4
4
|
import { resolveCliVersion } from "../../version.js";
|
|
5
|
+
const OVERVIEW = `<!--
|
|
6
|
+
OVERVIEW.md is required to list your plugin in the registry. Keep it beside
|
|
7
|
+
paseo-plugin.json in your repository at the pinned commit and in your npm package.
|
|
8
|
+
Replace this comment with useful facts before publishing.
|
|
9
|
+
|
|
10
|
+
This overview is for people choosing a plugin in Paseo, on the plugin page under
|
|
11
|
+
its install command. README assumes a GitHub audience and includes technical
|
|
12
|
+
details, installation instructions, and badges. Long, AI-generated READMEs leave
|
|
13
|
+
people reading past that material to understand what the plugin does.
|
|
14
|
+
|
|
15
|
+
Write these parts in order, including only what helps someone decide to install:
|
|
16
|
+
|
|
17
|
+
1. Describe what your plugin is and does in plain terms first.
|
|
18
|
+
2. Explain how it works only when it is not obvious.
|
|
19
|
+
3. Explain setup when needed: settings, accounts, tokens, providers, tools, or
|
|
20
|
+
other plugins. Include applicable daemon version and operating system
|
|
21
|
+
requirements. Setup guidance is allowed; installation instructions are not.
|
|
22
|
+
4. Explain capabilities and settings worth understanding, what each option does,
|
|
23
|
+
what the plugin reads or sends and where, permissions, and known limits.
|
|
24
|
+
|
|
25
|
+
Length follows complexity; a theme needs one paragraph. Choose headings only
|
|
26
|
+
when they help. Use sentence case and plain factual language, with no em dashes.
|
|
27
|
+
Omit installation commands, badges, changelog, contributing or license sections,
|
|
28
|
+
marketing, and unsupported claims. Avoid implementation filler such as empty
|
|
29
|
+
cleanup functions, theme-token field lists, or lists of absent features.
|
|
30
|
+
-->
|
|
31
|
+
`;
|
|
5
32
|
const TSCONFIG = {
|
|
6
33
|
compilerOptions: {
|
|
7
34
|
target: "ES2020",
|
|
@@ -138,6 +165,7 @@ export async function scaffoldPluginDirectory(targetDirectory, requestedId) {
|
|
|
138
165
|
version: "0.0.0",
|
|
139
166
|
files: [
|
|
140
167
|
"paseo-plugin.json",
|
|
168
|
+
"OVERVIEW.md",
|
|
141
169
|
"index.client.ts",
|
|
142
170
|
"index.client.tsx",
|
|
143
171
|
"index.server.ts",
|
|
@@ -160,8 +188,14 @@ export async function scaffoldPluginDirectory(targetDirectory, requestedId) {
|
|
|
160
188
|
const files = new Map([
|
|
161
189
|
[
|
|
162
190
|
"paseo-plugin.json",
|
|
163
|
-
`${JSON.stringify({
|
|
191
|
+
`${JSON.stringify({
|
|
192
|
+
id,
|
|
193
|
+
$comment: 'Add "name": "My plugin" to set a display name. Add "icon": "assets/icon.png" for a PNG inside the package. Add image or video paths (relative to this manifest) or HTTPS URLs to media. Include local assets in package.json files.',
|
|
194
|
+
media: [],
|
|
195
|
+
requirements: { paseo: `>=${version}` },
|
|
196
|
+
}, null, 2)}\n`,
|
|
164
197
|
],
|
|
198
|
+
["OVERVIEW.md", OVERVIEW],
|
|
165
199
|
["package.json", `${JSON.stringify(packageJson, null, 2)}\n`],
|
|
166
200
|
["tsconfig.json", `${JSON.stringify(TSCONFIG, null, 2)}\n`],
|
|
167
201
|
["index.client.tsx", CLIENT_ENTRY],
|
|
@@ -12,10 +12,8 @@ export interface ScheduleUpdateOptions extends ScheduleCommandOptions {
|
|
|
12
12
|
model?: string;
|
|
13
13
|
mode?: string;
|
|
14
14
|
cwd?: string;
|
|
15
|
-
maxRuns?: string;
|
|
16
|
-
|
|
17
|
-
expiresIn?: string;
|
|
18
|
-
noExpiresIn?: boolean;
|
|
15
|
+
maxRuns?: string | false;
|
|
16
|
+
expiresIn?: string | false;
|
|
19
17
|
}
|
|
20
18
|
export declare function runUpdateCommand(id: string, options: ScheduleUpdateOptions, _command: Command): Promise<ListResult<ScheduleInspectRow>>;
|
|
21
19
|
//# sourceMappingURL=update.d.ts.map
|
|
@@ -12,10 +12,10 @@ export async function runUpdateCommand(id, options, _command) {
|
|
|
12
12
|
model: options.model,
|
|
13
13
|
mode: options.mode,
|
|
14
14
|
cwd: options.cwd,
|
|
15
|
-
maxRuns: options.maxRuns,
|
|
16
|
-
expiresIn: options.expiresIn,
|
|
17
|
-
clearMaxRuns: options.
|
|
18
|
-
clearExpires: options.
|
|
15
|
+
maxRuns: options.maxRuns === false ? undefined : options.maxRuns,
|
|
16
|
+
expiresIn: options.expiresIn === false ? undefined : options.expiresIn,
|
|
17
|
+
clearMaxRuns: options.maxRuns === false,
|
|
18
|
+
clearExpires: options.expiresIn === false,
|
|
19
19
|
});
|
|
20
20
|
const { client } = await connectScheduleClient(options.daemonTarget);
|
|
21
21
|
try {
|
package/dist/run.js
CHANGED
|
@@ -24,7 +24,19 @@ export function createCliParseArgv(input) {
|
|
|
24
24
|
}
|
|
25
25
|
return [...nodeArgv, ...cliArgv];
|
|
26
26
|
}
|
|
27
|
+
// The program that started the CLI can close its end of stdout before the output
|
|
28
|
+
// is written, as `paseo ls | head -1` or a launcher that discards output does.
|
|
29
|
+
// Drop the output nobody reads and let the command finish, instead of surfacing
|
|
30
|
+
// the failed write as an uncaught error.
|
|
31
|
+
function ignoreClosedStdout(error) {
|
|
32
|
+
if (error.code !== "EPIPE") {
|
|
33
|
+
throw error;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
27
36
|
export async function runCli(argv, options = {}) {
|
|
37
|
+
if (!process.stdout.listeners("error").includes(ignoreClosedStdout)) {
|
|
38
|
+
process.stdout.on("error", ignoreClosedStdout);
|
|
39
|
+
}
|
|
28
40
|
const parseArgv = createCliParseArgv({
|
|
29
41
|
argv,
|
|
30
42
|
cwd: options.cwd ?? process.cwd(),
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Commands that stream until stopped (`paseo agent logs --follow`, `paseo agent
|
|
2
|
+
// attach`) stop on Ctrl+C, a termination signal, or when the program reading
|
|
3
|
+
// their output closes its end, as `| head` does. A closed reader shows up as
|
|
4
|
+
// EPIPE on the next write to stdout, so the command stops at its next line of
|
|
5
|
+
// output.
|
|
6
|
+
export function waitForStop() {
|
|
7
|
+
return new Promise((resolve) => {
|
|
8
|
+
const stop = () => {
|
|
9
|
+
process.off("SIGINT", stop);
|
|
10
|
+
process.off("SIGTERM", stop);
|
|
11
|
+
process.stdout.off("error", stopOnClosedStdout);
|
|
12
|
+
resolve();
|
|
13
|
+
};
|
|
14
|
+
const stopOnClosedStdout = (error) => {
|
|
15
|
+
if (error.code === "EPIPE") {
|
|
16
|
+
stop();
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
process.on("SIGINT", stop);
|
|
20
|
+
process.on("SIGTERM", stop);
|
|
21
|
+
process.stdout.on("error", stopOnClosedStdout);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=wait-for-stop.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camerontaylor/paseo-cli",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.1-fork.1",
|
|
4
4
|
"description": "Paseo CLI - control your AI coding agents from the command line",
|
|
5
5
|
"bin": {
|
|
6
6
|
"paseo": "bin/paseo"
|
|
@@ -28,9 +28,9 @@
|
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@clack/prompts": "^1.0.0",
|
|
31
|
-
"@camerontaylor/paseo-client": "0.11.
|
|
32
|
-
"@camerontaylor/paseo-protocol": "0.11.
|
|
33
|
-
"@camerontaylor/paseo-server": "0.11.
|
|
31
|
+
"@camerontaylor/paseo-client": "0.11.1-fork.1",
|
|
32
|
+
"@camerontaylor/paseo-protocol": "0.11.1-fork.1",
|
|
33
|
+
"@camerontaylor/paseo-server": "0.11.1-fork.1",
|
|
34
34
|
"chalk": "^5.3.0",
|
|
35
35
|
"commander": "^12.0.0",
|
|
36
36
|
"mime-types": "^2.1.35",
|