@camerontaylor/paseo-cli 0.8.0-fork.3 → 0.9.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/cli.js CHANGED
@@ -102,6 +102,25 @@ export function createCli() {
102
102
  // COMPAT(worktreeCli): legacy command alias added before workspace was the product unit.
103
103
  // Added in v0.2.0; remove after 2027-01-17.
104
104
  program.addCommand(createWorktreeCommand(), { hidden: true });
105
+ // Stop root parsing at the command so `plugin update --version` belongs to update.
106
+ // Keep global options available after a command, as they were before positional parsing.
107
+ program.enablePositionalOptions();
108
+ for (const command of program.commands) {
109
+ for (const option of program.options) {
110
+ if (option.long === "--version")
111
+ continue;
112
+ if (!command.options.some((local) => local.long === option.long))
113
+ command.addOption(option);
114
+ if (option.long === "--home" || option.long === "--host")
115
+ continue;
116
+ command.on(`option:${option.name()}`, () => {
117
+ const key = option.attributeName();
118
+ program.setOptionValueWithSource(key, command.opts()[key], "cli");
119
+ });
120
+ }
121
+ if (command.name() !== "plugin")
122
+ command.version(VERSION, "-v, --version", "output the version number");
123
+ }
105
124
  const enforceSelectorDuplicates = (command) => {
106
125
  for (const option of command.options) {
107
126
  if (option.long !== "--home" && option.long !== "--host")
@@ -1,6 +1,6 @@
1
1
  import { connectToDaemon } from "../../utils/client.js";
2
2
  import { fetchProjectedTimelineItems, LIVE_HISTORY_FETCH_TIMEOUT_MS, } from "../../utils/timeline.js";
3
- import { curateAgentActivity } from "@camerontaylor/paseo-server";
3
+ import { curateAgentActivity } from "@camerontaylor/paseo-server/agent-activity";
4
4
  export function addLogsOptions(cmd) {
5
5
  return cmd
6
6
  .description("View agent activity/timeline")
@@ -1,5 +1,5 @@
1
1
  import { Option } from "commander";
2
- import { getStructuredAgentResponse, StructuredAgentResponseError } from "@camerontaylor/paseo-server";
2
+ import { getStructuredAgentResponse, StructuredAgentResponseError, } from "@camerontaylor/paseo-server/agent-response";
3
3
  import { connectToDaemon } from "../../utils/client.js";
4
4
  import { readFileSync } from "node:fs";
5
5
  import { resolve } from "node:path";
@@ -1,6 +1,7 @@
1
1
  import { addLocalDaemonOptions } from "../../utils/command-options.js";
2
2
  import { Command } from "commander";
3
- import { readDaemonInstance, readPersistedConfig, getPersistedConfigValue, editPersistedConfig, } from "@camerontaylor/paseo-server";
3
+ import { readDaemonInstance } from "@camerontaylor/paseo-server/daemon-control";
4
+ import { readPersistedConfig, getPersistedConfigValue, editPersistedConfig, } from "@camerontaylor/paseo-server/configuration";
4
5
  import { connectToDaemon } from "../../utils/client.js";
5
6
  import { withOutput } from "../../output/index.js";
6
7
  function redact(value) {
@@ -4,7 +4,7 @@ export declare function launchLocalDaemon(options: {
4
4
  timeoutMs?: number;
5
5
  foreground?: boolean;
6
6
  }): Promise<{
7
- instance: import("@camerontaylor/paseo-server").DaemonInstance;
7
+ instance: import("@camerontaylor/paseo-server/daemon-control").DaemonInstance;
8
8
  spawned: boolean;
9
9
  exitCode?: number;
10
10
  }>;
@@ -2,7 +2,7 @@ import { Option } from "commander";
2
2
  import { existsSync, readFileSync } from "node:fs";
3
3
  import { createRequire } from "node:module";
4
4
  import path from "node:path";
5
- import { startDaemonInstance, resolvePaseoHome } from "@camerontaylor/paseo-server";
5
+ import { startDaemonInstance, resolvePaseoHome } from "@camerontaylor/paseo-server/daemon-control";
6
6
  const require = createRequire(import.meta.url);
7
7
  function resolveServerRunnerFromDir(currentDir) {
8
8
  const packageJsonPath = path.join(currentDir, "package.json");
@@ -1,7 +1,9 @@
1
1
  import { confirm, isCancel, log } from "@clack/prompts";
2
2
  import { Command } from "commander";
3
3
  import chalk from "chalk";
4
- import { generateLocalPairingOffer, readDaemonInstance, readPersistedConfig, editPersistedConfig, resolveConfigFromPersisted, } from "@camerontaylor/paseo-server";
4
+ import { generateLocalPairingOffer } from "@camerontaylor/paseo-server/pairing";
5
+ import { readDaemonInstance } from "@camerontaylor/paseo-server/daemon-control";
6
+ import { readPersistedConfig, editPersistedConfig, resolveConfigFromPersisted, } from "@camerontaylor/paseo-server/configuration";
5
7
  import { connectToDaemon } from "../../utils/client.js";
6
8
  import { addJsonAndDaemonHostOptions, withGlobalOptions } from "../../utils/command-options.js";
7
9
  import { formatPairingInstructions } from "../../output/pairing.js";
@@ -1,5 +1,5 @@
1
1
  import { Command } from "commander";
2
- import { readDaemonInstance, isSameDaemonInstance, DaemonInstanceError } from "@camerontaylor/paseo-server";
2
+ import { readDaemonInstance, isSameDaemonInstance, DaemonInstanceError, } from "@camerontaylor/paseo-server/daemon-control";
3
3
  import { setTimeout as delay } from "node:timers/promises";
4
4
  import { connectToDaemon } from "../../utils/client.js";
5
5
  import { withOutput } from "../../output/index.js";
@@ -1,5 +1,5 @@
1
1
  import { platform } from "node:os";
2
- import { execCommand } from "@camerontaylor/paseo-server";
2
+ import { execCommand } from "@camerontaylor/paseo-server/process";
3
3
  function normalizeError(error) {
4
4
  if (error instanceof Error) {
5
5
  return error.message;
@@ -1,6 +1,8 @@
1
1
  import path from "node:path";
2
2
  import { isCancel, password as passwordPrompt } from "@clack/prompts";
3
- import { hashDaemonPassword, readPersistedConfig, resolvePaseoHome, savePersistedConfig, } from "@camerontaylor/paseo-server";
3
+ import { hashDaemonPassword } from "@camerontaylor/paseo-server/auth";
4
+ import { readPersistedConfig, savePersistedConfig, } from "@camerontaylor/paseo-server/configuration";
5
+ import { resolvePaseoHome } from "@camerontaylor/paseo-server/daemon-control";
4
6
  const CONFIG_FILENAME = "config.json";
5
7
  const setPasswordResultSchema = {
6
8
  idField: "action",
@@ -1,6 +1,6 @@
1
1
  import { addLocalDaemonOptions } from "../../utils/command-options.js";
2
2
  import { Command } from "commander";
3
- import { daemonLogPath } from "@camerontaylor/paseo-server";
3
+ import { daemonLogPath } from "@camerontaylor/paseo-server/daemon-control";
4
4
  import { launchLocalDaemon, parseTimeoutMs, rejectRemovedLaunchFlags } from "./local-daemon.js";
5
5
  import { withOutput } from "../../output/index.js";
6
6
  export function startCommand() {
@@ -1,6 +1,7 @@
1
1
  import { Command } from "commander";
2
2
  import { DaemonConnectionError } from "@camerontaylor/paseo-client/internal/daemon-client";
3
- import { readDaemonInstance, readPersistedConfig, resolveConfigFromPersisted, daemonLogPath, isSameDaemonInstance, DaemonInstanceError, } from "@camerontaylor/paseo-server";
3
+ import { readDaemonInstance, daemonLogPath, isSameDaemonInstance, DaemonInstanceError, } from "@camerontaylor/paseo-server/daemon-control";
4
+ import { readPersistedConfig, resolveConfigFromPersisted } from "@camerontaylor/paseo-server/configuration";
4
5
  import { connectToDaemon, buildDaemonConnectionCommandError } from "../../utils/client.js";
5
6
  import { withOutput, toCommandError } from "../../output/index.js";
6
7
  import { addJsonAndDaemonHostOptions } from "../../utils/command-options.js";
@@ -1,5 +1,5 @@
1
1
  import { Command } from "commander";
2
- import { stopDaemonInstance } from "@camerontaylor/paseo-server";
2
+ import { stopDaemonInstance } from "@camerontaylor/paseo-server/daemon-control";
3
3
  import { connectToDaemon } from "../../utils/client.js";
4
4
  import { withOutput } from "../../output/index.js";
5
5
  import { addJsonAndDaemonHostOptions } from "../../utils/command-options.js";
@@ -2,7 +2,8 @@ import { addLocalDaemonOptions } from "../utils/command-options.js";
2
2
  import { cancel, confirm, intro, isCancel, log, note, outro } from "@clack/prompts";
3
3
  import { Command, Option } from "commander";
4
4
  import path from "node:path";
5
- import { readPersistedConfig as loadPersistedConfig, savePersistedConfig, readDaemonInstance, waitForDaemonReady, } from "@camerontaylor/paseo-server";
5
+ import { readPersistedConfig as loadPersistedConfig, savePersistedConfig, } from "@camerontaylor/paseo-server/configuration";
6
+ import { readDaemonInstance, waitForDaemonReady } from "@camerontaylor/paseo-server/daemon-control";
6
7
  import { withGlobalOptions } from "../utils/command-options.js";
7
8
  import { launchLocalDaemon, parseTimeoutMs } from "./daemon/local-daemon.js";
8
9
  import { connectToDaemon } from "../utils/client.js";
@@ -1,7 +1,7 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import path from "node:path";
4
- import { spawnProcess } from "@camerontaylor/paseo-server";
4
+ import { spawnProcess } from "@camerontaylor/paseo-server/process";
5
5
  import { buildAgentDeepLink } from "@camerontaylor/paseo-protocol/agent-deep-link";
6
6
  function findDesktopApp() {
7
7
  if (process.platform === "darwin") {
@@ -1,3 +1,4 @@
1
+ import { type UpdateOutcome } from "./update.js";
1
2
  import { Command } from "commander";
2
3
  import type { PluginListItem, PluginLogEntry } from "@camerontaylor/paseo-protocol/messages";
3
4
  import type { CommandOptions, ListResult, SingleResult } from "../../output/index.js";
@@ -8,10 +9,15 @@ interface PluginOptions extends CommandOptions {
8
9
  ref?: string;
9
10
  path?: string;
10
11
  all?: boolean;
12
+ version?: string;
13
+ check?: boolean;
14
+ yes?: boolean;
11
15
  }
12
16
  export declare function runPluginInitCommand(directory: string, options: PluginOptions, _command: Command): Promise<SingleResult<PluginScaffold>>;
13
17
  export declare function runPluginListCommand(pluginId: string | undefined, options: PluginOptions, _command: Command): Promise<ListResult<PluginListItem>>;
14
18
  export declare function runPluginLogsCommand(pluginId: string, options: PluginOptions, _command: Command): Promise<ListResult<PluginLogEntry>>;
19
+ export declare function runPluginInstallCommand(source: string, options: PluginOptions, _command: Command): Promise<SingleResult<PluginListItem>>;
20
+ export declare function runPluginUpdateCommand(pluginId: string | undefined, options: PluginOptions, _command: Command): Promise<ListResult<UpdateOutcome>>;
15
21
  export declare function createPluginCommand(): Command;
16
22
  export {};
17
23
  //# sourceMappingURL=index.d.ts.map
@@ -1,19 +1,27 @@
1
+ import { createInterface } from "node:readline/promises";
2
+ import { reviewPluginUpdates } from "./update.js";
1
3
  import { Command } from "commander";
2
- import path from "node:path";
3
- import { formatPluginSourceReference, parsePluginSourceReference, } from "@camerontaylor/paseo-protocol/plugin-source-reference";
4
+ import { formatPluginSourceReference, formatPluginIdentity, } from "@camerontaylor/paseo-protocol/plugin-source-reference";
4
5
  import { withOutput } from "../../output/index.js";
5
6
  import { addJsonAndDaemonHostOptions, addJsonOption } from "../../utils/command-options.js";
6
7
  import { scaffoldPluginDirectory } from "./scaffold.js";
7
- import { withPluginLogsClient, withPluginManagementClient, withPluginSourceClient, } from "./shared.js";
8
+ import { withPluginLogsClient, withPluginUpdateClient, withPluginManagementClient, withPluginSourceClient, } from "./shared.js";
8
9
  const pluginSchema = {
9
10
  idField: "id",
10
11
  columns: [
11
12
  { header: "PLUGIN", field: "id", width: 20 },
12
13
  { header: "STATUS", field: "status", width: 10 },
13
14
  { header: "ENABLED", field: (plugin) => (plugin.enabled ? "yes" : "no"), width: 8 },
14
- { header: "SOURCE", field: (plugin) => plugin.source ?? "directory", width: 10 },
15
- { header: "COMMIT", field: (plugin) => shortCommit(plugin.commit), width: 14 },
16
- { header: "REF", field: (plugin) => plugin.ref ?? "-", width: 24 },
15
+ {
16
+ header: "SOURCE",
17
+ field: (plugin) => plugin.installation ? formatPluginIdentity(plugin.installation.identity) : "-",
18
+ width: 40,
19
+ },
20
+ {
21
+ header: "REVISION",
22
+ field: (plugin) => plugin.installation?.currentRevision ?? "-",
23
+ width: 16,
24
+ },
17
25
  { header: "DIRECTORY", field: "path", width: 40 },
18
26
  { header: "ERROR", field: (plugin) => plugin.error ?? "", width: 40 },
19
27
  ],
@@ -33,17 +41,16 @@ const pluginLogsSchema = {
33
41
  { header: "MESSAGE", field: "message", width: 80 },
34
42
  ],
35
43
  };
36
- function shortCommit(commit) {
37
- return commit?.slice(0, 12) ?? "-";
38
- }
39
44
  const pluginUpdateSchema = {
40
45
  idField: "id",
41
46
  columns: [
42
- { header: "PLUGIN", field: "id", width: 20 },
43
- { header: "PREVIOUS", field: (plugin) => shortCommit(plugin.previousCommit), width: 14 },
44
- { header: "CURRENT", field: (plugin) => shortCommit(plugin.currentCommit), width: 14 },
45
- { header: "COMMITS", field: (plugin) => String(plugin.commits), width: 8 },
46
- { header: "UPDATED", field: (plugin) => (plugin.updated ? "yes" : "no"), width: 8 },
47
+ { header: "PLUGIN", field: "id", width: 24 },
48
+ { header: "RESULT", field: "outcome", width: 18 },
49
+ {
50
+ header: "DETAIL",
51
+ field: (item) => item.error ?? item.warning ?? item.plugin?.installation?.currentRevision ?? "",
52
+ width: 50,
53
+ },
47
54
  ],
48
55
  };
49
56
  export async function runPluginInitCommand(directory, options, _command) {
@@ -64,32 +71,37 @@ export async function runPluginLogsCommand(pluginId, options, _command) {
64
71
  const data = await withPluginLogsClient(options.daemonTarget, (client) => client.getPluginLogs(pluginId));
65
72
  return { type: "list", data, schema: pluginLogsSchema };
66
73
  }
67
- async function install(source, options, _command) {
68
- process.stderr.write("Trusting plugin code: server code and Git build commands run unsandboxed on the daemon host; client code runs inside Paseo. Dependencies and future updates are part of the codebase you trust.\n");
69
- const isExplicitPath = path.isAbsolute(source) ||
70
- source === "." ||
71
- source === ".." ||
72
- source.startsWith("./") ||
73
- source.startsWith("../") ||
74
- source.startsWith(".\\") ||
75
- source.startsWith("..\\");
76
- const hasPluginPathSuffix = parsePluginSourceReference(source).pluginPath !== undefined;
77
- const canUseLegacyDirectoryInstall = isExplicitPath && !hasPluginPathSuffix && !options.ref && !options.path;
74
+ export async function runPluginInstallCommand(source, options, _command) {
75
+ 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");
78
76
  const sourceReference = formatPluginSourceReference(source, options.path);
79
- const data = canUseLegacyDirectoryInstall
80
- ? await withPluginManagementClient(options.daemonTarget, (client) => client.installDirectoryPlugin(source, options.id))
81
- : await withPluginSourceClient(options.daemonTarget, (client) => client.installPluginSource({
82
- source: sourceReference,
83
- ...(options.id ? { id: options.id } : {}),
84
- ...(options.ref ? { ref: options.ref } : {}),
85
- }));
77
+ const data = await withPluginSourceClient(options.daemonTarget, (client) => client.installPluginSource({
78
+ source: sourceReference,
79
+ ...(options.id ? { id: options.id } : {}),
80
+ ...(options.ref ? { ref: options.ref } : {}),
81
+ }));
86
82
  return { type: "single", data, schema: pluginSchema };
87
83
  }
88
- async function update(pluginId, options, _command) {
89
- if ((pluginId === undefined) === (options.all !== true)) {
90
- throw new Error("Choose one plugin ID or pass --all");
91
- }
92
- const data = await withPluginSourceClient(options.daemonTarget, (client) => client.updatePluginSources(pluginId));
84
+ export async function runPluginUpdateCommand(pluginId, options, _command) {
85
+ const data = await withPluginUpdateClient(options.daemonTarget, (client) => reviewPluginUpdates(client, { ...options, pluginId }, {
86
+ interactive: process.stdin.isTTY === true,
87
+ structured: options.json === true ||
88
+ ["json", "yaml"].includes(options.format?.trim().toLowerCase() ?? ""),
89
+ write: (text) => process.stderr.write(text),
90
+ confirm: async (message) => {
91
+ const prompt = createInterface({ input: process.stdin, output: process.stderr });
92
+ try {
93
+ return /^(y|yes)$/i.test((await prompt.question(message)).trim());
94
+ }
95
+ catch {
96
+ return false;
97
+ }
98
+ finally {
99
+ prompt.close();
100
+ }
101
+ },
102
+ }));
103
+ if (data.some((item) => item.outcome === "error"))
104
+ process.exitCode = 1;
93
105
  return { type: "list", data, schema: pluginUpdateSchema };
94
106
  }
95
107
  async function act(action, pluginId, options) {
@@ -119,16 +131,20 @@ export function createPluginCommand() {
119
131
  addJsonAndDaemonHostOptions(plugin
120
132
  .command("install")
121
133
  .alias("add")
122
- .description("Trust and install a plugin from a directory or Git repository")
123
- .argument("<source>", "Host directory, Git source, or Git source:plugin/path")
134
+ .description("Trust and install a plugin from a directory, Git repository, or npm package")
135
+ .argument("<source>", "Host directory, Git or npm source, optionally followed by :plugin/path")
124
136
  .option("--id <id>", "Runtime plugin ID (defaults to paseo-plugin.json id)")
125
137
  .option("--ref <ref>", "Git branch, tag, or commit")
126
- .option("--path <path>", "Legacy form of the :plugin/path source suffix")).action(withOutput(install));
138
+ .option("--path <path>", "Legacy form of the :plugin/path source suffix")).action(withOutput(runPluginInstallCommand));
127
139
  addJsonAndDaemonHostOptions(plugin
128
140
  .command("update")
129
- .description("Fetch and install Git-managed plugin updates")
141
+ .description("Review and update installed plugins")
130
142
  .argument("[id]")
131
- .option("--all", "Update every Git-managed plugin")).action(withOutput(update));
143
+ .option("--all", "Review all configured plugins")
144
+ .option("--check", "Show available updates without installing")
145
+ .option("--yes", "Apply displayed updates without asking")
146
+ .option("--ref <ref>", "Apply a Git branch, tag, or commit without asking")
147
+ .option("--version <version>", "Apply an npm version, tag, or range without asking")).action(withOutput(runPluginUpdateCommand));
132
148
  for (const action of ["reload", "enable", "disable"]) {
133
149
  addJsonAndDaemonHostOptions(plugin.command(action).description(`${action} a plugin`).argument("<id>")).action(withOutput((id, options, _command) => act(action, id, options)));
134
150
  }
@@ -130,6 +130,16 @@ export async function scaffoldPluginDirectory(targetDirectory, requestedId) {
130
130
  name: id,
131
131
  private: true,
132
132
  version: "0.0.0",
133
+ files: [
134
+ "paseo-plugin.json",
135
+ "index.client.ts",
136
+ "index.client.tsx",
137
+ "index.server.ts",
138
+ "index.server.tsx",
139
+ "client/",
140
+ "server/",
141
+ "shared/",
142
+ ],
133
143
  scripts: { typecheck: "tsc --noEmit" },
134
144
  devDependencies: {
135
145
  "@camerontaylor/paseo-plugin": version,
@@ -3,4 +3,5 @@ import type { DaemonClient } from "@camerontaylor/paseo-client/internal/daemon-c
3
3
  export declare function withPluginManagementClient<T>(target: DaemonTarget, run: (client: DaemonClient) => Promise<T>): Promise<T>;
4
4
  export declare function withPluginLogsClient<T>(target: DaemonTarget, run: (client: DaemonClient) => Promise<T>): Promise<T>;
5
5
  export declare function withPluginSourceClient<T>(target: DaemonTarget, run: (client: DaemonClient) => Promise<T>): Promise<T>;
6
+ export declare function withPluginUpdateClient<T>(target: DaemonTarget, run: (client: DaemonClient) => Promise<T>): Promise<T>;
6
7
  //# sourceMappingURL=shared.d.ts.map
@@ -24,7 +24,11 @@ export async function withPluginLogsClient(target, run) {
24
24
  return withPluginClient(target, "pluginLogs", "Update the host to view plugin logs.", run);
25
25
  }
26
26
  export async function withPluginSourceClient(target, run) {
27
- // COMPAT(pluginGitManagement): added in v0.7.0, remove gate after 2027-08-26.
28
- return withPluginClient(target, "pluginGitManagement", "Update the host to install and update Git plugins.", run);
27
+ // COMPAT(pluginSourceInstallation): added in v0.8.0; remove gate after 2027-03-16 once daemon floor supports source identifiers.
28
+ return withPluginClient(target, "pluginSourceInstallation", "Update the host to install plugin sources.", run);
29
+ }
30
+ export async function withPluginUpdateClient(target, run) {
31
+ // COMPAT(pluginSourceUpdates): added in v0.8.0; remove after 2027-03-16 once daemon floor supports reviewed updates.
32
+ return withPluginClient(target, "pluginSourceUpdates", "Update the host to review plugin updates.", run);
29
33
  }
30
34
  //# sourceMappingURL=shared.js.map
@@ -0,0 +1,37 @@
1
+ import type { PluginUpdatePreview, PluginUpdateResult, PluginUpdateSelection, PluginUpdateProposal } from "@camerontaylor/paseo-protocol/messages";
2
+ interface UpdateClient {
3
+ previewPluginUpdates(input: {
4
+ pluginId?: string;
5
+ target?: PluginUpdateSelection;
6
+ }): Promise<PluginUpdatePreview[]>;
7
+ applyPluginUpdates(proposals: PluginUpdateProposal[]): Promise<PluginUpdateResult[]>;
8
+ }
9
+ export interface UpdateOptions {
10
+ pluginId?: string;
11
+ all?: boolean;
12
+ ref?: string;
13
+ version?: string;
14
+ check?: boolean;
15
+ yes?: boolean;
16
+ }
17
+ export interface UpdateInteraction {
18
+ interactive: boolean;
19
+ structured: boolean;
20
+ write(text: string): void;
21
+ confirm(message: string): Promise<boolean>;
22
+ }
23
+ export interface UpdateOutcome {
24
+ id: string;
25
+ outcome: PluginUpdatePreview["outcome"] | PluginUpdateResult["outcome"] | "declined";
26
+ current?: PluginUpdatePreview["current"];
27
+ target?: PluginUpdatePreview["target"];
28
+ links?: string[];
29
+ proposal?: PluginUpdateProposal;
30
+ plugin?: PluginUpdateResult["plugin"];
31
+ error?: string;
32
+ warning?: string;
33
+ }
34
+ export declare function reviewPluginUpdates(client: UpdateClient, options: UpdateOptions, interaction: UpdateInteraction): Promise<UpdateOutcome[]>;
35
+ export declare function formatUpdatePreview(items: PluginUpdatePreview[]): string;
36
+ export {};
37
+ //# sourceMappingURL=update.d.ts.map
@@ -0,0 +1,58 @@
1
+ export async function reviewPluginUpdates(client, options, interaction) {
2
+ if ((options.pluginId === undefined) === (options.all !== true))
3
+ throw new Error("Choose one plugin ID or pass --all");
4
+ if (options.ref && options.version)
5
+ throw new Error("Choose --ref or --version, not both");
6
+ let target;
7
+ if (options.ref)
8
+ target = { kind: "git", ref: options.ref };
9
+ if (options.version)
10
+ target = { kind: "npm", version: options.version };
11
+ if (target && options.all)
12
+ throw new Error("An explicit target requires one plugin ID, without --all");
13
+ const preview = await client.previewPluginUpdates({ pluginId: options.pluginId, target });
14
+ if (!interaction.structured)
15
+ interaction.write(formatUpdatePreview(preview));
16
+ if (options.check)
17
+ return preview;
18
+ const proposals = preview.flatMap((item) => (item.proposal ? [item.proposal] : []));
19
+ if (!proposals.length)
20
+ return preview;
21
+ if (!options.yes && !target) {
22
+ if (interaction.structured || !interaction.interactive)
23
+ throw new Error("Confirmation required; rerun with --yes.");
24
+ if (!(await interaction.confirm(options.all ? "Update these plugins? [y/N] " : `Update ${options.pluginId}? [y/N] `))) {
25
+ return preview.map((item) => (item.proposal ? { id: item.id, outcome: "declined" } : item));
26
+ }
27
+ }
28
+ const applied = new Map((await client.applyPluginUpdates(proposals)).map((item) => [item.id, item]));
29
+ return preview.map((item) => applied.get(item.id) ?? item);
30
+ }
31
+ export function formatUpdatePreview(items) {
32
+ return items
33
+ .map((item) => {
34
+ const current = item.current?.currentRevision ?? "unknown";
35
+ const target = item.target?.kind === "git" ? item.target.commit : item.target?.version;
36
+ let text;
37
+ switch (item.outcome) {
38
+ case "update":
39
+ text = `${current} → ${target}`;
40
+ break;
41
+ case "current":
42
+ text = `up to date (${current})`;
43
+ break;
44
+ case "installed-newer":
45
+ text = `installed ${current} is newer than latest ${target}; keeping installed version`;
46
+ break;
47
+ case "local":
48
+ text = "local directory; use Reload after editing";
49
+ break;
50
+ case "error":
51
+ text = item.error ?? "Check failed";
52
+ break;
53
+ }
54
+ return `${item.id}: ${text}\n${item.links.map((link) => `Review: ${link}\n`).join("")}`;
55
+ })
56
+ .join("\n");
57
+ }
58
+ //# sourceMappingURL=update.js.map
@@ -1,4 +1,4 @@
1
- import { ensurePrivateDirectory } from "@camerontaylor/paseo-server";
1
+ import { ensurePrivateDirectory } from "@camerontaylor/paseo-server/daemon-control";
2
2
  import { readFile, writeFile } from "node:fs/promises";
3
3
  import { randomUUID } from "node:crypto";
4
4
  import { join } from "node:path";
@@ -1,4 +1,4 @@
1
- import { type DaemonInstance } from "@camerontaylor/paseo-server";
1
+ import { type DaemonInstance } from "@camerontaylor/paseo-server/daemon-control";
2
2
  import { type DaemonTarget } from "./daemon-target.js";
3
3
  export type { DaemonTarget } from "./daemon-target.js";
4
4
  import { DaemonClient } from "@camerontaylor/paseo-client/internal/daemon-client";
@@ -1,4 +1,4 @@
1
- import { waitForDaemonReady, resolvePaseoHome } from "@camerontaylor/paseo-server";
1
+ import { waitForDaemonReady, resolvePaseoHome, } from "@camerontaylor/paseo-server/daemon-control";
2
2
  import { describeDaemonTarget } from "./daemon-target.js";
3
3
  import { buildDaemonWebSocketUrl, buildRelayWebSocketUrl, normalizeHostPort, parseConnectionUri, shouldUseTlsForDefaultHostedRelay, } from "@camerontaylor/paseo-protocol/daemon-endpoints";
4
4
  import { parseConnectionOfferFromUrl, } from "@camerontaylor/paseo-protocol/connection-offer";
@@ -36,8 +36,10 @@ export function withGlobalOptions(handler) {
36
36
  selectors[key].add(value);
37
37
  }
38
38
  }
39
- if (selectors.home.size > 1 || selectors.host.size > 1)
40
- throw { code: "TARGET_AMBIGUOUS", message: "Conflicting duplicate daemon selectors." };
39
+ for (const key of ["home", "host"]) {
40
+ if (selectors[key].size > 1)
41
+ throw { code: "TARGET_AMBIGUOUS", message: `Conflicting duplicate --${key} selectors.` };
42
+ }
41
43
  const localOnly = localCommands.has(command);
42
44
  options.daemonTarget = selectDaemonTarget(options, process.env, localOnly);
43
45
  mergedArgs[mergedArgs.length - 2] = options;
@@ -1,4 +1,4 @@
1
- import { resolvePaseoHome } from "@camerontaylor/paseo-server";
1
+ import { resolvePaseoHome } from "@camerontaylor/paseo-server/daemon-control";
2
2
  export function selectDaemonTarget(options, env = process.env, localOnly = false) {
3
3
  for (const key of ["home", "host"]) {
4
4
  if (options[key] !== undefined && !options[key].trim())
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camerontaylor/paseo-cli",
3
- "version": "0.8.0-fork.3",
3
+ "version": "0.9.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.8.0-fork.3",
32
- "@camerontaylor/paseo-protocol": "0.8.0-fork.3",
33
- "@camerontaylor/paseo-server": "0.8.0-fork.3",
31
+ "@camerontaylor/paseo-client": "0.9.1-fork.1",
32
+ "@camerontaylor/paseo-protocol": "0.9.1-fork.1",
33
+ "@camerontaylor/paseo-server": "0.9.1-fork.1",
34
34
  "chalk": "^5.3.0",
35
35
  "commander": "^12.0.0",
36
36
  "mime-types": "^2.1.35",