@workermill/agent 0.2.2 → 0.3.0

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
@@ -13,12 +13,14 @@ import { stopCommand } from "./commands/stop.js";
13
13
  import { statusCommand } from "./commands/status.js";
14
14
  import { logsCommand } from "./commands/logs.js";
15
15
  import { pullCommand } from "./commands/pull.js";
16
+ import { updateCommand } from "./commands/update.js";
16
17
  import { getConfigFile } from "./config.js";
18
+ import { AGENT_VERSION } from "./version.js";
17
19
  const program = new Command();
18
20
  program
19
21
  .name("workermill-agent")
20
22
  .description("WorkerMill Remote Agent - Run AI workers locally with your Claude Max subscription")
21
- .version("0.1.0");
23
+ .version(AGENT_VERSION);
22
24
  program
23
25
  .command("setup")
24
26
  .description("Interactive setup wizard - configure API key, validate prerequisites, pull worker image")
@@ -45,6 +47,10 @@ program
45
47
  .command("pull")
46
48
  .description("Pull the latest worker Docker image")
47
49
  .action(pullCommand);
50
+ program
51
+ .command("update")
52
+ .description("Update the agent to the latest version")
53
+ .action(updateCommand);
48
54
  // If no command given, auto-detect: run setup if no config, otherwise start
49
55
  if (process.argv.length <= 2) {
50
56
  if (existsSync(getConfigFile())) {
@@ -0,0 +1 @@
1
+ export declare function updateCommand(): Promise<void>;
@@ -0,0 +1,20 @@
1
+ import chalk from "chalk";
2
+ import { AGENT_VERSION } from "../version.js";
3
+ import { selfUpdate } from "../updater.js";
4
+ export async function updateCommand() {
5
+ console.log();
6
+ console.log(chalk.bold.cyan(" WorkerMill Agent Update"));
7
+ console.log(chalk.dim(" ─────────────────────────────────────"));
8
+ console.log(` ${chalk.dim("Current version:")} ${AGENT_VERSION}`);
9
+ console.log();
10
+ const success = await selfUpdate();
11
+ if (success) {
12
+ console.log();
13
+ console.log(chalk.green(" Update complete. If the agent is running, restart it to use the new version."));
14
+ }
15
+ else {
16
+ console.log();
17
+ console.error(chalk.red(" Update failed. Try running manually: npm install -g @workermill/agent@latest"));
18
+ process.exitCode = 1;
19
+ }
20
+ }
package/dist/index.js CHANGED
@@ -8,6 +8,8 @@ import chalk from "chalk";
8
8
  import { initApi, api } from "./api.js";
9
9
  import { startPolling, startHeartbeat } from "./poller.js";
10
10
  import { stopAll } from "./spawner.js";
11
+ import { AGENT_VERSION } from "./version.js";
12
+ import { selfUpdate, restartAgent } from "./updater.js";
11
13
  export { loadConfig, loadConfigFromFile, validatePrerequisites, getSystemInfo, findClaudePath } from "./config.js";
12
14
  /**
13
15
  * Start the remote agent with the given config.
@@ -47,10 +49,25 @@ export async function startAgent(config) {
47
49
  }
48
50
  // Register agent
49
51
  try {
50
- await api.post("/api/agent/register", {
52
+ const registerResponse = await api.post("/api/agent/register", {
51
53
  agentId: config.agentId,
52
54
  maxWorkers: config.maxWorkers,
55
+ agentVersion: AGENT_VERSION,
53
56
  });
57
+ const { updateAvailable, updateRequired, latestVersion } = registerResponse.data;
58
+ if (updateRequired) {
59
+ console.log(chalk.red(` ⚠ Agent update required (current: ${AGENT_VERSION}, required: ${latestVersion})`));
60
+ const success = await selfUpdate();
61
+ if (success) {
62
+ restartAgent();
63
+ }
64
+ else {
65
+ console.log(chalk.yellow(" Auto-update failed. Run: npm install -g @workermill/agent@latest"));
66
+ }
67
+ }
68
+ else if (updateAvailable) {
69
+ console.log(chalk.yellow(` Update available: ${latestVersion} (current: ${AGENT_VERSION}). Run: workermill-agent update`));
70
+ }
54
71
  }
55
72
  catch {
56
73
  // Registration is best-effort, don't fail startup
package/dist/poller.js CHANGED
@@ -8,10 +8,15 @@ import chalk from "chalk";
8
8
  import { api } from "./api.js";
9
9
  import { planTask } from "./planner.js";
10
10
  import { spawnWorker, getActiveCount, getActiveTaskIds, stopTask } from "./spawner.js";
11
+ import { AGENT_VERSION } from "./version.js";
12
+ import { selfUpdate, restartAgent } from "./updater.js";
11
13
  // Track tasks currently being planned (to avoid double-dispatching)
12
14
  const planningInProgress = new Set();
13
15
  // Cached org config
14
16
  let orgConfig = null;
17
+ // Flags to avoid spamming update notices every heartbeat
18
+ let updateNoticePrinted = false;
19
+ let updateInProgress = false;
15
20
  /** Timestamp prefix for log lines */
16
21
  function ts() {
17
22
  return chalk.dim(new Date().toLocaleTimeString());
@@ -36,6 +41,9 @@ async function getOrgConfig() {
36
41
  * Run a single poll iteration.
37
42
  */
38
43
  async function pollOnce(config) {
44
+ // Skip polling when a required update is in progress
45
+ if (updateInProgress)
46
+ return;
39
47
  try {
40
48
  const response = await api.get("/api/agent/poll", {
41
49
  params: { agentId: config.agentId },
@@ -174,6 +182,7 @@ export function startHeartbeat(config) {
174
182
  const response = await api.post("/api/agent/heartbeat", {
175
183
  agentId: config.agentId,
176
184
  activeTasks: activeTaskIds,
185
+ agentVersion: AGENT_VERSION,
177
186
  });
178
187
  // Stop containers for tasks cancelled via the cloud dashboard
179
188
  const cancelledTasks = response.data?.cancelledTasks;
@@ -182,6 +191,25 @@ export function startHeartbeat(config) {
182
191
  stopTask(taskId);
183
192
  }
184
193
  }
194
+ // Handle update signals from server
195
+ const { updateAvailable, updateRequired, latestVersion } = response.data ?? {};
196
+ if (updateRequired && !updateInProgress) {
197
+ updateInProgress = true;
198
+ console.log(`${ts()} ${chalk.red("⚠ Agent update required")} (current: ${AGENT_VERSION}, required: ${latestVersion})`);
199
+ console.log(`${ts()} ${chalk.yellow("Refusing new tasks until updated.")}`);
200
+ const success = await selfUpdate();
201
+ if (success) {
202
+ restartAgent();
203
+ }
204
+ else {
205
+ console.log(`${ts()} ${chalk.red("Auto-update failed.")} Run: npm install -g @workermill/agent@latest`);
206
+ updateInProgress = false;
207
+ }
208
+ }
209
+ else if (updateAvailable && !updateNoticePrinted && !updateRequired) {
210
+ updateNoticePrinted = true;
211
+ console.log(`${ts()} ${chalk.yellow(`Update available: ${latestVersion}`)} (current: ${AGENT_VERSION}). Run: workermill-agent update`);
212
+ }
185
213
  }
186
214
  catch {
187
215
  // Heartbeat failures are non-critical
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Run `npm install -g @workermill/agent@latest` and return whether it succeeded.
3
+ */
4
+ export declare function selfUpdate(): Promise<boolean>;
5
+ /**
6
+ * Restart the current process by spawning a new one and exiting.
7
+ */
8
+ export declare function restartAgent(): never;
@@ -0,0 +1,40 @@
1
+ import { spawn } from "child_process";
2
+ import chalk from "chalk";
3
+ /**
4
+ * Run `npm install -g @workermill/agent@latest` and return whether it succeeded.
5
+ */
6
+ export async function selfUpdate() {
7
+ console.log(chalk.cyan(" Updating @workermill/agent..."));
8
+ return new Promise((resolve) => {
9
+ const child = spawn("npm", ["install", "-g", "@workermill/agent@latest"], {
10
+ stdio: "inherit",
11
+ shell: true,
12
+ });
13
+ child.on("error", (err) => {
14
+ console.error(chalk.red(` Update failed: ${err.message}`));
15
+ resolve(false);
16
+ });
17
+ child.on("close", (code) => {
18
+ if (code === 0) {
19
+ console.log(chalk.green(" Update successful."));
20
+ resolve(true);
21
+ }
22
+ else {
23
+ console.error(chalk.red(` Update failed with exit code ${code}`));
24
+ resolve(false);
25
+ }
26
+ });
27
+ });
28
+ }
29
+ /**
30
+ * Restart the current process by spawning a new one and exiting.
31
+ */
32
+ export function restartAgent() {
33
+ console.log(chalk.cyan(" Restarting agent..."));
34
+ const child = spawn(process.argv[0], process.argv.slice(1), {
35
+ stdio: "inherit",
36
+ detached: true,
37
+ });
38
+ child.unref();
39
+ process.exit(0);
40
+ }
@@ -0,0 +1 @@
1
+ export declare const AGENT_VERSION: string;
@@ -0,0 +1,4 @@
1
+ import { createRequire } from "module";
2
+ const require = createRequire(import.meta.url);
3
+ const pkg = require("../package.json");
4
+ export const AGENT_VERSION = pkg.version;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@workermill/agent",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "WorkerMill Remote Agent - Run AI workers locally with your Claude Max subscription",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",