@duetso/agent 0.1.62 → 0.1.63
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/README.md +4 -2
- package/dist/package.json +1 -1
- package/dist/src/cli/auto-upgrade.d.ts +147 -0
- package/dist/src/cli/auto-upgrade.d.ts.map +1 -0
- package/dist/src/cli/auto-upgrade.js +314 -0
- package/dist/src/cli/auto-upgrade.js.map +1 -0
- package/dist/src/cli/help.d.ts.map +1 -1
- package/dist/src/cli/help.js +1 -0
- package/dist/src/cli/help.js.map +1 -1
- package/dist/src/cli/run.d.ts.map +1 -1
- package/dist/src/cli/run.js +29 -13
- package/dist/src/cli/run.js.map +1 -1
- package/dist/src/cli/version-check.d.ts +0 -8
- package/dist/src/cli/version-check.d.ts.map +1 -1
- package/dist/src/cli/version-check.js +5 -29
- package/dist/src/cli/version-check.js.map +1 -1
- package/dist/src/cli.d.ts +1 -1
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +1 -1
- package/dist/src/cli.js.map +1 -1
- package/dist/src/tui/app.d.ts +9 -5
- package/dist/src/tui/app.d.ts.map +1 -1
- package/dist/src/tui/app.js +26 -11
- package/dist/src/tui/app.js.map +1 -1
- package/dist/src/tui/sidebar.js +3 -3
- package/dist/src/tui/sidebar.js.map +1 -1
- package/dist/src/turn-runner/turn-runner.d.ts +17 -0
- package/dist/src/turn-runner/turn-runner.d.ts.map +1 -1
- package/dist/src/turn-runner/turn-runner.js +24 -1
- package/dist/src/turn-runner/turn-runner.js.map +1 -1
- package/dist/src/types/protocol.d.ts +8 -2
- package/dist/src/types/protocol.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -501,11 +501,13 @@ import { TurnRunner } from "@duetso/agent";
|
|
|
501
501
|
|
|
502
502
|
const turnRunner = new TurnRunner({
|
|
503
503
|
model: "opus-4.7",
|
|
504
|
-
memoryDbPath: false, //
|
|
504
|
+
memoryDbPath: false, // Disables observational memory and compaction.
|
|
505
505
|
});
|
|
506
506
|
```
|
|
507
507
|
|
|
508
|
-
By default, the CLI stores durable observations in `~/.duet/memory.db`; run it with `--incognito` (or `-i`) to
|
|
508
|
+
By default, the CLI stores durable observations in `~/.duet/memory.db`; run it with `--incognito` (or `-i`) to set `memoryDbPath: false`, which disables observational memory and compaction for that session. Programmatic callers can pass `memoryDbPath: false` or provide a custom `memoryDbPath`. The CLI's `SessionManager` is a convenience layer that stores session snapshots under `~/.duet/sessions`, but the runner owns memory hydration, pi-turn observation/reflection, compaction, and observation persistence.
|
|
509
|
+
|
|
510
|
+
**We strongly recommend running with a `memoryDbPath`, which is the default.** Compaction is implemented as part of the observational memory pipeline: raw transcript content is replaced by observations/reflections that the observer and reflector write to the durable store. Without a database, the runner skips that pipeline entirely — there is no compaction, the transcript grows unbounded against the raw model context window, and long sessions will eventually hit a provider context-length error. `memoryDbPath: false` is appropriate for short-lived scripts, tests, and incognito runs that stay well under the model window.
|
|
509
511
|
|
|
510
512
|
You can also resume directly from saved state. The runner owns state
|
|
511
513
|
internally after `start`, so resumed state is handed in through the start
|
package/dist/package.json
CHANGED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { type ChildProcess, type SpawnOptions } from "node:child_process";
|
|
2
|
+
import { type PackageManager } from "./package-manager.js";
|
|
3
|
+
/**
|
|
4
|
+
* Why this exists: the CLI ships several versions per hour. Manual `duet
|
|
5
|
+
* upgrade` lags behind, so each launch probes the registry and — when a
|
|
6
|
+
* newer version exists — runs the package manager's global install in the
|
|
7
|
+
* background while the user is already using the TUI. The next launch is
|
|
8
|
+
* then already on latest.
|
|
9
|
+
*
|
|
10
|
+
* Concurrency safety:
|
|
11
|
+
* - The package manager mutates the global install path in place. Already-
|
|
12
|
+
* running CLI processes do not re-read those files, so they are unaffected.
|
|
13
|
+
* - A CLI that *starts* mid-install could see partial files. We minimize
|
|
14
|
+
* that window with an exclusive lockfile so only one process upgrades at
|
|
15
|
+
* a time; others fall through and observe whatever state currently exists.
|
|
16
|
+
* - Stale locks (worker crash, OS kill) older than STALE_LOCK_MS are
|
|
17
|
+
* reclaimed so a missed unlink does not permanently disable upgrades.
|
|
18
|
+
*
|
|
19
|
+
* Status stream: `runAutoUpgrade` emits each transition through `onStatus`
|
|
20
|
+
* so the TUI can render a live "Checking… / Upgrading… / Upgraded, restart"
|
|
21
|
+
* line in the header. The returned promise resolves once the final state
|
|
22
|
+
* is reached.
|
|
23
|
+
*/
|
|
24
|
+
/** Env var users set to disable the auto-upgrade entirely. */
|
|
25
|
+
export declare const NO_AUTO_UPGRADE_ENV = "DUET_NO_AUTO_UPGRADE";
|
|
26
|
+
/**
|
|
27
|
+
* Live status stream emitted while the upgrade runs. Designed so the TUI
|
|
28
|
+
* can render one line that mutates in place; each variant carries the
|
|
29
|
+
* fields needed to phrase the line without re-deriving them.
|
|
30
|
+
*/
|
|
31
|
+
export type UpgradeStatus = {
|
|
32
|
+
kind: "checking";
|
|
33
|
+
} | {
|
|
34
|
+
kind: "current";
|
|
35
|
+
version: string;
|
|
36
|
+
} | {
|
|
37
|
+
kind: "upgrading";
|
|
38
|
+
from: string;
|
|
39
|
+
to: string;
|
|
40
|
+
manager: PackageManager;
|
|
41
|
+
} | {
|
|
42
|
+
kind: "upgraded";
|
|
43
|
+
from: string;
|
|
44
|
+
to: string;
|
|
45
|
+
manager: PackageManager;
|
|
46
|
+
} | {
|
|
47
|
+
kind: "failed";
|
|
48
|
+
from: string;
|
|
49
|
+
to: string;
|
|
50
|
+
manager: PackageManager;
|
|
51
|
+
error: string;
|
|
52
|
+
} | {
|
|
53
|
+
kind: "locked";
|
|
54
|
+
} | {
|
|
55
|
+
kind: "skipped";
|
|
56
|
+
reason: "disabled" | "source-checkout" | "registry-unreachable";
|
|
57
|
+
};
|
|
58
|
+
export type UpgradeStatusListener = (status: UpgradeStatus) => void;
|
|
59
|
+
/**
|
|
60
|
+
* A pub/sub handle the TUI subscribes to so it can render the live upgrade
|
|
61
|
+
* line. `publish` is called once per intermediate status; `complete` is
|
|
62
|
+
* called with the final status so late subscribers (e.g. the TUI mounting
|
|
63
|
+
* after the upgrade already finished) still see the terminal state.
|
|
64
|
+
*/
|
|
65
|
+
export interface UpgradeStatusStream {
|
|
66
|
+
subscribe(listener: UpgradeStatusListener): () => void;
|
|
67
|
+
publish(status: UpgradeStatus): void;
|
|
68
|
+
complete(final: UpgradeStatus): void;
|
|
69
|
+
/** Latest status emitted, or undefined before the first publish. */
|
|
70
|
+
current(): UpgradeStatus | undefined;
|
|
71
|
+
}
|
|
72
|
+
export declare function createUpgradeStatusStream(): UpgradeStatusStream;
|
|
73
|
+
export interface RunAutoUpgradeInput {
|
|
74
|
+
packageName: string;
|
|
75
|
+
currentVersion: string;
|
|
76
|
+
/** Callback fired on every status transition. Never throws back into caller. */
|
|
77
|
+
onStatus?: UpgradeStatusListener;
|
|
78
|
+
/** Suppresses the run regardless of env (used after `--no-auto-upgrade`). */
|
|
79
|
+
disabled?: boolean;
|
|
80
|
+
/** process.argv[1] — used to detect source-checkout invocations. */
|
|
81
|
+
scriptPath?: string;
|
|
82
|
+
/** process.env at call site; lets tests inject a controlled environment. */
|
|
83
|
+
env?: NodeJS.ProcessEnv;
|
|
84
|
+
/** Override for tests; defaults to fetchLatestPackageVersion. */
|
|
85
|
+
fetchLatest?: (packageName: string) => Promise<string | undefined>;
|
|
86
|
+
/** Override for tests; defaults to spawning the real package manager. */
|
|
87
|
+
runUpgrade?: (command: string[]) => Promise<{
|
|
88
|
+
ok: boolean;
|
|
89
|
+
stderr?: string;
|
|
90
|
+
}>;
|
|
91
|
+
/** Override for tests. */
|
|
92
|
+
now?: () => number;
|
|
93
|
+
/** Override for tests. */
|
|
94
|
+
detectManager?: () => PackageManager;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Run the auto-upgrade flow in-process. Emits status transitions through
|
|
98
|
+
* `onStatus` and resolves to the final status. Never throws — every error
|
|
99
|
+
* path produces a terminal status the caller can render.
|
|
100
|
+
*/
|
|
101
|
+
export declare function runAutoUpgrade(input: RunAutoUpgradeInput): Promise<UpgradeStatus>;
|
|
102
|
+
/**
|
|
103
|
+
* Heuristic: only run auto-upgrade when the CLI looks like a globally
|
|
104
|
+
* installed binary. Source-checkout runs (`bun src/cli.ts`) and per-project
|
|
105
|
+
* installs are skipped so contributors never get their working copy
|
|
106
|
+
* overwritten by `npm install --global`.
|
|
107
|
+
*/
|
|
108
|
+
export declare function isLikelyGlobalInstall(scriptPath: string): boolean;
|
|
109
|
+
/**
|
|
110
|
+
* Human-readable one-liner for any UpgradeStatus. Used by both the JSON
|
|
111
|
+
* stderr path and the TUI header so the wording stays consistent.
|
|
112
|
+
*/
|
|
113
|
+
export declare function describeUpgradeStatus(packageName: string, status: UpgradeStatus): string | undefined;
|
|
114
|
+
/** Resolved lazily so tests can swap $HOME per case. */
|
|
115
|
+
declare function upgradePaths(): {
|
|
116
|
+
duetDir: string;
|
|
117
|
+
lockPath: string;
|
|
118
|
+
logPath: string;
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* Spawn options chosen so the install survives the parent CLI exiting:
|
|
122
|
+
* - `detached: true` puts npm in its own process group so a terminal
|
|
123
|
+
* Ctrl+C on the parent does not propagate and abort the install
|
|
124
|
+
* mid-write (which would leave the global node_modules tree corrupt).
|
|
125
|
+
* - The returned child is also `unref()`d (and so is its stderr pipe)
|
|
126
|
+
* so a clean `/exit` returns the shell immediately and the install
|
|
127
|
+
* continues independently. The parent still observes the child's exit
|
|
128
|
+
* event for as long as it is alive, so the TUI can render the
|
|
129
|
+
* "Updated… restart duet" line when the user stays.
|
|
130
|
+
*/
|
|
131
|
+
export declare const PACKAGE_MANAGER_SPAWN_OPTIONS: SpawnOptions;
|
|
132
|
+
export type SpawnFn = (command: string, args: string[], options: SpawnOptions) => ChildProcess;
|
|
133
|
+
/**
|
|
134
|
+
* Default in-process runner for the package manager upgrade command.
|
|
135
|
+
* Exported so tests can verify the spawn contract (detached, unref'd) by
|
|
136
|
+
* injecting a fake spawn.
|
|
137
|
+
*/
|
|
138
|
+
export declare function defaultRunUpgrade(command: string[], spawn?: SpawnFn): Promise<{
|
|
139
|
+
ok: boolean;
|
|
140
|
+
stderr?: string;
|
|
141
|
+
}>;
|
|
142
|
+
export declare const __testing: {
|
|
143
|
+
upgradePaths: typeof upgradePaths;
|
|
144
|
+
STALE_LOCK_MS: number;
|
|
145
|
+
};
|
|
146
|
+
export {};
|
|
147
|
+
//# sourceMappingURL=auto-upgrade.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-upgrade.d.ts","sourceRoot":"","sources":["../../../src/cli/auto-upgrade.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import { spawn as nodeSpawn } from "node:child_process";
|
|
2
|
+
import { appendFileSync, closeSync, mkdirSync, openSync, readFileSync, unlinkSync, writeSync, } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { detectPackageManagerFromContext, globalUpgradeCommand, } from "./package-manager.js";
|
|
6
|
+
import { compareSemverVersions, fetchLatestPackageVersion } from "./version-check.js";
|
|
7
|
+
/**
|
|
8
|
+
* Why this exists: the CLI ships several versions per hour. Manual `duet
|
|
9
|
+
* upgrade` lags behind, so each launch probes the registry and — when a
|
|
10
|
+
* newer version exists — runs the package manager's global install in the
|
|
11
|
+
* background while the user is already using the TUI. The next launch is
|
|
12
|
+
* then already on latest.
|
|
13
|
+
*
|
|
14
|
+
* Concurrency safety:
|
|
15
|
+
* - The package manager mutates the global install path in place. Already-
|
|
16
|
+
* running CLI processes do not re-read those files, so they are unaffected.
|
|
17
|
+
* - A CLI that *starts* mid-install could see partial files. We minimize
|
|
18
|
+
* that window with an exclusive lockfile so only one process upgrades at
|
|
19
|
+
* a time; others fall through and observe whatever state currently exists.
|
|
20
|
+
* - Stale locks (worker crash, OS kill) older than STALE_LOCK_MS are
|
|
21
|
+
* reclaimed so a missed unlink does not permanently disable upgrades.
|
|
22
|
+
*
|
|
23
|
+
* Status stream: `runAutoUpgrade` emits each transition through `onStatus`
|
|
24
|
+
* so the TUI can render a live "Checking… / Upgrading… / Upgraded, restart"
|
|
25
|
+
* line in the header. The returned promise resolves once the final state
|
|
26
|
+
* is reached.
|
|
27
|
+
*/
|
|
28
|
+
/** Env var users set to disable the auto-upgrade entirely. */
|
|
29
|
+
export const NO_AUTO_UPGRADE_ENV = "DUET_NO_AUTO_UPGRADE";
|
|
30
|
+
/** A lock held longer than this is treated as abandoned and replaced. */
|
|
31
|
+
const STALE_LOCK_MS = 10 * 60 * 1000;
|
|
32
|
+
/** How long to wait on the registry. The TUI is already up and showing a
|
|
33
|
+
* "checking…" placeholder, so we can wait longer than the legacy inline
|
|
34
|
+
* notice without making the user feel the call. */
|
|
35
|
+
const REGISTRY_TIMEOUT_MS = 8_000;
|
|
36
|
+
export function createUpgradeStatusStream() {
|
|
37
|
+
const listeners = new Set();
|
|
38
|
+
let latest;
|
|
39
|
+
const publish = (status) => {
|
|
40
|
+
latest = status;
|
|
41
|
+
for (const listener of listeners) {
|
|
42
|
+
try {
|
|
43
|
+
listener(status);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// Listener errors must not break the upgrade flow.
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
return {
|
|
51
|
+
subscribe(listener) {
|
|
52
|
+
listeners.add(listener);
|
|
53
|
+
// Replay the most recent status so late subscribers do not miss the
|
|
54
|
+
// current state — e.g. if the upgrade finished before the TUI mounted.
|
|
55
|
+
if (latest)
|
|
56
|
+
listener(latest);
|
|
57
|
+
return () => listeners.delete(listener);
|
|
58
|
+
},
|
|
59
|
+
publish,
|
|
60
|
+
complete(final) {
|
|
61
|
+
if (latest !== final)
|
|
62
|
+
publish(final);
|
|
63
|
+
},
|
|
64
|
+
current() {
|
|
65
|
+
return latest;
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Run the auto-upgrade flow in-process. Emits status transitions through
|
|
71
|
+
* `onStatus` and resolves to the final status. Never throws — every error
|
|
72
|
+
* path produces a terminal status the caller can render.
|
|
73
|
+
*/
|
|
74
|
+
export async function runAutoUpgrade(input) {
|
|
75
|
+
const emit = (status) => {
|
|
76
|
+
try {
|
|
77
|
+
input.onStatus?.(status);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// Listener errors must never affect the upgrade flow.
|
|
81
|
+
}
|
|
82
|
+
return status;
|
|
83
|
+
};
|
|
84
|
+
const env = input.env ?? process.env;
|
|
85
|
+
if (input.disabled || env[NO_AUTO_UPGRADE_ENV] === "1") {
|
|
86
|
+
return emit({ kind: "skipped", reason: "disabled" });
|
|
87
|
+
}
|
|
88
|
+
const scriptPath = input.scriptPath ?? process.argv[1];
|
|
89
|
+
if (!scriptPath || !isLikelyGlobalInstall(scriptPath)) {
|
|
90
|
+
return emit({ kind: "skipped", reason: "source-checkout" });
|
|
91
|
+
}
|
|
92
|
+
const now = input.now ?? (() => Date.now());
|
|
93
|
+
const handle = acquireLock(now());
|
|
94
|
+
if (handle === null) {
|
|
95
|
+
appendLog(`skip locked package=${input.packageName} current=${input.currentVersion}`);
|
|
96
|
+
return emit({ kind: "locked" });
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
emit({ kind: "checking" });
|
|
100
|
+
const fetchLatest = input.fetchLatest ?? ((name) => fetchLatestPackageVersion(name, REGISTRY_TIMEOUT_MS));
|
|
101
|
+
let latest;
|
|
102
|
+
try {
|
|
103
|
+
latest = await fetchLatest(input.packageName);
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
appendLog(`fetch-error package=${input.packageName} error=${describeError(error)}`);
|
|
107
|
+
return emit({ kind: "skipped", reason: "registry-unreachable" });
|
|
108
|
+
}
|
|
109
|
+
if (!latest) {
|
|
110
|
+
appendLog(`no-latest package=${input.packageName}`);
|
|
111
|
+
return emit({ kind: "skipped", reason: "registry-unreachable" });
|
|
112
|
+
}
|
|
113
|
+
if (compareSemverVersions(latest, input.currentVersion) <= 0) {
|
|
114
|
+
appendLog(`current package=${input.packageName} version=${input.currentVersion}`);
|
|
115
|
+
return emit({ kind: "current", version: input.currentVersion });
|
|
116
|
+
}
|
|
117
|
+
const detect = input.detectManager ??
|
|
118
|
+
(() => detectPackageManagerFromContext({
|
|
119
|
+
userAgent: process.env.npm_config_user_agent,
|
|
120
|
+
runtimeExecutable: process.argv[0],
|
|
121
|
+
scriptPath: process.argv[1],
|
|
122
|
+
}));
|
|
123
|
+
const manager = detect();
|
|
124
|
+
emit({ kind: "upgrading", from: input.currentVersion, to: latest, manager });
|
|
125
|
+
const command = globalUpgradeCommand(manager, input.packageName, latest);
|
|
126
|
+
const run = input.runUpgrade ?? defaultRunUpgrade;
|
|
127
|
+
const result = await run(command);
|
|
128
|
+
if (result.ok) {
|
|
129
|
+
appendLog(`upgraded package=${input.packageName} from=${input.currentVersion} to=${latest} manager=${manager}`);
|
|
130
|
+
return emit({ kind: "upgraded", from: input.currentVersion, to: latest, manager });
|
|
131
|
+
}
|
|
132
|
+
const error = result.stderr ?? "package manager exited non-zero";
|
|
133
|
+
appendLog(`failed package=${input.packageName} from=${input.currentVersion} to=${latest} manager=${manager} stderr=${error}`);
|
|
134
|
+
return emit({ kind: "failed", from: input.currentVersion, to: latest, manager, error });
|
|
135
|
+
}
|
|
136
|
+
finally {
|
|
137
|
+
releaseLock(handle);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Heuristic: only run auto-upgrade when the CLI looks like a globally
|
|
142
|
+
* installed binary. Source-checkout runs (`bun src/cli.ts`) and per-project
|
|
143
|
+
* installs are skipped so contributors never get their working copy
|
|
144
|
+
* overwritten by `npm install --global`.
|
|
145
|
+
*/
|
|
146
|
+
export function isLikelyGlobalInstall(scriptPath) {
|
|
147
|
+
const normalized = scriptPath.replace(/\\/g, "/");
|
|
148
|
+
if (normalized.includes("/.bun/install/global/") ||
|
|
149
|
+
normalized.includes("/.bun/bin/") ||
|
|
150
|
+
normalized.includes("/.pnpm/") ||
|
|
151
|
+
normalized.includes("/share/pnpm/") ||
|
|
152
|
+
normalized.includes("/.config/yarn/global/") ||
|
|
153
|
+
normalized.includes("/yarn/global/")) {
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
// npm global installs live under `<prefix>/lib/node_modules/<pkg>/...`.
|
|
157
|
+
if (/\/lib\/node_modules\//.test(normalized))
|
|
158
|
+
return true;
|
|
159
|
+
// Volta, fnm, nvm prefixes vary; treat any `node_modules/@duetso/` as global.
|
|
160
|
+
if (normalized.includes("/node_modules/@duetso/"))
|
|
161
|
+
return true;
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Human-readable one-liner for any UpgradeStatus. Used by both the JSON
|
|
166
|
+
* stderr path and the TUI header so the wording stays consistent.
|
|
167
|
+
*/
|
|
168
|
+
export function describeUpgradeStatus(packageName, status) {
|
|
169
|
+
switch (status.kind) {
|
|
170
|
+
case "checking":
|
|
171
|
+
return "Checking for updates…";
|
|
172
|
+
case "upgrading":
|
|
173
|
+
return `Updating ${packageName} ${status.from} → ${status.to}…`;
|
|
174
|
+
case "upgraded":
|
|
175
|
+
return `Updated ${packageName} to ${status.to}. Restart duet to use it.`;
|
|
176
|
+
case "failed":
|
|
177
|
+
return `Update to ${status.to} failed (${status.error}). Run: duet upgrade`;
|
|
178
|
+
case "locked":
|
|
179
|
+
case "current":
|
|
180
|
+
case "skipped":
|
|
181
|
+
// No notice — either we are on latest, another process is handling it,
|
|
182
|
+
// or auto-upgrade is intentionally off. Header stays clean.
|
|
183
|
+
return undefined;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/** Resolved lazily so tests can swap $HOME per case. */
|
|
187
|
+
function upgradePaths() {
|
|
188
|
+
const duetDir = join(homedir(), ".duet");
|
|
189
|
+
return {
|
|
190
|
+
duetDir,
|
|
191
|
+
lockPath: join(duetDir, "upgrade.lock"),
|
|
192
|
+
logPath: join(duetDir, "logs", "upgrade.log"),
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
function acquireLock(now) {
|
|
196
|
+
const { duetDir, lockPath } = upgradePaths();
|
|
197
|
+
ensureDir(duetDir);
|
|
198
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
199
|
+
try {
|
|
200
|
+
const fd = openSync(lockPath, "wx");
|
|
201
|
+
writeSync(fd, JSON.stringify({ pid: process.pid, startedAt: now }));
|
|
202
|
+
return { fd, lockPath };
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
if (error?.code !== "EEXIST")
|
|
206
|
+
return null;
|
|
207
|
+
if (!isStaleLock(lockPath, now))
|
|
208
|
+
return null;
|
|
209
|
+
try {
|
|
210
|
+
unlinkSync(lockPath);
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
function isStaleLock(lockPath, now) {
|
|
220
|
+
try {
|
|
221
|
+
const raw = readFileSync(lockPath, "utf8");
|
|
222
|
+
const payload = JSON.parse(raw);
|
|
223
|
+
if (typeof payload.startedAt !== "number")
|
|
224
|
+
return true;
|
|
225
|
+
return now - payload.startedAt > STALE_LOCK_MS;
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
function releaseLock(handle) {
|
|
232
|
+
try {
|
|
233
|
+
closeSync(handle.fd);
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
// ignore
|
|
237
|
+
}
|
|
238
|
+
try {
|
|
239
|
+
unlinkSync(handle.lockPath);
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
// ignore
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
function ensureDir(path) {
|
|
246
|
+
try {
|
|
247
|
+
mkdirSync(path, { recursive: true });
|
|
248
|
+
}
|
|
249
|
+
catch {
|
|
250
|
+
// ignore
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
function appendLog(line) {
|
|
254
|
+
try {
|
|
255
|
+
const { logPath } = upgradePaths();
|
|
256
|
+
ensureDir(join(logPath, ".."));
|
|
257
|
+
appendFileSync(logPath, `${new Date().toISOString()} ${line}\n`);
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
// ignore
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
function describeError(error) {
|
|
264
|
+
if (error instanceof Error)
|
|
265
|
+
return error.message;
|
|
266
|
+
return String(error);
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Spawn options chosen so the install survives the parent CLI exiting:
|
|
270
|
+
* - `detached: true` puts npm in its own process group so a terminal
|
|
271
|
+
* Ctrl+C on the parent does not propagate and abort the install
|
|
272
|
+
* mid-write (which would leave the global node_modules tree corrupt).
|
|
273
|
+
* - The returned child is also `unref()`d (and so is its stderr pipe)
|
|
274
|
+
* so a clean `/exit` returns the shell immediately and the install
|
|
275
|
+
* continues independently. The parent still observes the child's exit
|
|
276
|
+
* event for as long as it is alive, so the TUI can render the
|
|
277
|
+
* "Updated… restart duet" line when the user stays.
|
|
278
|
+
*/
|
|
279
|
+
export const PACKAGE_MANAGER_SPAWN_OPTIONS = {
|
|
280
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
281
|
+
detached: true,
|
|
282
|
+
};
|
|
283
|
+
/**
|
|
284
|
+
* Default in-process runner for the package manager upgrade command.
|
|
285
|
+
* Exported so tests can verify the spawn contract (detached, unref'd) by
|
|
286
|
+
* injecting a fake spawn.
|
|
287
|
+
*/
|
|
288
|
+
export function defaultRunUpgrade(command, spawn = nodeSpawn) {
|
|
289
|
+
return new Promise((resolve) => {
|
|
290
|
+
const child = spawn(command[0], command.slice(1), PACKAGE_MANAGER_SPAWN_OPTIONS);
|
|
291
|
+
child.unref();
|
|
292
|
+
// The stderr pipe is a separate libuv handle that also pins the event
|
|
293
|
+
// loop alive; unref it so /exit returns even while the install is still
|
|
294
|
+
// streaming output. Node exposes `unref` on the underlying socket; cast
|
|
295
|
+
// because @types/node does not surface it on the Readable interface.
|
|
296
|
+
child.stderr?.unref?.();
|
|
297
|
+
let stderr = "";
|
|
298
|
+
child.stderr?.on("data", (chunk) => {
|
|
299
|
+
stderr += chunk.toString();
|
|
300
|
+
});
|
|
301
|
+
child.once("error", (error) => {
|
|
302
|
+
resolve({ ok: false, stderr: describeError(error) });
|
|
303
|
+
});
|
|
304
|
+
child.once("exit", (code) => {
|
|
305
|
+
resolve({ ok: code === 0, stderr: stderr.trim() || undefined });
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
// Test-only access to internal paths and constants.
|
|
310
|
+
export const __testing = {
|
|
311
|
+
upgradePaths,
|
|
312
|
+
STALE_LOCK_MS,
|
|
313
|
+
};
|
|
314
|
+
//# sourceMappingURL=auto-upgrade.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-upgrade.js","sourceRoot":"","sources":["../../../src/cli/auto-upgrade.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../../src/cli/help.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,eAAO,MAAM,+BAA+B,IAAI,CAAC;AAEjD,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../../src/cli/help.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,eAAO,MAAM,+BAA+B,IAAI,CAAC;AAEjD,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CA4EtD;AAED,wBAAgB,cAAc,IAAI,IAAI,CA0BrC;AAED,wBAAgB,YAAY,IAAI,IAAI,CAsBnC;AAED,wBAAgB,eAAe,IAAI,IAAI,CAkBtC;AAED,wBAAgB,eAAe,IAAI,IAAI,CAoBtC;AAED,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAa1D"}
|
package/dist/src/cli/help.js
CHANGED
|
@@ -42,6 +42,7 @@ OPTIONS
|
|
|
42
42
|
Load a file into the system prompt; repeatable
|
|
43
43
|
--no-system-prompt-files Disable default AGENTS.md system prompt loading
|
|
44
44
|
--env-file <path> Shared env file to load after <workdir>/.env (default: ${DEFAULT_DUET_ENV_FILE})
|
|
45
|
+
--no-auto-upgrade Skip the auto-upgrade probe for this run (also: DUET_NO_AUTO_UPGRADE=1)
|
|
45
46
|
--json Force JSONL event output instead of the TUI
|
|
46
47
|
-v, --version Print the installed duet version and exit
|
|
47
48
|
-h, --help Show this help
|
package/dist/src/cli/help.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"help.js","sourceRoot":"","sources":["../../../src/cli/help.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAExE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,CAAC,CAAC;AAEjD,MAAM,UAAU,YAAY,CAAC,WAAmB;IAC9C,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;gDAiBkC,WAAW;;;;;;;;;;;;gHAYqD,+BAA+B;;;;;oFAK3D,qBAAqB
|
|
1
|
+
{"version":3,"file":"help.js","sourceRoot":"","sources":["../../../src/cli/help.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAExE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,CAAC,CAAC;AAEjD,MAAM,UAAU,YAAY,CAAC,WAAmB;IAC9C,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;gDAiBkC,WAAW;;;;;;;;;;;;gHAYqD,+BAA+B;;;;;oFAK3D,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwCxG,CAAC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;wEAW0D,qBAAqB;;;;;;;;;;;;;CAa5F,CAAC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;yDAa2C,qBAAqB;;;;;;IAM1E,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC;CAChC,CAAC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;CAgBb,CAAC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;CAkBb,CAAC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,WAAmB;IAClD,OAAO,CAAC,GAAG,CAAC;oCACsB,WAAW;;;;;;;;;;CAU9C,CAAC,CAAC;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/cli/run.ts"],"names":[],"mappings":"AASA,OAAO,EAIL,KAAK,eAAe,EACrB,MAAM,iCAAiC,CAAC;AAGzC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/cli/run.ts"],"names":[],"mappings":"AASA,OAAO,EAIL,KAAK,eAAe,EACrB,MAAM,iCAAiC,CAAC;AAGzC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAW3D,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,gBAAgB,CAAC;IACzB,eAAe,EAAE,eAAe,CAAC;IACjC,qBAAqB,EAAE,eAAe,CAAC;CACxC;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE;IAClC,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAEV;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,kBAAkB,EACzB,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,GACtB,uBAAuB,CAgBzB;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CA2QvF"}
|
package/dist/src/cli/run.js
CHANGED
|
@@ -9,7 +9,7 @@ import { DEFAULT_RESUME_HISTORY_MESSAGES, printRunHelp } from "./help.js";
|
|
|
9
9
|
import { resumeCommand } from "./resume-hint.js";
|
|
10
10
|
import { fail, isInteractive, loadCliEnvFiles, parseResumeHistoryMessages } from "./shared.js";
|
|
11
11
|
import { installShutdownHandlers } from "./shutdown.js";
|
|
12
|
-
import {
|
|
12
|
+
import { createUpgradeStatusStream, describeUpgradeStatus, runAutoUpgrade, } from "./auto-upgrade.js";
|
|
13
13
|
/**
|
|
14
14
|
* Decide whether to render the interactive TUI vs JSONL events.
|
|
15
15
|
*
|
|
@@ -60,13 +60,9 @@ export async function runRunCommand(args, pkg) {
|
|
|
60
60
|
let jsonOutput = false;
|
|
61
61
|
let envFilePath;
|
|
62
62
|
let incognito = false;
|
|
63
|
+
let noAutoUpgrade = false;
|
|
63
64
|
const promptParts = [];
|
|
64
65
|
const interactive = isInteractive();
|
|
65
|
-
// Kick off the npm registry probe immediately so it overlaps with env
|
|
66
|
-
// loading, model resolution, skill discovery, and session bootstrap. JSON
|
|
67
|
-
// callers await the result; the TUI path only consumes it if it has already
|
|
68
|
-
// settled by the time we render, so a slow registry never delays first paint.
|
|
69
|
-
const versionNoticePromise = getNewVersionNotice(pkg.name, pkg.version).catch(() => undefined);
|
|
70
66
|
for (let i = 0; i < args.length; i++) {
|
|
71
67
|
switch (args[i]) {
|
|
72
68
|
case "--model":
|
|
@@ -133,6 +129,9 @@ export async function runRunCommand(args, pkg) {
|
|
|
133
129
|
fail(`Missing value for ${args[i]}`);
|
|
134
130
|
envFilePath = args[++i];
|
|
135
131
|
break;
|
|
132
|
+
case "--no-auto-upgrade":
|
|
133
|
+
noAutoUpgrade = true;
|
|
134
|
+
break;
|
|
136
135
|
case "--version":
|
|
137
136
|
case "-v":
|
|
138
137
|
console.log(pkg.version);
|
|
@@ -183,6 +182,20 @@ export async function runRunCommand(args, pkg) {
|
|
|
183
182
|
}
|
|
184
183
|
const dotenvKeys = loadCliEnvFiles(workDir, envFilePath);
|
|
185
184
|
shimDuetApiKeyToAiGateway();
|
|
185
|
+
// Probe the registry and (if newer) run the package manager in-process
|
|
186
|
+
// while the TUI is already mounted. The TUI subscribes to upgradeStatus$
|
|
187
|
+
// to render a live header line; the JSON path awaits the final status
|
|
188
|
+
// and prints one summary line to stderr.
|
|
189
|
+
const upgradeStatus$ = createUpgradeStatusStream();
|
|
190
|
+
const upgradePromise = runAutoUpgrade({
|
|
191
|
+
packageName: pkg.name,
|
|
192
|
+
currentVersion: pkg.version,
|
|
193
|
+
disabled: noAutoUpgrade,
|
|
194
|
+
onStatus: (status) => upgradeStatus$.publish(status),
|
|
195
|
+
}).then((status) => {
|
|
196
|
+
upgradeStatus$.complete(status);
|
|
197
|
+
return status;
|
|
198
|
+
});
|
|
186
199
|
// Refresh the gateway-managed default skills when the user has previously
|
|
187
200
|
// opted in via `duet login` (i.e. `~/.duet/.skills-hash` exists). Logging
|
|
188
201
|
// in with --skip-skill-sync leaves no hash, so this stays a no-op until
|
|
@@ -203,13 +216,15 @@ export async function runRunCommand(args, pkg) {
|
|
|
203
216
|
memoryModelName = memoryModelResolution.modelName;
|
|
204
217
|
const useTui = shouldUseTui({ interactive, jsonOutput, prompt });
|
|
205
218
|
const useJson = !useTui;
|
|
206
|
-
// JSON consumers
|
|
207
|
-
//
|
|
208
|
-
//
|
|
219
|
+
// JSON consumers want a single summary line, not a streaming status.
|
|
220
|
+
// Await the final upgrade status and print the human-readable form (if any)
|
|
221
|
+
// before the regular boot lines. The TUI subscribes to the live stream
|
|
222
|
+
// instead and renders intermediate "Checking…/Updating…" states inline.
|
|
209
223
|
if (useJson) {
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
224
|
+
const finalStatus = await upgradePromise;
|
|
225
|
+
const notice = describeUpgradeStatus(pkg.name, finalStatus);
|
|
226
|
+
if (notice)
|
|
227
|
+
process.stderr.write(`${notice}\n`);
|
|
213
228
|
process.stderr.write(`Model: ${modelName}\n`);
|
|
214
229
|
process.stderr.write(`Source: ${describeModelResolution(modelResolution)}\n`);
|
|
215
230
|
process.stderr.write(`Memory model: ${memoryModelName}\n`);
|
|
@@ -263,8 +278,9 @@ export async function runRunCommand(args, pkg) {
|
|
|
263
278
|
memoryModelSource: describeModelResolution(memoryModelResolution),
|
|
264
279
|
workDir,
|
|
265
280
|
sessionId: session.id,
|
|
281
|
+
packageName: pkg.name,
|
|
266
282
|
packageVersion: pkg.version,
|
|
267
|
-
|
|
283
|
+
upgradeStatus$,
|
|
268
284
|
});
|
|
269
285
|
}
|
|
270
286
|
process.stderr.write(`To resume this session:\n${resumeCommand(session.id, {
|