@rynx-ai/cli 0.1.11-beta.1 → 0.1.11-beta.3
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/app-runtime.d.ts +10 -0
- package/dist/app-runtime.js +34 -0
- package/dist/client.d.ts +1 -1
- package/dist/client.js +1 -1
- package/dist/commands/app-distribution.d.ts +2 -2
- package/dist/commands/app-distribution.js +2 -6
- package/dist/commands/browser.js +9 -5
- package/dist/commands/index.d.ts +3 -1
- package/dist/commands/index.js +3 -1
- package/dist/commands/lifecycle.d.ts +1 -0
- package/dist/commands/lifecycle.js +21 -0
- package/dist/commands/market.d.ts +1 -0
- package/dist/commands/market.js +71 -0
- package/dist/commands/plugin.js +37 -93
- package/dist/commands/session.d.ts +1 -0
- package/dist/commands/session.js +38 -0
- package/dist/commands/setup.d.ts +2 -0
- package/dist/commands/setup.js +314 -0
- package/dist/commands/skills.d.ts +6 -4
- package/dist/commands/skills.js +41 -28
- package/dist/commands/update.d.ts +20 -0
- package/dist/commands/update.js +234 -0
- package/dist/control-client.d.ts +7 -14
- package/dist/control-client.js +32 -54
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/run-cli.js +41 -12
- package/dist/standalone.d.ts +13 -0
- package/dist/standalone.js +54 -0
- package/dist/usage.d.ts +1 -1
- package/dist/usage.js +27 -16
- package/package.json +20 -6
- package/skill-guides/browser.md +72 -0
- package/skill-guides/emulator.md +79 -0
- package/skills/rynx-cli/SKILL.md +19 -87
- package/dist/legacy-adapter.d.ts +0 -7
- package/dist/legacy-adapter.js +0 -63
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface CliDependencyVersions {
|
|
2
|
+
cliVersion: string;
|
|
3
|
+
daemonVersion: string;
|
|
4
|
+
}
|
|
5
|
+
/** Read the versions in the dependency closure owned by this CLI installation. */
|
|
6
|
+
export declare function resolveCliDependencyVersions(): CliDependencyVersions;
|
|
7
|
+
/** Resolve the CLI-owned process entry used exclusively by Rynx App. */
|
|
8
|
+
export declare function resolveCliAppRuntimeEntry(moduleUrl?: string): string;
|
|
9
|
+
/** Run the App-owned daemon in this dedicated CLI App Runtime process. */
|
|
10
|
+
export declare function runAppRuntimeProcess(): Promise<void>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { installedDaemonVersion } from "@rynx-ai/daemon/entry-path";
|
|
4
|
+
import { installedVersion } from "./version.js";
|
|
5
|
+
/** Read the versions in the dependency closure owned by this CLI installation. */
|
|
6
|
+
export function resolveCliDependencyVersions() {
|
|
7
|
+
return {
|
|
8
|
+
cliVersion: installedVersion(),
|
|
9
|
+
daemonVersion: installedDaemonVersion(),
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
/** Resolve the CLI-owned process entry used exclusively by Rynx App. */
|
|
13
|
+
export function resolveCliAppRuntimeEntry(moduleUrl = import.meta.url) {
|
|
14
|
+
return fileURLToPath(moduleUrl);
|
|
15
|
+
}
|
|
16
|
+
/** Run the App-owned daemon in this dedicated CLI App Runtime process. */
|
|
17
|
+
export async function runAppRuntimeProcess() {
|
|
18
|
+
if (process.env.RYNX_DISTRIBUTION !== "app") {
|
|
19
|
+
throw new Error("CLI App Runtime requires RYNX_DISTRIBUTION=app");
|
|
20
|
+
}
|
|
21
|
+
if (process.env.RYNX_DAEMON_LIFECYCLE !== "follow-app" &&
|
|
22
|
+
process.env.RYNX_DAEMON_LIFECYCLE !== "resident") {
|
|
23
|
+
throw new Error("CLI App Runtime requires an App daemon lifecycle");
|
|
24
|
+
}
|
|
25
|
+
const { runDaemonProcess } = await import("@rynx-ai/daemon/internal/process");
|
|
26
|
+
await runDaemonProcess();
|
|
27
|
+
}
|
|
28
|
+
if (process.argv[1] &&
|
|
29
|
+
path.resolve(process.argv[1]) === path.resolve(fileURLToPath(import.meta.url))) {
|
|
30
|
+
runAppRuntimeProcess().catch((error) => {
|
|
31
|
+
console.error(error instanceof Error ? error.stack ?? error.message : String(error));
|
|
32
|
+
process.exitCode = 1;
|
|
33
|
+
});
|
|
34
|
+
}
|
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { callManagedRuntimeBrowser, callResidentRuntime, cancelResidentPluginInstallation, commitResidentPluginInstallation,
|
|
1
|
+
export { callManagedRuntimeBrowser, callResidentRuntime, cancelResidentPluginInstallation, commitResidentPluginInstallation, cleanupResidentSessions, createResidentRemoteRuntimePairingOffer, forgetResidentRuntimeTarget, getResidentChromeInspectionStatus, getResidentDaemonIdentity, getResidentDaemonRuntimeStatus, getResidentRuntimeLocalBrowserEndpoint, invokeResidentPluginCommand, listResidentPlugins, listResidentRemoteRuntimeClients, listResidentRuntimeTargets, pairResidentRuntimeTarget, prepareResidentPluginInstallation, readManagedRuntimeBrowserCredential, readOptionalManagedRuntimeBrowserCredential, revokeResidentRemoteRuntimeClient, setResidentPluginEnabled, shutdownResidentDaemonIfIdle, configureResidentChromeInspection, testResidentRuntimeTarget, uninstallResidentPlugin, connectResidentDesktopBrowserHost, ResidentRuntimeCallError, type InvokeResidentPluginCommandOptions, type ResidentDaemonIdentity, type ResidentDesktopBrowserHostCommandRequest, type ResidentDesktopBrowserHostConnectOptions, type ResidentDesktopBrowserHostConnection, type ResidentDesktopBrowserHostFailure, type ResidentPluginCommandResult, type ResidentPluginManagementItem, type ResidentPluginManagementState, type ResidentRemoteRuntimeClientGrant, type ResidentRuntimeCallErrorCode, type ResidentRuntimeCallOutcome, type ResidentRuntimeTargetTestResult, type ResidentRuntimeTargetView, } from "./control-client.js";
|
|
2
2
|
export type { DaemonChromeInspectionConfigureInput, DaemonChromeInspectionStatus, } from "@rynx-ai/protocol/control";
|
|
3
3
|
export type { PluginInstallCommitInput, PluginInstallCommitResult, PluginInstallPreparation, PluginInstallPrepareInput, } from "@rynx-ai/protocol/plugin-management";
|
|
4
4
|
export { ensureDaemonControlEndpoint, resolveDaemonControlEndpoint, type DaemonControlEndpoint, type ResolveDaemonControlEndpointOptions, } from "./control-endpoint.js";
|
package/dist/client.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { callManagedRuntimeBrowser, callResidentRuntime, cancelResidentPluginInstallation, commitResidentPluginInstallation,
|
|
1
|
+
export { callManagedRuntimeBrowser, callResidentRuntime, cancelResidentPluginInstallation, commitResidentPluginInstallation, cleanupResidentSessions, createResidentRemoteRuntimePairingOffer, forgetResidentRuntimeTarget, getResidentChromeInspectionStatus, getResidentDaemonIdentity, getResidentDaemonRuntimeStatus, getResidentRuntimeLocalBrowserEndpoint, invokeResidentPluginCommand, listResidentPlugins, listResidentRemoteRuntimeClients, listResidentRuntimeTargets, pairResidentRuntimeTarget, prepareResidentPluginInstallation, readManagedRuntimeBrowserCredential, readOptionalManagedRuntimeBrowserCredential, revokeResidentRemoteRuntimeClient, setResidentPluginEnabled, shutdownResidentDaemonIfIdle, configureResidentChromeInspection, testResidentRuntimeTarget, uninstallResidentPlugin, connectResidentDesktopBrowserHost, ResidentRuntimeCallError, } from "./control-client.js";
|
|
2
2
|
export { ensureDaemonControlEndpoint, resolveDaemonControlEndpoint, } from "./control-endpoint.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export declare function shouldUseAppDistributionCommand(command: string | undefined, env?: NodeJS.ProcessEnv): boolean;
|
|
2
2
|
/**
|
|
3
|
-
* Prevent the App launcher from falling through to
|
|
4
|
-
*
|
|
3
|
+
* Prevent the App launcher from falling through to standalone lifecycle
|
|
4
|
+
* commands and accidentally creating or inspecting a PM2-owned second owner.
|
|
5
5
|
*/
|
|
6
6
|
export declare function runAppDistributionCommand(command: string, args: readonly string[]): Promise<number>;
|
|
@@ -4,7 +4,6 @@ import { rynxHome } from "@rynx-ai/core";
|
|
|
4
4
|
import { resolveDaemonControlEndpoint } from "../control-endpoint.js";
|
|
5
5
|
import { fail } from "./errors.js";
|
|
6
6
|
const APP_ONLY_COMMANDS = new Set([
|
|
7
|
-
"setup",
|
|
8
7
|
"update",
|
|
9
8
|
"start",
|
|
10
9
|
"restart",
|
|
@@ -19,8 +18,8 @@ export function shouldUseAppDistributionCommand(command, env = process.env) {
|
|
|
19
18
|
&& env.RYNX_DISTRIBUTION === "app";
|
|
20
19
|
}
|
|
21
20
|
/**
|
|
22
|
-
* Prevent the App launcher from falling through to
|
|
23
|
-
*
|
|
21
|
+
* Prevent the App launcher from falling through to standalone lifecycle
|
|
22
|
+
* commands and accidentally creating or inspecting a PM2-owned second owner.
|
|
24
23
|
*/
|
|
25
24
|
export async function runAppDistributionCommand(command, args) {
|
|
26
25
|
const json = args.includes("--json");
|
|
@@ -44,9 +43,6 @@ export async function runAppDistributionCommand(command, args) {
|
|
|
44
43
|
printRecentDaemonLogs();
|
|
45
44
|
return 0;
|
|
46
45
|
}
|
|
47
|
-
if (command === "setup") {
|
|
48
|
-
fail("Rynx App setup is completed in its onboarding and Desktop Settings.");
|
|
49
|
-
}
|
|
50
46
|
if (command === "update") {
|
|
51
47
|
fail("Rynx App updates are managed from Rynx > Check for Updates.");
|
|
52
48
|
}
|
package/dist/commands/browser.js
CHANGED
|
@@ -3,7 +3,7 @@ import path from "node:path";
|
|
|
3
3
|
import { connectBrowserAutomation, } from "@rynx-ai/browser-cdp";
|
|
4
4
|
import WebSocket from "ws";
|
|
5
5
|
import { parseBrowserCliArgs, resolveBrowserCliTarget, } from "../browser-cli-args.js";
|
|
6
|
-
import { callManagedRuntimeBrowser, callResidentRuntime,
|
|
6
|
+
import { callManagedRuntimeBrowser, callResidentRuntime, getResidentRuntimeLocalBrowserAutomationAccess, getResidentRuntimeLocalBrowserEndpoint, readOptionalManagedRuntimeBrowserCredential, } from "../control-client.js";
|
|
7
7
|
import { fail } from "./errors.js";
|
|
8
8
|
export async function runBrowserCommand(args) {
|
|
9
9
|
let parsed;
|
|
@@ -18,9 +18,11 @@ export async function runBrowserCommand(args) {
|
|
|
18
18
|
fail("browser install: choose either --version or --channel");
|
|
19
19
|
}
|
|
20
20
|
const channel = browserReleaseChannel(parsed.channel);
|
|
21
|
+
const { createBrowserArtifactManagementService } = await import("@rynx-ai/daemon/browser-artifacts");
|
|
22
|
+
const artifacts = createBrowserArtifactManagementService();
|
|
21
23
|
const result = parsed.subcommand === "install"
|
|
22
|
-
? await
|
|
23
|
-
: await
|
|
24
|
+
? await artifacts.install(parsed.version ? { version: parsed.version } : { channel }, parsed.json ? {} : { onProgress: (message) => console.error(message) })
|
|
25
|
+
: await artifacts.update({ channel }, parsed.json ? {} : { onProgress: (message) => console.error(message) });
|
|
24
26
|
if (parsed.json) {
|
|
25
27
|
console.log(JSON.stringify({
|
|
26
28
|
resolved: result.resolved,
|
|
@@ -35,7 +37,8 @@ export async function runBrowserCommand(args) {
|
|
|
35
37
|
return 0;
|
|
36
38
|
}
|
|
37
39
|
if (parsed.subcommand === "version") {
|
|
38
|
-
const {
|
|
40
|
+
const { createBrowserArtifactManagementService } = await import("@rynx-ai/daemon/browser-artifacts");
|
|
41
|
+
const { installed } = createBrowserArtifactManagementService().version();
|
|
39
42
|
if (parsed.json)
|
|
40
43
|
console.log(JSON.stringify({ installed }, null, 2));
|
|
41
44
|
else if (installed) {
|
|
@@ -47,7 +50,8 @@ export async function runBrowserCommand(args) {
|
|
|
47
50
|
return 0;
|
|
48
51
|
}
|
|
49
52
|
if (parsed.subcommand === "clean") {
|
|
50
|
-
const
|
|
53
|
+
const { createBrowserArtifactManagementService } = await import("@rynx-ai/daemon/browser-artifacts");
|
|
54
|
+
const result = createBrowserArtifactManagementService().clean();
|
|
51
55
|
if (parsed.json)
|
|
52
56
|
console.log(JSON.stringify(result, null, 2));
|
|
53
57
|
else if (result.removedBuilds.length === 0) {
|
package/dist/commands/index.d.ts
CHANGED
|
@@ -5,5 +5,7 @@ export { runCleanupCommand } from "./cleanup.js";
|
|
|
5
5
|
export { runEmulatorCommand } from "./emulator.js";
|
|
6
6
|
export { CliUsageError } from "./errors.js";
|
|
7
7
|
export { type PluginManageCommandOptions, runPluginCommand, runPluginManageCommand, } from "./plugin.js";
|
|
8
|
+
export { runMarketCommand } from "./market.js";
|
|
8
9
|
export { runRuntimeCommand } from "./runtime.js";
|
|
9
|
-
export {
|
|
10
|
+
export { runSessionCommand } from "./session.js";
|
|
11
|
+
export { builtinSkillGuidesDirectory, listBuiltinSkills, readBuiltinSkill, runSkillsCommand, } from "./skills.js";
|
package/dist/commands/index.js
CHANGED
|
@@ -5,5 +5,7 @@ export { runCleanupCommand } from "./cleanup.js";
|
|
|
5
5
|
export { runEmulatorCommand } from "./emulator.js";
|
|
6
6
|
export { CliUsageError } from "./errors.js";
|
|
7
7
|
export { runPluginCommand, runPluginManageCommand, } from "./plugin.js";
|
|
8
|
+
export { runMarketCommand } from "./market.js";
|
|
8
9
|
export { runRuntimeCommand } from "./runtime.js";
|
|
9
|
-
export {
|
|
10
|
+
export { runSessionCommand } from "./session.js";
|
|
11
|
+
export { builtinSkillGuidesDirectory, listBuiltinSkills, readBuiltinSkill, runSkillsCommand, } from "./skills.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runLifecycleCommand(command: string, args: readonly string[]): Promise<number>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { startStandaloneDaemon, statusStandaloneDaemon, stopStandaloneDaemon, streamStandaloneDaemonLogs, } from "../standalone.js";
|
|
2
|
+
import { fail } from "./errors.js";
|
|
3
|
+
export async function runLifecycleCommand(command, args) {
|
|
4
|
+
if (args.length > 0)
|
|
5
|
+
fail(`${command}: unexpected argument ${args[0]}`);
|
|
6
|
+
switch (command) {
|
|
7
|
+
case "start":
|
|
8
|
+
return startStandaloneDaemon();
|
|
9
|
+
case "restart":
|
|
10
|
+
return startStandaloneDaemon({ restart: true });
|
|
11
|
+
case "stop":
|
|
12
|
+
return stopStandaloneDaemon();
|
|
13
|
+
case "status":
|
|
14
|
+
return statusStandaloneDaemon();
|
|
15
|
+
case "logs":
|
|
16
|
+
await streamStandaloneDaemonLogs();
|
|
17
|
+
return 0;
|
|
18
|
+
default:
|
|
19
|
+
fail(`unknown lifecycle command: ${command}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runMarketCommand(args: readonly string[]): Promise<number>;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { addResidentPluginMarketplace, listResidentPluginMarketplaces, refreshResidentPluginMarketplace, removeResidentPluginMarketplace, } from "../control-client.js";
|
|
2
|
+
import { fail } from "./errors.js";
|
|
3
|
+
export async function runMarketCommand(args) {
|
|
4
|
+
const [command, target, ...rest] = args;
|
|
5
|
+
switch (command) {
|
|
6
|
+
case "list": {
|
|
7
|
+
rejectExtra(rest, "market list");
|
|
8
|
+
const markets = await listResidentPluginMarketplaces();
|
|
9
|
+
console.log("Markets");
|
|
10
|
+
if (markets.length === 0) {
|
|
11
|
+
console.log(" (none)");
|
|
12
|
+
return 0;
|
|
13
|
+
}
|
|
14
|
+
for (const market of markets) {
|
|
15
|
+
const source = market.builtIn ? "built-in" : market.source;
|
|
16
|
+
const revision = market.builtIn ? "bundled with Rynx" : market.resolvedRevision;
|
|
17
|
+
console.log(` ${market.id.padEnd(24)} ${source.padEnd(8)} ` +
|
|
18
|
+
`${String(market.plugins.length).padStart(3)} plugins ${revision}`);
|
|
19
|
+
}
|
|
20
|
+
return 0;
|
|
21
|
+
}
|
|
22
|
+
case "add": {
|
|
23
|
+
if (!target || target.startsWith("--"))
|
|
24
|
+
fail("market add: missing Git/local source");
|
|
25
|
+
let alias;
|
|
26
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
27
|
+
const arg = rest[index];
|
|
28
|
+
if (arg !== "--alias" || alias !== undefined)
|
|
29
|
+
fail(`market add: unexpected option ${arg}`);
|
|
30
|
+
const value = rest[index + 1];
|
|
31
|
+
if (!value || value.startsWith("--"))
|
|
32
|
+
fail("market add: --alias requires a value");
|
|
33
|
+
alias = value;
|
|
34
|
+
index += 1;
|
|
35
|
+
}
|
|
36
|
+
const market = await addResidentPluginMarketplace({
|
|
37
|
+
source: target,
|
|
38
|
+
...(alias ? { alias } : {}),
|
|
39
|
+
});
|
|
40
|
+
console.log(`Market "${market.id}" added with ${market.plugins.length} plugin(s).`);
|
|
41
|
+
return 0;
|
|
42
|
+
}
|
|
43
|
+
case "refresh": {
|
|
44
|
+
rejectExtra(rest, "market refresh");
|
|
45
|
+
const ids = target
|
|
46
|
+
? [target]
|
|
47
|
+
: (await listResidentPluginMarketplaces()).map((market) => market.id);
|
|
48
|
+
for (const id of ids) {
|
|
49
|
+
const market = await refreshResidentPluginMarketplace(id);
|
|
50
|
+
console.log(`Market "${market.id}" refreshed at ${market.resolvedRevision}.`);
|
|
51
|
+
}
|
|
52
|
+
if (ids.length === 0)
|
|
53
|
+
console.log("No Markets to refresh.");
|
|
54
|
+
return 0;
|
|
55
|
+
}
|
|
56
|
+
case "remove": {
|
|
57
|
+
if (!target)
|
|
58
|
+
fail("market remove: missing market id");
|
|
59
|
+
rejectExtra(rest, "market remove");
|
|
60
|
+
await removeResidentPluginMarketplace(target);
|
|
61
|
+
console.log(`Market "${target}" removed.`);
|
|
62
|
+
return 0;
|
|
63
|
+
}
|
|
64
|
+
default:
|
|
65
|
+
fail("market: expected list, add, refresh, or remove");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function rejectExtra(args, command) {
|
|
69
|
+
if (args.length > 0)
|
|
70
|
+
fail(`${command}: unexpected argument ${args[0]}`);
|
|
71
|
+
}
|
package/dist/commands/plugin.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { createInterface } from "node:readline/promises";
|
|
2
|
-
import { cancelResidentPluginInstallation, commitResidentPluginInstallation, invokeResidentPluginCommand, listResidentPlugins, prepareResidentPluginInstallation,
|
|
2
|
+
import { cancelResidentPluginInstallation, commitResidentPluginInstallation, invokeResidentPluginCommand, listResidentPlugins, prepareResidentPluginInstallation, setResidentPluginEnabled, uninstallResidentPlugin, } from "../control-client.js";
|
|
3
3
|
import { fail } from "./errors.js";
|
|
4
4
|
const PLUGIN_STDIN_MAX_BYTES = 256 * 1024;
|
|
5
5
|
const PLUGIN_DIGEST_PATTERN = /^sha256-[A-Za-z0-9+/]{43}={0,2}$/;
|
|
6
|
+
const CANONICAL_PLUGIN_ID_PATTERN = /^[a-z][a-z0-9-]{0,62}@[a-z0-9][a-z0-9-]{0,62}(?:\/[a-z0-9][a-z0-9._-]{0,99})?$/;
|
|
6
7
|
export async function runPluginManageCommand(args, options = {}) {
|
|
7
8
|
const [subcommand, pluginId] = args;
|
|
8
9
|
switch (subcommand) {
|
|
@@ -22,16 +23,16 @@ export async function runPluginManageCommand(args, options = {}) {
|
|
|
22
23
|
case "enable":
|
|
23
24
|
case "disable": {
|
|
24
25
|
if (!pluginId)
|
|
25
|
-
fail(`plugin
|
|
26
|
+
fail(`plugin ${subcommand}: missing canonical plugin id`);
|
|
26
27
|
await setResidentPluginEnabled(pluginId, subcommand === "enable");
|
|
27
|
-
console.log(`Plugin "${pluginId}" ${subcommand}d
|
|
28
|
+
console.log(`Plugin "${pluginId}" ${subcommand}d.`);
|
|
28
29
|
return 0;
|
|
29
30
|
}
|
|
30
31
|
case "uninstall": {
|
|
31
32
|
if (!pluginId)
|
|
32
|
-
fail("plugin
|
|
33
|
+
fail("plugin uninstall: missing canonical plugin id");
|
|
33
34
|
await uninstallResidentPlugin(pluginId);
|
|
34
|
-
console.log(`Plugin "${pluginId}" uninstalled
|
|
35
|
+
console.log(`Plugin "${pluginId}" uninstalled.`);
|
|
35
36
|
return 0;
|
|
36
37
|
}
|
|
37
38
|
case "install":
|
|
@@ -40,9 +41,10 @@ export async function runPluginManageCommand(args, options = {}) {
|
|
|
40
41
|
const preparation = await prepareResidentPluginInstallation({
|
|
41
42
|
operation: subcommand,
|
|
42
43
|
...(subcommand === "install"
|
|
43
|
-
?
|
|
44
|
+
? CANONICAL_PLUGIN_ID_PATTERN.test(parsed.target)
|
|
45
|
+
? { marketplacePlugin: parsed.target }
|
|
46
|
+
: { spec: parsed.target }
|
|
44
47
|
: { pluginId: parsed.target }),
|
|
45
|
-
allowScripts: parsed.allowScripts,
|
|
46
48
|
});
|
|
47
49
|
let committed = false;
|
|
48
50
|
try {
|
|
@@ -56,26 +58,24 @@ export async function runPluginManageCommand(args, options = {}) {
|
|
|
56
58
|
fail(`prepared plugin digest ${preparation.artifact.digest} does not match ` +
|
|
57
59
|
`--expect-digest ${parsed.expectedDigest}`);
|
|
58
60
|
}
|
|
61
|
+
if (subcommand === "update" && preparation.unchanged) {
|
|
62
|
+
if (parsed.json) {
|
|
63
|
+
console.log(JSON.stringify({
|
|
64
|
+
preparation: publicPreparation(preparation),
|
|
65
|
+
unchanged: true,
|
|
66
|
+
}, null, 2));
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
console.log(`Plugin "${preparation.artifact.id}" is already up to date.`);
|
|
70
|
+
}
|
|
71
|
+
return 0;
|
|
72
|
+
}
|
|
59
73
|
if ((!interactive || parsed.json) &&
|
|
60
|
-
|
|
74
|
+
parsed.force &&
|
|
61
75
|
parsed.expectedDigest === undefined) {
|
|
62
|
-
fail("non-interactive --force
|
|
76
|
+
fail("non-interactive --force confirmation must be bound to the " +
|
|
63
77
|
`prepared artifact; re-run with --expect-digest ${preparation.artifact.digest}`);
|
|
64
78
|
}
|
|
65
|
-
if (preparation.artifact.requiresBuildScripts && !parsed.allowScripts) {
|
|
66
|
-
fail(`plugin "${preparation.artifact.id}" requires package build scripts; ` +
|
|
67
|
-
`review digest ${preparation.artifact.digest} and re-run with ` +
|
|
68
|
-
`--allow-scripts --expect-digest ${preparation.artifact.digest}`);
|
|
69
|
-
}
|
|
70
|
-
if (preparation.artifact.requiresBuildScripts &&
|
|
71
|
-
parsed.allowScripts &&
|
|
72
|
-
parsed.expectedDigest === undefined) {
|
|
73
|
-
const approved = await confirm(`Run declared build scripts from digest ${preparation.artifact.digest}?`);
|
|
74
|
-
if (!approved) {
|
|
75
|
-
console.log("Plugin installation cancelled; build scripts were not run.");
|
|
76
|
-
return 0;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
79
|
let overwrite = subcommand === "update";
|
|
80
80
|
if (subcommand === "install" && preparation.existing) {
|
|
81
81
|
overwrite = parsed.force;
|
|
@@ -92,49 +92,15 @@ export async function runPluginManageCommand(args, options = {}) {
|
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
|
-
let grants = [...preparation.capabilityDiff.retained];
|
|
96
|
-
if (preparation.capabilityDiff.added.length > 0) {
|
|
97
|
-
if (parsed.grantAll) {
|
|
98
|
-
grants = [...preparation.artifact.requestedCapabilities];
|
|
99
|
-
}
|
|
100
|
-
else {
|
|
101
|
-
if (!interactive || parsed.json) {
|
|
102
|
-
fail(`plugin "${preparation.artifact.id}" requests new capabilities ` +
|
|
103
|
-
`(${preparation.capabilityDiff.added.join(", ")}); inspect digest ` +
|
|
104
|
-
`${preparation.artifact.digest} and re-run with ` +
|
|
105
|
-
`--grant-all --expect-digest ${preparation.artifact.digest}`);
|
|
106
|
-
}
|
|
107
|
-
const approved = await confirm(`Grant ${preparation.capabilityDiff.added.length} new capability(s) ` +
|
|
108
|
-
`to "${preparation.artifact.id}"?`);
|
|
109
|
-
if (!approved) {
|
|
110
|
-
console.log("Plugin installation cancelled; no changes were made.");
|
|
111
|
-
return 0;
|
|
112
|
-
}
|
|
113
|
-
grants = [...preparation.artifact.requestedCapabilities];
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
95
|
const result = await commitResidentPluginInstallation(preparation.token, {
|
|
117
|
-
grants,
|
|
118
96
|
overwrite,
|
|
119
97
|
expectedDigest: preparation.artifact.digest,
|
|
120
98
|
});
|
|
121
99
|
committed = true;
|
|
122
|
-
let runtimeReloaded = false;
|
|
123
|
-
if (parsed.restart) {
|
|
124
|
-
try {
|
|
125
|
-
await reloadResidentPluginRuntime(result.plugin.id);
|
|
126
|
-
runtimeReloaded = true;
|
|
127
|
-
}
|
|
128
|
-
catch (error) {
|
|
129
|
-
throw new Error(`Plugin "${result.plugin.id}" was committed, but its runtime did not reload: ` +
|
|
130
|
-
`${messageOf(error)}`, { cause: error });
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
100
|
if (parsed.json) {
|
|
134
101
|
console.log(JSON.stringify({
|
|
135
102
|
preparation: publicPreparation(preparation),
|
|
136
103
|
plugin: result.plugin,
|
|
137
|
-
runtimeReloaded,
|
|
138
104
|
}, null, 2));
|
|
139
105
|
}
|
|
140
106
|
else {
|
|
@@ -142,9 +108,7 @@ export async function runPluginManageCommand(args, options = {}) {
|
|
|
142
108
|
console.log(`Plugin "${result.plugin.id}"${result.plugin.version
|
|
143
109
|
? ` @${result.plugin.version}`
|
|
144
110
|
: ""} ${action}.`);
|
|
145
|
-
console.log(
|
|
146
|
-
? `Plugin "${result.plugin.id}" runtime is ready.`
|
|
147
|
-
: "Use --restart to reload the plugin runtime now.");
|
|
111
|
+
console.log(`Runtime state: ${result.plugin.runtimeState}.`);
|
|
148
112
|
}
|
|
149
113
|
return 0;
|
|
150
114
|
}
|
|
@@ -155,15 +119,15 @@ export async function runPluginManageCommand(args, options = {}) {
|
|
|
155
119
|
}
|
|
156
120
|
}
|
|
157
121
|
default:
|
|
158
|
-
fail("plugin
|
|
122
|
+
fail("plugin: expected list, install, update, uninstall, enable, or disable");
|
|
159
123
|
}
|
|
160
124
|
}
|
|
161
125
|
export async function runPluginCommand(args) {
|
|
162
126
|
const [pluginId, command] = args;
|
|
163
127
|
if (!pluginId)
|
|
164
128
|
fail("plugin: missing plugin id");
|
|
165
|
-
if (
|
|
166
|
-
return runPluginManageCommand(args
|
|
129
|
+
if (["list", "install", "update", "uninstall", "enable", "disable"].includes(pluginId)) {
|
|
130
|
+
return runPluginManageCommand(args);
|
|
167
131
|
}
|
|
168
132
|
if (!command)
|
|
169
133
|
fail(`plugin ${pluginId}: missing command`);
|
|
@@ -184,15 +148,12 @@ function parseInstallationArgs(operation, args) {
|
|
|
184
148
|
const target = args[0];
|
|
185
149
|
if (!target || target.startsWith("--")) {
|
|
186
150
|
fail(operation === "install"
|
|
187
|
-
? "plugin
|
|
188
|
-
: "plugin
|
|
151
|
+
? "plugin install: missing Git/local source or market plugin identity"
|
|
152
|
+
: "plugin update: missing canonical plugin id");
|
|
189
153
|
}
|
|
190
154
|
const flags = new Set();
|
|
191
155
|
const allowedFlags = new Set([
|
|
192
156
|
"--force",
|
|
193
|
-
"--grant-all",
|
|
194
|
-
"--allow-scripts",
|
|
195
|
-
"--restart",
|
|
196
157
|
"--json",
|
|
197
158
|
]);
|
|
198
159
|
let expectedDigest;
|
|
@@ -200,36 +161,33 @@ function parseInstallationArgs(operation, args) {
|
|
|
200
161
|
const arg = args[index];
|
|
201
162
|
if (arg === "--expect-digest" || arg === "--expected-digest") {
|
|
202
163
|
if (expectedDigest !== undefined) {
|
|
203
|
-
fail(`plugin
|
|
164
|
+
fail(`plugin ${operation}: duplicate digest expectation`);
|
|
204
165
|
}
|
|
205
166
|
const value = args[index + 1];
|
|
206
167
|
if (!value || value.startsWith("--")) {
|
|
207
|
-
fail(`plugin
|
|
168
|
+
fail(`plugin ${operation}: ${arg} requires a value`);
|
|
208
169
|
}
|
|
209
170
|
if (!PLUGIN_DIGEST_PATTERN.test(value)) {
|
|
210
|
-
fail(`plugin
|
|
171
|
+
fail(`plugin ${operation}: ${arg} must be a sha256 digest`);
|
|
211
172
|
}
|
|
212
173
|
expectedDigest = value;
|
|
213
174
|
index += 1;
|
|
214
175
|
continue;
|
|
215
176
|
}
|
|
216
177
|
if (!arg.startsWith("--")) {
|
|
217
|
-
fail(`plugin
|
|
178
|
+
fail(`plugin ${operation}: unexpected argument ${arg}`);
|
|
218
179
|
}
|
|
219
180
|
if (!allowedFlags.has(arg)) {
|
|
220
|
-
fail(`plugin
|
|
181
|
+
fail(`plugin ${operation}: unknown option ${arg}`);
|
|
221
182
|
}
|
|
222
183
|
if (flags.has(arg)) {
|
|
223
|
-
fail(`plugin
|
|
184
|
+
fail(`plugin ${operation}: duplicate option ${arg}`);
|
|
224
185
|
}
|
|
225
186
|
flags.add(arg);
|
|
226
187
|
}
|
|
227
188
|
return {
|
|
228
189
|
target,
|
|
229
190
|
force: flags.has("--force"),
|
|
230
|
-
grantAll: flags.has("--grant-all"),
|
|
231
|
-
allowScripts: flags.has("--allow-scripts"),
|
|
232
|
-
restart: flags.has("--restart"),
|
|
233
191
|
json: flags.has("--json"),
|
|
234
192
|
...(expectedDigest ? { expectedDigest } : {}),
|
|
235
193
|
};
|
|
@@ -240,19 +198,8 @@ function printPreparation(preparation) {
|
|
|
240
198
|
: "";
|
|
241
199
|
console.log(`Prepared plugin "${preparation.artifact.id}"${version}.`);
|
|
242
200
|
console.log(`Artifact digest: ${preparation.artifact.digest}`);
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}
|
|
246
|
-
printCapabilities("New capabilities", preparation.capabilityDiff.added);
|
|
247
|
-
printCapabilities("Retained capabilities", preparation.capabilityDiff.retained);
|
|
248
|
-
printCapabilities("Removed capabilities", preparation.capabilityDiff.removed);
|
|
249
|
-
}
|
|
250
|
-
function printCapabilities(label, capabilities) {
|
|
251
|
-
if (capabilities.length === 0)
|
|
252
|
-
return;
|
|
253
|
-
console.log(`${label}:`);
|
|
254
|
-
for (const capability of capabilities)
|
|
255
|
-
console.log(` - ${capability}`);
|
|
201
|
+
console.log(`Source: ${preparation.artifact.source}`);
|
|
202
|
+
console.log(`Runtime: ${preparation.artifact.hasRuntime ? "declared" : "none"}`);
|
|
256
203
|
}
|
|
257
204
|
function publicPreparation(preparation) {
|
|
258
205
|
const { token: _token, ...publicFields } = preparation;
|
|
@@ -272,9 +219,6 @@ async function confirmInTerminal(message) {
|
|
|
272
219
|
prompt.close();
|
|
273
220
|
}
|
|
274
221
|
}
|
|
275
|
-
function messageOf(error) {
|
|
276
|
-
return error instanceof Error ? error.message : String(error);
|
|
277
|
-
}
|
|
278
222
|
async function readStdinBounded(maxBytes) {
|
|
279
223
|
const chunks = [];
|
|
280
224
|
let bytes = 0;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runSessionCommand(args: readonly string[]): Promise<number>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { callResidentRuntime } from "../control-client.js";
|
|
3
|
+
import { fail } from "./errors.js";
|
|
4
|
+
export async function runSessionCommand(args) {
|
|
5
|
+
const [command, sourceSessionId, ...options] = args;
|
|
6
|
+
if (command !== "fork" || !sourceSessionId) {
|
|
7
|
+
fail("Usage: rynx session fork <session-id> [--title <title>] [--json]");
|
|
8
|
+
}
|
|
9
|
+
let title;
|
|
10
|
+
let json = false;
|
|
11
|
+
for (let index = 0; index < options.length; index += 1) {
|
|
12
|
+
const option = options[index];
|
|
13
|
+
if (option === "--json") {
|
|
14
|
+
if (json)
|
|
15
|
+
fail("session fork: duplicate option --json");
|
|
16
|
+
json = true;
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
if (option === "--title") {
|
|
20
|
+
if (title !== undefined)
|
|
21
|
+
fail("session fork: duplicate option --title");
|
|
22
|
+
title = options[++index];
|
|
23
|
+
if (!title?.trim())
|
|
24
|
+
fail("session fork: --title requires a value");
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
fail(`session fork: unknown option ${option}`);
|
|
28
|
+
}
|
|
29
|
+
const result = await callResidentRuntime("local", "session.fork", {
|
|
30
|
+
sourceSessionId,
|
|
31
|
+
operationId: `cli_${randomUUID()}`,
|
|
32
|
+
...(title ? { title: title.trim() } : {}),
|
|
33
|
+
});
|
|
34
|
+
console.log(json
|
|
35
|
+
? JSON.stringify(result)
|
|
36
|
+
: result.sessionId);
|
|
37
|
+
return 0;
|
|
38
|
+
}
|