@mnemom/mnemom 0.17.31 → 0.17.32-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/base-url-proxy.mjs +30 -0
- package/dist/commands/agent-fuel.d.ts +75 -0
- package/dist/commands/agent-fuel.js +298 -0
- package/dist/commands/agent-implicit-notice.d.ts +31 -0
- package/dist/commands/agent-implicit-notice.js +73 -0
- package/dist/commands/agent-session.d.ts +2 -1
- package/dist/commands/agent-session.js +2 -1
- package/dist/commands/agent-share-fixture.d.ts +20 -0
- package/dist/commands/agent-share-fixture.js +134 -0
- package/dist/commands/agent-share.d.ts +87 -0
- package/dist/commands/agent-share.js +251 -0
- package/dist/commands/agent.d.ts +75 -2
- package/dist/commands/agent.js +165 -28
- package/dist/commands/status.js +1 -1
- package/dist/commands/update.js +3 -1
- package/dist/commands/validate.d.ts +1 -1
- package/dist/commands/validate.js +1 -1
- package/dist/index.js +68 -12
- package/dist/lib/agent.d.ts +34 -19
- package/dist/lib/agent.js +41 -32
- package/dist/lib/api.d.ts +66 -0
- package/dist/lib/api.js +50 -10
- package/dist/lib/canonical-json.d.ts +12 -0
- package/dist/lib/canonical-json.js +22 -0
- package/dist/lib/commit-report.d.ts +109 -0
- package/dist/lib/commit-report.js +205 -0
- package/dist/lib/conduct-receipt.d.ts +81 -0
- package/dist/lib/conduct-receipt.js +177 -0
- package/dist/lib/contract-hash.d.ts +61 -0
- package/dist/lib/contract-hash.js +126 -0
- package/dist/lib/experimental.d.ts +10 -1
- package/dist/lib/experimental.js +35 -1
- package/dist/lib/oauth.d.ts +2 -1
- package/dist/lib/oauth.js +2 -1
- package/dist/lib/statusline-bin.d.ts +2 -2
- package/dist/lib/statusline-bin.js +4 -7
- package/dist/lib/statusline-feed.d.ts +33 -0
- package/dist/lib/statusline-feed.js +68 -0
- package/dist/lib/statusline.d.ts +39 -88
- package/dist/lib/statusline.js +58 -250
- package/dist/lib/update/decide.d.ts +22 -3
- package/dist/lib/update/decide.js +35 -12
- package/dist/lib/update/doctor.js +5 -1
- package/dist/lib/update/run.d.ts +2 -0
- package/dist/lib/update/run.js +22 -8
- package/package.json +1 -1
|
@@ -15,8 +15,13 @@
|
|
|
15
15
|
* for entitled callers (internal cohort, staff, `cli_next` invite —
|
|
16
16
|
* GET /v1/cli/channel-entitlement); otherwise it behaves as latest.
|
|
17
17
|
*
|
|
18
|
-
* Never downgrades
|
|
19
|
-
* keeps the current build until latest
|
|
18
|
+
* Never downgrades on its own, with one exception: a moved tag. Switching
|
|
19
|
+
* next → latest while ahead of latest keeps the current build until latest
|
|
20
|
+
* overtakes it (`mnemom update --force`). But when the channel pointed at the
|
|
21
|
+
* running build on the last check and now points lower, the tag was moved back
|
|
22
|
+
* (a bad release rolled back by hand: OIDC publishing can't move a tag, a
|
|
23
|
+
* person can), and the next check follows it down. A build the channel never
|
|
24
|
+
* pointed at (a dev build, a newer local install) is never downgraded.
|
|
20
25
|
*/
|
|
21
26
|
export type UpdateChannel = "latest" | "next";
|
|
22
27
|
export type UpdateMode = "auto" | "notify" | "off";
|
|
@@ -64,6 +69,13 @@ export interface UpdateState {
|
|
|
64
69
|
method?: InstallMethod;
|
|
65
70
|
/** Channel the last successful update installed from; feeds defaultChannel. */
|
|
66
71
|
followChannel?: UpdateChannel;
|
|
72
|
+
/** What the channel pointed at on the last check; detects a tag moved back. */
|
|
73
|
+
channelTarget?: {
|
|
74
|
+
channel: UpdateChannel;
|
|
75
|
+
version: string;
|
|
76
|
+
};
|
|
77
|
+
/** `available` is the channel's tag moved back below the running build. */
|
|
78
|
+
movedBack?: boolean;
|
|
67
79
|
/** The background worker startup last spawned, while it runs (cleared when it finishes). */
|
|
68
80
|
workerPid?: number;
|
|
69
81
|
workerSince?: number;
|
|
@@ -124,6 +136,11 @@ export declare function entitlementFresh(cache: EntitlementCache | undefined, no
|
|
|
124
136
|
* registry response).
|
|
125
137
|
*/
|
|
126
138
|
export declare function pickTarget(tags: DistTags, channel: UpdateChannel): string | undefined;
|
|
139
|
+
/**
|
|
140
|
+
* The channel's tag was moved back off the running build: on the last check it
|
|
141
|
+
* pointed at `current`, and now it points at an older version.
|
|
142
|
+
*/
|
|
143
|
+
export declare function tagMovedBack(previous: UpdateState["channelTarget"], channel: UpdateChannel, current: string, target: string | undefined): boolean;
|
|
127
144
|
/**
|
|
128
145
|
* Longest a worker lives: it waits for its parent `mnemom` (a whole agent session)
|
|
129
146
|
* up to this long. A recorded worker older than this is treated as gone, so a
|
|
@@ -155,7 +172,8 @@ export type UpdateAction = {
|
|
|
155
172
|
/**
|
|
156
173
|
* The single decision: given where the registry is and how we're installed,
|
|
157
174
|
* install / notify / do nothing. `force` (explicit `mnemom update --force`)
|
|
158
|
-
* permits a downgrade to the channel's target
|
|
175
|
+
* permits a downgrade to the channel's target; `followMovedTag` (see
|
|
176
|
+
* tagMovedBack) permits one to the version the channel's tag was moved back to.
|
|
159
177
|
*/
|
|
160
178
|
export declare function decide(input: {
|
|
161
179
|
current: string;
|
|
@@ -164,6 +182,7 @@ export declare function decide(input: {
|
|
|
164
182
|
method: InstallMethod;
|
|
165
183
|
installBlocked?: boolean;
|
|
166
184
|
force?: boolean;
|
|
185
|
+
followMovedTag?: boolean;
|
|
167
186
|
}): UpdateAction;
|
|
168
187
|
/** The command a human should run to upgrade, for the given install method. */
|
|
169
188
|
export declare function manualUpgradeCommand(method: InstallMethod, version: string): string;
|
|
@@ -15,8 +15,13 @@
|
|
|
15
15
|
* for entitled callers (internal cohort, staff, `cli_next` invite —
|
|
16
16
|
* GET /v1/cli/channel-entitlement); otherwise it behaves as latest.
|
|
17
17
|
*
|
|
18
|
-
* Never downgrades
|
|
19
|
-
* keeps the current build until latest
|
|
18
|
+
* Never downgrades on its own, with one exception: a moved tag. Switching
|
|
19
|
+
* next → latest while ahead of latest keeps the current build until latest
|
|
20
|
+
* overtakes it (`mnemom update --force`). But when the channel pointed at the
|
|
21
|
+
* running build on the last check and now points lower, the tag was moved back
|
|
22
|
+
* (a bad release rolled back by hand: OIDC publishing can't move a tag, a
|
|
23
|
+
* person can), and the next check follows it down. A build the channel never
|
|
24
|
+
* pointed at (a dev build, a newer local install) is never downgraded.
|
|
20
25
|
*/
|
|
21
26
|
import { compareVersions, isPrerelease, isValidVersion } from "./semver.js";
|
|
22
27
|
export const UPDATE_CHANNELS = ["latest", "next"];
|
|
@@ -115,6 +120,24 @@ export function pickTarget(tags, channel) {
|
|
|
115
120
|
return next;
|
|
116
121
|
return compareVersions(next, stable) >= 0 ? next : stable;
|
|
117
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* The channel's tag was moved back off the running build: on the last check it
|
|
125
|
+
* pointed at `current`, and now it points at an older version.
|
|
126
|
+
*/
|
|
127
|
+
export function tagMovedBack(previous, channel, current, target) {
|
|
128
|
+
return (previous !== undefined &&
|
|
129
|
+
previous.channel === channel &&
|
|
130
|
+
previous.version === current &&
|
|
131
|
+
target !== undefined &&
|
|
132
|
+
compareVersions(target, current) < 0);
|
|
133
|
+
}
|
|
134
|
+
/** `state.available` is something to act on: newer, or the tag moved back to it. */
|
|
135
|
+
function actionable(state, current) {
|
|
136
|
+
if (!state.available || !isValidVersion(state.available))
|
|
137
|
+
return false;
|
|
138
|
+
const cmp = compareVersions(state.available, current);
|
|
139
|
+
return cmp > 0 || (state.movedBack === true && cmp < 0);
|
|
140
|
+
}
|
|
118
141
|
/**
|
|
119
142
|
* Longest a worker lives: it waits for its parent `mnemom` (a whole agent session)
|
|
120
143
|
* up to this long. A recorded worker older than this is treated as gone, so a
|
|
@@ -128,9 +151,7 @@ export const WORKER_MAX_AGE_MS = 12 * HOUR;
|
|
|
128
151
|
* scheduled check.
|
|
129
152
|
*/
|
|
130
153
|
export function shouldResume(state, current, now, alive) {
|
|
131
|
-
if (!state
|
|
132
|
-
return false;
|
|
133
|
-
if (compareVersions(state.available, current) <= 0)
|
|
154
|
+
if (!actionable(state, current))
|
|
134
155
|
return false;
|
|
135
156
|
if (state.installBlocked || !canSelfUpdate(state.method ?? "unknown"))
|
|
136
157
|
return false;
|
|
@@ -160,10 +181,11 @@ export function canSelfUpdate(method) {
|
|
|
160
181
|
/**
|
|
161
182
|
* The single decision: given where the registry is and how we're installed,
|
|
162
183
|
* install / notify / do nothing. `force` (explicit `mnemom update --force`)
|
|
163
|
-
* permits a downgrade to the channel's target
|
|
184
|
+
* permits a downgrade to the channel's target; `followMovedTag` (see
|
|
185
|
+
* tagMovedBack) permits one to the version the channel's tag was moved back to.
|
|
164
186
|
*/
|
|
165
187
|
export function decide(input) {
|
|
166
|
-
const { current, target, mode, method, installBlocked, force } = input;
|
|
188
|
+
const { current, target, mode, method, installBlocked, force, followMovedTag } = input;
|
|
167
189
|
if (mode === "off")
|
|
168
190
|
return { kind: "noop", reason: "updates are off" };
|
|
169
191
|
if (!target)
|
|
@@ -171,7 +193,7 @@ export function decide(input) {
|
|
|
171
193
|
const cmp = compareVersions(target, current);
|
|
172
194
|
if (cmp === 0)
|
|
173
195
|
return { kind: "noop", reason: "already on the newest version" };
|
|
174
|
-
if (cmp < 0 && !force)
|
|
196
|
+
if (cmp < 0 && !force && !followMovedTag)
|
|
175
197
|
return { kind: "noop", reason: "current build is newer than the channel" };
|
|
176
198
|
if (!canSelfUpdate(method)) {
|
|
177
199
|
return { kind: "notify", version: target, reason: `installed via ${method}` };
|
|
@@ -215,9 +237,7 @@ export function startupNotice(input) {
|
|
|
215
237
|
patch: { pending: undefined },
|
|
216
238
|
};
|
|
217
239
|
}
|
|
218
|
-
if (mode === "off" || !state
|
|
219
|
-
return null;
|
|
220
|
-
if (compareVersions(state.available, current) <= 0)
|
|
240
|
+
if (mode === "off" || !actionable(state, current))
|
|
221
241
|
return null;
|
|
222
242
|
// In auto mode with a working self-update, the worker installs it — stay quiet.
|
|
223
243
|
if (mode === "auto" && canSelfUpdate(method) && !state.installBlocked)
|
|
@@ -229,8 +249,11 @@ export function startupNotice(input) {
|
|
|
229
249
|
: state.installBlocked
|
|
230
250
|
? ` — the global npm prefix isn't writable; run: ${manualUpgradeCommand(method, state.available)} (see https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally)`
|
|
231
251
|
: " — run: mnemom update";
|
|
252
|
+
const what = state.movedBack
|
|
253
|
+
? `mnemom ${current} was withdrawn; the channel is back on ${state.available}`
|
|
254
|
+
: `mnemom ${state.available} is available (you have ${current})`;
|
|
232
255
|
return {
|
|
233
|
-
message:
|
|
256
|
+
message: `${what}${cmd}`,
|
|
234
257
|
patch: { lastNotified: now },
|
|
235
258
|
};
|
|
236
259
|
}
|
|
@@ -46,7 +46,11 @@ export function updateDoctorRow(input) {
|
|
|
46
46
|
hint: "it retries automatically with backoff; `mnemom update` retries now",
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
|
-
const pending = state.available
|
|
49
|
+
const pending = !state.available
|
|
50
|
+
? ""
|
|
51
|
+
: state.movedBack
|
|
52
|
+
? `, withdrawn: the channel is back on ${state.available}`
|
|
53
|
+
: `, ${state.available} available`;
|
|
50
54
|
return {
|
|
51
55
|
mark: "ok",
|
|
52
56
|
text: `auto-update: ${settings.mode} on ${settings.channel} (v${current}${pending})`,
|
package/dist/lib/update/run.d.ts
CHANGED
|
@@ -42,6 +42,8 @@ export interface RunResult {
|
|
|
42
42
|
action?: UpdateAction;
|
|
43
43
|
installed?: string;
|
|
44
44
|
error?: string;
|
|
45
|
+
/** The channel's tag moved back below the running build (a withdrawn release). */
|
|
46
|
+
movedBack?: boolean;
|
|
45
47
|
}
|
|
46
48
|
/**
|
|
47
49
|
* Resolve the `next` entitlement: a fresh cache wins; otherwise ask the API.
|
package/dist/lib/update/run.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* (maybe) install → persist state. Never throws; failures become state
|
|
6
6
|
* (backoff) and a result the caller may print.
|
|
7
7
|
*/
|
|
8
|
-
import { backoffDelay, decide, effectiveChannel, entitlementFresh, pickTarget, } from "./decide.js";
|
|
8
|
+
import { backoffDelay, decide, effectiveChannel, entitlementFresh, pickTarget, tagMovedBack, } from "./decide.js";
|
|
9
9
|
import { compareVersions } from "./semver.js";
|
|
10
10
|
/**
|
|
11
11
|
* Resolve the `next` entitlement: a fresh cache wins; otherwise ask the API.
|
|
@@ -55,6 +55,7 @@ export async function runUpdate(deps, settings, opts = {}) {
|
|
|
55
55
|
const channelDowngraded = channel !== settings.channel;
|
|
56
56
|
const target = pickTarget(tags, channel);
|
|
57
57
|
const method = await deps.installMethod();
|
|
58
|
+
const movedBack = tagMovedBack(state.channelTarget, channel, deps.current, target);
|
|
58
59
|
const action = decide({
|
|
59
60
|
current: deps.current,
|
|
60
61
|
target,
|
|
@@ -63,17 +64,30 @@ export async function runUpdate(deps, settings, opts = {}) {
|
|
|
63
64
|
// An explicit `mnemom update` retries even after an EACCES (the user may have fixed it).
|
|
64
65
|
installBlocked: opts.foreground ? false : state.installBlocked,
|
|
65
66
|
force: opts.force,
|
|
67
|
+
followMovedTag: movedBack,
|
|
66
68
|
});
|
|
67
69
|
const newer = target !== undefined && compareVersions(target, deps.current) > 0;
|
|
68
70
|
const base = {
|
|
69
71
|
lastCheck: now,
|
|
70
72
|
method,
|
|
71
|
-
available: newer ? target : undefined,
|
|
73
|
+
available: newer || movedBack ? target : undefined,
|
|
74
|
+
movedBack: movedBack || undefined,
|
|
75
|
+
// A moved-back tag keeps the old record until it is followed, so a deferred
|
|
76
|
+
// or notify-only pass doesn't forget the move on the next check; an empty
|
|
77
|
+
// answer from the registry keeps it too.
|
|
78
|
+
...(movedBack || target === undefined ? {} : { channelTarget: { channel, version: target } }),
|
|
72
79
|
failures: undefined,
|
|
73
80
|
backoffUntil: undefined,
|
|
74
81
|
lastError: undefined,
|
|
75
82
|
};
|
|
76
|
-
const result = {
|
|
83
|
+
const result = {
|
|
84
|
+
channel,
|
|
85
|
+
channelDowngraded,
|
|
86
|
+
method,
|
|
87
|
+
target,
|
|
88
|
+
action,
|
|
89
|
+
...(movedBack ? { movedBack: true } : {}),
|
|
90
|
+
};
|
|
77
91
|
if (action.kind !== "install") {
|
|
78
92
|
deps.saveState({ ...base, ...done });
|
|
79
93
|
return result;
|
|
@@ -94,9 +108,11 @@ export async function runUpdate(deps, settings, opts = {}) {
|
|
|
94
108
|
deps.saveState({
|
|
95
109
|
...base,
|
|
96
110
|
available: undefined,
|
|
111
|
+
movedBack: undefined,
|
|
97
112
|
installBlocked: undefined,
|
|
98
113
|
pending: { from: deps.current, to: action.version, channel },
|
|
99
114
|
followChannel: channel,
|
|
115
|
+
channelTarget: { channel, version: action.version },
|
|
100
116
|
...done,
|
|
101
117
|
});
|
|
102
118
|
return { ...result, installed: action.version };
|
|
@@ -106,11 +122,9 @@ export async function runUpdate(deps, settings, opts = {}) {
|
|
|
106
122
|
deps.saveState({ ...base, installBlocked: true, lastError: outcome.message, ...done });
|
|
107
123
|
}
|
|
108
124
|
else {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
...done,
|
|
113
|
-
});
|
|
125
|
+
// Keep the moved-tag record: startup reads `movedBack` to notify and resume,
|
|
126
|
+
// and a moved-back tag must stay remembered until it is followed.
|
|
127
|
+
deps.saveState({ ...base, ...failurePatch(state, now, outcome.message), ...done });
|
|
114
128
|
}
|
|
115
129
|
return { ...result, error: outcome.message };
|
|
116
130
|
}
|