maqcli 0.6.0 → 0.6.2
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/core/command-catalog.js +1 -0
- package/dist/core/launcher.js +1 -1
- package/dist/core/update.d.ts +119 -0
- package/dist/core/update.js +185 -0
- package/dist/index.js +61 -1
- package/dist/server/daemon.js +1 -1
- package/package.json +1 -1
|
@@ -42,6 +42,7 @@ export const maqCommands = [
|
|
|
42
42
|
{ name: "flow", category: "control", summary: "Scheduled agent sessions (run under the daemon).", usage: "maq flow [list|add|remove]", needsInput: "none", args: [] },
|
|
43
43
|
{ name: "audit", category: "control", summary: "Verify a run's hash-chained audit log.", usage: "maq audit verify <run-dir>", needsInput: "none", args: [] },
|
|
44
44
|
{ name: "security", category: "system", summary: "Enforced security rules (protected paths, egress allowlist, injection scanning) + recent findings.", usage: "maq security [report|rules|scan <path>]", needsInput: "none", args: [] },
|
|
45
|
+
{ name: "update", category: "system", summary: "Check npm for a newer maqcli and self-update.", usage: "maq update [--check]", needsInput: "none", args: [{ name: "check", type: "boolean", description: "only check, don't install" }] },
|
|
45
46
|
];
|
|
46
47
|
/** Catalog of AI worker CLIs' own commands/flags (verified 2026-07-01). */
|
|
47
48
|
export const aiCliCatalog = [
|
package/dist/core/launcher.js
CHANGED
|
@@ -353,7 +353,7 @@ async function connectMobile(rl) {
|
|
|
353
353
|
async function launchUi(authKey) {
|
|
354
354
|
// Reuse the daemon; open its landing page. Import lazily to avoid a cycle.
|
|
355
355
|
const { createDaemon } = await import("../server/daemon.js");
|
|
356
|
-
const daemon = createDaemon({ token: authKey, version: "0.6.
|
|
356
|
+
const daemon = createDaemon({ token: authKey, version: "0.6.2" });
|
|
357
357
|
try {
|
|
358
358
|
const { host, port } = await daemon.listen();
|
|
359
359
|
const url = `http://${host}:${port}/`;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Self-update — lets a user run `maq update` from inside the CLI instead of
|
|
3
|
+
* remembering the npm incantation. Checks the npm registry for the latest
|
|
4
|
+
* published version, compares it to the running version, and (unless
|
|
5
|
+
* --check-only) re-installs globally via npm.
|
|
6
|
+
*
|
|
7
|
+
* Network access here is a deliberate, user-initiated exception to the
|
|
8
|
+
* default-deny egress posture (core/security.ts) — it only ever talks to the
|
|
9
|
+
* npm registry, and only when the user explicitly runs this command.
|
|
10
|
+
*
|
|
11
|
+
* THE WINDOWS FILE-LOCK PROBLEM AND ITS FIX
|
|
12
|
+
* ------------------------------------------
|
|
13
|
+
* `npm install -g` has to overwrite the `maq`/`maqcli` shim and the running
|
|
14
|
+
* package's files. If that `npm install` runs as a CHILD of the currently
|
|
15
|
+
* executing `maq` process (i.e. `maq` spawns it and waits), Windows can hold
|
|
16
|
+
* file/handle locks on the very binary being replaced for as long as the
|
|
17
|
+
* parent process tree is alive — npm's overwrite can fail or silently leave a
|
|
18
|
+
* half-updated install. Waiting-for-the-child is the actual cause, not npm
|
|
19
|
+
* itself.
|
|
20
|
+
*
|
|
21
|
+
* The fix: spawn the npm install fully DETACHED (`detached: true`,
|
|
22
|
+
* `stdio: "ignore"`, `.unref()`) so it is not a child of `maq` in any way
|
|
23
|
+
* meaningful to the OS's lock semantics, then have `maq` EXIT IMMEDIATELY
|
|
24
|
+
* instead of waiting on it. By the time npm actually touches the shim files,
|
|
25
|
+
* the process that used to hold them has already terminated. This is the same
|
|
26
|
+
* pattern self-updating CLIs (rustup, deno, bun) use on Windows.
|
|
27
|
+
*/
|
|
28
|
+
import { spawn } from "node:child_process";
|
|
29
|
+
export interface VersionInfo {
|
|
30
|
+
current: string;
|
|
31
|
+
latest: string | null;
|
|
32
|
+
upToDate: boolean;
|
|
33
|
+
error?: string;
|
|
34
|
+
}
|
|
35
|
+
/** Returns true if `latest` is strictly newer than `current`. */
|
|
36
|
+
export declare function isNewer(current: string, latest: string): boolean;
|
|
37
|
+
/** Query the npm registry for the latest published version of `maqcli`. */
|
|
38
|
+
export declare function fetchLatestVersion(registryUrl?: string, timeoutMs?: number): Promise<string>;
|
|
39
|
+
/** Compare the running version against the registry; never throws. */
|
|
40
|
+
export declare function checkForUpdate(currentVersion: string): Promise<VersionInfo>;
|
|
41
|
+
export interface UpdateOutcome {
|
|
42
|
+
ok: boolean;
|
|
43
|
+
message: string;
|
|
44
|
+
version?: VersionInfo;
|
|
45
|
+
/** Set when the update was launched detached; the caller should exit(0) promptly instead of doing more work. */
|
|
46
|
+
detachedPid?: number;
|
|
47
|
+
/** Path a --wait caller can poll to see the detached install's result. */
|
|
48
|
+
logPath?: string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Build the OS-appropriate command to run `npm install -g maqcli@<version>`
|
|
52
|
+
* fully detached from the current process (see module doc for why). On
|
|
53
|
+
* Windows this wraps npm in a new cmd.exe so the child truly detaches from
|
|
54
|
+
* the parent's console/job object; on POSIX, npm itself is spawned directly
|
|
55
|
+
* with `detached: true`.
|
|
56
|
+
*/
|
|
57
|
+
export declare function detachedInstallCommand(version: string, logPath: string, platform?: NodeJS.Platform): {
|
|
58
|
+
cmd: string;
|
|
59
|
+
args: string[];
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Launch the update fully detached and return immediately — the caller
|
|
63
|
+
* (cmdUpdate) should print guidance and exit right away rather than await
|
|
64
|
+
* anything else, which is what avoids the Windows file-lock failure mode.
|
|
65
|
+
* `spawner` is injectable for tests so nothing is actually spawned there.
|
|
66
|
+
*/
|
|
67
|
+
export declare function launchDetachedUpdate(version: string, logPath: string, spawner?: typeof spawn): number | undefined;
|
|
68
|
+
export interface UpdateOutcome {
|
|
69
|
+
ok: boolean;
|
|
70
|
+
message: string;
|
|
71
|
+
version?: VersionInfo;
|
|
72
|
+
/** Set when the update was launched detached; the caller should exit(0) promptly instead of doing more work. */
|
|
73
|
+
detachedPid?: number;
|
|
74
|
+
/** Path a caller can inspect afterward to see the detached install's own npm output. */
|
|
75
|
+
logPath?: string;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Perform the update: check the registry, and if a newer version exists,
|
|
79
|
+
* launch `npm install -g maqcli@<latest>` DETACHED from this process (see the
|
|
80
|
+
* module doc — this is what avoids the Windows file-lock failure mode where
|
|
81
|
+
* npm can't overwrite files the running `maq` process still holds open).
|
|
82
|
+
*
|
|
83
|
+
* The detached path is the default and recommended flow: this function
|
|
84
|
+
* returns as soon as the install is launched; it does not wait for npm to
|
|
85
|
+
* finish, because waiting would defeat the point (the parent would still be
|
|
86
|
+
* alive, still holding the lock, for the whole duration).
|
|
87
|
+
*
|
|
88
|
+
* `opts.wait: true` (used by tests, and by --wait for a CI/non-interactive
|
|
89
|
+
* caller that can tolerate the lock risk) runs synchronously via `runner`
|
|
90
|
+
* instead, so the outcome is available in the same process.
|
|
91
|
+
*/
|
|
92
|
+
export declare function performUpdate(currentVersion: string, opts?: {
|
|
93
|
+
checkOnly?: boolean;
|
|
94
|
+
/** Run synchronously in-process instead of detaching (tests; --wait). */
|
|
95
|
+
wait?: boolean;
|
|
96
|
+
runner?: (cmd: string, args: string[]) => Promise<{
|
|
97
|
+
code: number | null;
|
|
98
|
+
stdout: string;
|
|
99
|
+
stderr: string;
|
|
100
|
+
}>;
|
|
101
|
+
spawner?: typeof spawn;
|
|
102
|
+
logPath?: string;
|
|
103
|
+
}): Promise<UpdateOutcome>;
|
|
104
|
+
/**
|
|
105
|
+
* Read a detached update's log file (best-effort). Used by `maq update
|
|
106
|
+
* --status` to show what the background install actually did, since the
|
|
107
|
+
* `maq update` invocation that launched it already exited.
|
|
108
|
+
*/
|
|
109
|
+
export declare function readUpdateLog(logPath: string): Promise<string | null>;
|
|
110
|
+
/**
|
|
111
|
+
* Ask npm what is ACTUALLY installed globally right now (ground truth, not
|
|
112
|
+
* "what did we ask for") — this is what confirms a detached update landed,
|
|
113
|
+
* independent of which process/terminal you're asking from.
|
|
114
|
+
*/
|
|
115
|
+
export declare function installedGlobalVersion(runner?: (cmd: string, args: string[]) => Promise<{
|
|
116
|
+
code: number | null;
|
|
117
|
+
stdout: string;
|
|
118
|
+
stderr: string;
|
|
119
|
+
}>): Promise<string | null>;
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Self-update — lets a user run `maq update` from inside the CLI instead of
|
|
3
|
+
* remembering the npm incantation. Checks the npm registry for the latest
|
|
4
|
+
* published version, compares it to the running version, and (unless
|
|
5
|
+
* --check-only) re-installs globally via npm.
|
|
6
|
+
*
|
|
7
|
+
* Network access here is a deliberate, user-initiated exception to the
|
|
8
|
+
* default-deny egress posture (core/security.ts) — it only ever talks to the
|
|
9
|
+
* npm registry, and only when the user explicitly runs this command.
|
|
10
|
+
*
|
|
11
|
+
* THE WINDOWS FILE-LOCK PROBLEM AND ITS FIX
|
|
12
|
+
* ------------------------------------------
|
|
13
|
+
* `npm install -g` has to overwrite the `maq`/`maqcli` shim and the running
|
|
14
|
+
* package's files. If that `npm install` runs as a CHILD of the currently
|
|
15
|
+
* executing `maq` process (i.e. `maq` spawns it and waits), Windows can hold
|
|
16
|
+
* file/handle locks on the very binary being replaced for as long as the
|
|
17
|
+
* parent process tree is alive — npm's overwrite can fail or silently leave a
|
|
18
|
+
* half-updated install. Waiting-for-the-child is the actual cause, not npm
|
|
19
|
+
* itself.
|
|
20
|
+
*
|
|
21
|
+
* The fix: spawn the npm install fully DETACHED (`detached: true`,
|
|
22
|
+
* `stdio: "ignore"`, `.unref()`) so it is not a child of `maq` in any way
|
|
23
|
+
* meaningful to the OS's lock semantics, then have `maq` EXIT IMMEDIATELY
|
|
24
|
+
* instead of waiting on it. By the time npm actually touches the shim files,
|
|
25
|
+
* the process that used to hold them has already terminated. This is the same
|
|
26
|
+
* pattern self-updating CLIs (rustup, deno, bun) use on Windows.
|
|
27
|
+
*/
|
|
28
|
+
import { spawn } from "node:child_process";
|
|
29
|
+
import { tmpdir } from "node:os";
|
|
30
|
+
import { join } from "node:path";
|
|
31
|
+
import { execSafe } from "./exec.js";
|
|
32
|
+
/** Parse "x.y.z" into a comparable tuple; non-numeric parts sort as 0. */
|
|
33
|
+
function parseSemver(v) {
|
|
34
|
+
const parts = v.replace(/^v/, "").split(".").map((p) => parseInt(p, 10));
|
|
35
|
+
return [parts[0] || 0, parts[1] || 0, parts[2] || 0];
|
|
36
|
+
}
|
|
37
|
+
/** Returns true if `latest` is strictly newer than `current`. */
|
|
38
|
+
export function isNewer(current, latest) {
|
|
39
|
+
const a = parseSemver(current);
|
|
40
|
+
const b = parseSemver(latest);
|
|
41
|
+
for (let i = 0; i < 3; i++) {
|
|
42
|
+
if (b[i] > a[i])
|
|
43
|
+
return true;
|
|
44
|
+
if (b[i] < a[i])
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
/** Query the npm registry for the latest published version of `maqcli`. */
|
|
50
|
+
export async function fetchLatestVersion(registryUrl = "https://registry.npmjs.org/maqcli/latest", timeoutMs = 8000) {
|
|
51
|
+
const res = await fetch(registryUrl, { signal: AbortSignal.timeout(timeoutMs) });
|
|
52
|
+
if (!res.ok)
|
|
53
|
+
throw new Error(`registry returned ${res.status}`);
|
|
54
|
+
const body = (await res.json());
|
|
55
|
+
if (!body.version)
|
|
56
|
+
throw new Error("registry response missing a version field");
|
|
57
|
+
return body.version;
|
|
58
|
+
}
|
|
59
|
+
/** Compare the running version against the registry; never throws. */
|
|
60
|
+
export async function checkForUpdate(currentVersion) {
|
|
61
|
+
try {
|
|
62
|
+
const latest = await fetchLatestVersion();
|
|
63
|
+
return { current: currentVersion, latest, upToDate: !isNewer(currentVersion, latest) };
|
|
64
|
+
}
|
|
65
|
+
catch (e) {
|
|
66
|
+
return { current: currentVersion, latest: null, upToDate: true, error: e.message };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Build the OS-appropriate command to run `npm install -g maqcli@<version>`
|
|
71
|
+
* fully detached from the current process (see module doc for why). On
|
|
72
|
+
* Windows this wraps npm in a new cmd.exe so the child truly detaches from
|
|
73
|
+
* the parent's console/job object; on POSIX, npm itself is spawned directly
|
|
74
|
+
* with `detached: true`.
|
|
75
|
+
*/
|
|
76
|
+
export function detachedInstallCommand(version, logPath, platform = process.platform) {
|
|
77
|
+
const pkg = `maqcli@${version}`;
|
|
78
|
+
if (platform === "win32") {
|
|
79
|
+
// /c runs and closes; redirection captures npm's own output for later inspection.
|
|
80
|
+
return { cmd: "cmd.exe", args: ["/c", `npm install -g ${pkg} > "${logPath}" 2>&1`] };
|
|
81
|
+
}
|
|
82
|
+
return { cmd: "sh", args: ["-c", `npm install -g ${pkg} > '${logPath}' 2>&1`] };
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Launch the update fully detached and return immediately — the caller
|
|
86
|
+
* (cmdUpdate) should print guidance and exit right away rather than await
|
|
87
|
+
* anything else, which is what avoids the Windows file-lock failure mode.
|
|
88
|
+
* `spawner` is injectable for tests so nothing is actually spawned there.
|
|
89
|
+
*/
|
|
90
|
+
export function launchDetachedUpdate(version, logPath, spawner = spawn) {
|
|
91
|
+
const { cmd, args } = detachedInstallCommand(version, logPath);
|
|
92
|
+
const child = spawner(cmd, args, { detached: true, stdio: "ignore", windowsHide: true });
|
|
93
|
+
child.on("error", () => {
|
|
94
|
+
/* best-effort; the log file (or its absence) is the source of truth */
|
|
95
|
+
});
|
|
96
|
+
child.unref();
|
|
97
|
+
return child.pid;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Perform the update: check the registry, and if a newer version exists,
|
|
101
|
+
* launch `npm install -g maqcli@<latest>` DETACHED from this process (see the
|
|
102
|
+
* module doc — this is what avoids the Windows file-lock failure mode where
|
|
103
|
+
* npm can't overwrite files the running `maq` process still holds open).
|
|
104
|
+
*
|
|
105
|
+
* The detached path is the default and recommended flow: this function
|
|
106
|
+
* returns as soon as the install is launched; it does not wait for npm to
|
|
107
|
+
* finish, because waiting would defeat the point (the parent would still be
|
|
108
|
+
* alive, still holding the lock, for the whole duration).
|
|
109
|
+
*
|
|
110
|
+
* `opts.wait: true` (used by tests, and by --wait for a CI/non-interactive
|
|
111
|
+
* caller that can tolerate the lock risk) runs synchronously via `runner`
|
|
112
|
+
* instead, so the outcome is available in the same process.
|
|
113
|
+
*/
|
|
114
|
+
export async function performUpdate(currentVersion, opts = {}) {
|
|
115
|
+
const version = await checkForUpdate(currentVersion);
|
|
116
|
+
if (version.error) {
|
|
117
|
+
return { ok: false, message: `could not check for updates: ${version.error}`, version };
|
|
118
|
+
}
|
|
119
|
+
if (version.upToDate) {
|
|
120
|
+
return { ok: true, message: `already up to date (v${version.current})`, version };
|
|
121
|
+
}
|
|
122
|
+
if (opts.checkOnly) {
|
|
123
|
+
return { ok: true, message: `update available: v${version.current} -> v${version.latest}`, version };
|
|
124
|
+
}
|
|
125
|
+
const latest = version.latest;
|
|
126
|
+
if (!opts.wait) {
|
|
127
|
+
const logPath = opts.logPath ?? defaultLogPath();
|
|
128
|
+
const pid = launchDetachedUpdate(latest, logPath, opts.spawner);
|
|
129
|
+
return {
|
|
130
|
+
ok: true,
|
|
131
|
+
message: `update to v${latest} launched in the background (detached, avoids file locks). Open a new terminal in a few seconds to pick it up.`,
|
|
132
|
+
version,
|
|
133
|
+
detachedPid: pid,
|
|
134
|
+
logPath,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
// --wait / test path: run synchronously in this process. On Windows this
|
|
138
|
+
// can hit the file-lock failure mode described above if `maq` itself is
|
|
139
|
+
// the thing being replaced; it's kept for CI and for tests with a stubbed
|
|
140
|
+
// runner, not as the default interactive experience.
|
|
141
|
+
const runner = opts.runner ?? (async (cmd, args) => execSafe(cmd, args, { timeoutMs: 120000 }));
|
|
142
|
+
const outcome = await runner("npm", ["install", "-g", `maqcli@${latest}`]);
|
|
143
|
+
if (outcome.code !== 0) {
|
|
144
|
+
return {
|
|
145
|
+
ok: false,
|
|
146
|
+
message: `update to v${latest} failed (exit ${outcome.code}): ${outcome.stderr.slice(0, 400) || outcome.stdout.slice(0, 400)}`,
|
|
147
|
+
version,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
return { ok: true, message: `updated v${version.current} -> v${latest}`, version };
|
|
151
|
+
}
|
|
152
|
+
function defaultLogPath() {
|
|
153
|
+
return join(tmpdir(), `maq-update-${Date.now()}.log`);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Read a detached update's log file (best-effort). Used by `maq update
|
|
157
|
+
* --status` to show what the background install actually did, since the
|
|
158
|
+
* `maq update` invocation that launched it already exited.
|
|
159
|
+
*/
|
|
160
|
+
export async function readUpdateLog(logPath) {
|
|
161
|
+
try {
|
|
162
|
+
const { readFile } = await import("node:fs/promises");
|
|
163
|
+
return await readFile(logPath, "utf8");
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Ask npm what is ACTUALLY installed globally right now (ground truth, not
|
|
171
|
+
* "what did we ask for") — this is what confirms a detached update landed,
|
|
172
|
+
* independent of which process/terminal you're asking from.
|
|
173
|
+
*/
|
|
174
|
+
export async function installedGlobalVersion(runner = (cmd, args) => execSafe(cmd, args, { timeoutMs: 15000 })) {
|
|
175
|
+
const outcome = await runner("npm", ["list", "-g", "maqcli", "--depth=0", "--json"]);
|
|
176
|
+
if (outcome.code !== 0)
|
|
177
|
+
return null;
|
|
178
|
+
try {
|
|
179
|
+
const parsed = JSON.parse(outcome.stdout);
|
|
180
|
+
return parsed.dependencies?.maqcli?.version ?? null;
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -43,9 +43,10 @@ import { runInit } from "./core/init-wizard.js";
|
|
|
43
43
|
import { CostTracker } from "./core/cost-tracker.js";
|
|
44
44
|
import { runLauncher } from "./core/launcher.js";
|
|
45
45
|
import { runOrchestration } from "./core/orchestrator.js";
|
|
46
|
+
import { performUpdate, installedGlobalVersion } from "./core/update.js";
|
|
46
47
|
import { checkProtectedPath, scanForInjection, securityLog, securityRules, } from "./core/security.js";
|
|
47
48
|
import { readFileSync, statSync } from "node:fs";
|
|
48
|
-
const VERSION = "0.6.
|
|
49
|
+
const VERSION = "0.6.2";
|
|
49
50
|
async function main(argv) {
|
|
50
51
|
const [command, ...rest] = argv;
|
|
51
52
|
switch (command) {
|
|
@@ -79,6 +80,8 @@ async function main(argv) {
|
|
|
79
80
|
return cmdOrchestrate(rest);
|
|
80
81
|
case "security":
|
|
81
82
|
return cmdSecurity(rest);
|
|
83
|
+
case "update":
|
|
84
|
+
return cmdUpdate(rest);
|
|
82
85
|
case "verify":
|
|
83
86
|
return cmdVerify(rest);
|
|
84
87
|
case "serve":
|
|
@@ -426,6 +429,62 @@ function cmdSecurity(args) {
|
|
|
426
429
|
return 0;
|
|
427
430
|
}
|
|
428
431
|
/** Machine-readable rule set — shared shape with the daemon's GET /v1/security. */
|
|
432
|
+
async function cmdUpdate(args) {
|
|
433
|
+
const { values } = parseArgs({
|
|
434
|
+
args,
|
|
435
|
+
options: {
|
|
436
|
+
...commonFlags(),
|
|
437
|
+
check: { type: "boolean", default: false },
|
|
438
|
+
wait: { type: "boolean", default: false },
|
|
439
|
+
status: { type: "boolean", default: false },
|
|
440
|
+
},
|
|
441
|
+
allowPositionals: true,
|
|
442
|
+
});
|
|
443
|
+
if (values.status) {
|
|
444
|
+
const installed = await installedGlobalVersion();
|
|
445
|
+
if (values.json) {
|
|
446
|
+
logger.out(JSON.stringify({ runningVersion: VERSION, installedGlobalVersion: installed }, null, 0));
|
|
447
|
+
return 0;
|
|
448
|
+
}
|
|
449
|
+
logger.out(`this terminal's maq: v${VERSION}`);
|
|
450
|
+
logger.out(`globally installed maq: ${installed ? "v" + installed : "unknown (npm list failed)"}`);
|
|
451
|
+
if (installed && installed !== VERSION) {
|
|
452
|
+
logger.out("\nThese differ — a background update likely finished. Open a NEW terminal window");
|
|
453
|
+
logger.out("(this one is still running the old code in memory) to pick up the new version.");
|
|
454
|
+
}
|
|
455
|
+
else if (installed) {
|
|
456
|
+
logger.out("\nUp to date and consistent.");
|
|
457
|
+
}
|
|
458
|
+
return 0;
|
|
459
|
+
}
|
|
460
|
+
logger.out(`current version: ${VERSION}`);
|
|
461
|
+
logger.out(values.check ? "checking npm for a newer version…" : "checking npm and updating if a newer version is available…");
|
|
462
|
+
const outcome = await performUpdate(VERSION, { checkOnly: values.check, wait: values.wait });
|
|
463
|
+
if (values.json) {
|
|
464
|
+
logger.out(JSON.stringify(outcome, null, 0));
|
|
465
|
+
return outcome.ok ? 0 : 1;
|
|
466
|
+
}
|
|
467
|
+
logger.out(outcome.message);
|
|
468
|
+
if (outcome.ok && outcome.version && !outcome.version.upToDate) {
|
|
469
|
+
if (values.check) {
|
|
470
|
+
logger.out("run 'maq update' (without --check) to install it.");
|
|
471
|
+
}
|
|
472
|
+
else if (outcome.detachedPid) {
|
|
473
|
+
// Detached path: THIS is the fix for the Windows file-lock problem —
|
|
474
|
+
// maq is exiting now, on purpose, so nothing holds a lock on the files
|
|
475
|
+
// npm is about to overwrite. Give the user a concrete way to confirm it
|
|
476
|
+
// landed instead of leaving them guessing.
|
|
477
|
+
logger.out("");
|
|
478
|
+
logger.out(` install log: ${outcome.logPath}`);
|
|
479
|
+
logger.out(" check progress any time with: maq update --status");
|
|
480
|
+
logger.out(" (open a NEW terminal window before running that — this one will keep showing the old version)");
|
|
481
|
+
}
|
|
482
|
+
else {
|
|
483
|
+
logger.out("restart your terminal (or open a new one) to pick up the updated binary.");
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
return outcome.ok ? 0 : 1;
|
|
487
|
+
}
|
|
429
488
|
async function cmdServe(args) {
|
|
430
489
|
const { values } = parseArgs({
|
|
431
490
|
args,
|
|
@@ -1177,6 +1236,7 @@ function printHelp() {
|
|
|
1177
1236
|
' completion <shell> Shell completions (bash/zsh/fish/powershell)',
|
|
1178
1237
|
" audit verify <run-dir> Verify a run's hash-chained audit log",
|
|
1179
1238
|
" security [report|rules|scan <path>|add|remove] Enforced security rules + recent findings",
|
|
1239
|
+
" update [--check|--wait|--status] Check npm for a newer maqcli and self-update (detached; avoids file locks)",
|
|
1180
1240
|
" config [get|set|path|reset] Read or update ~/.maqcli/config.json",
|
|
1181
1241
|
" version | help [<topic>]",
|
|
1182
1242
|
"",
|
package/dist/server/daemon.js
CHANGED
|
@@ -52,7 +52,7 @@ export function createDaemon(opts = {}) {
|
|
|
52
52
|
const host = opts.host ?? process.env.MAQ_HOST ?? "127.0.0.1";
|
|
53
53
|
const port = opts.port ?? Number(process.env.MAQ_PORT ?? 7717);
|
|
54
54
|
const token = opts.token ?? process.env.MAQ_TOKEN ?? generateToken();
|
|
55
|
-
const version = opts.version ?? "0.6.
|
|
55
|
+
const version = opts.version ?? "0.6.2";
|
|
56
56
|
const corsOrigin = opts.corsOrigin ?? process.env.MAQ_CORS_ORIGIN;
|
|
57
57
|
const registry = opts.registry ?? new SessionRegistry();
|
|
58
58
|
const interactive = new InteractiveRegistry();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "maqcli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"description": "MAQ master orchestrator — a token-efficient, agent-agnostic supervisor CLI that sits on top of any worker CLI (AI or not) via a Scout -> Plan -> Execute -> Verify pipeline.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|