memgineering 0.16.0 → 0.17.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/CHANGELOG.md +22 -0
- package/assets/MEMGINEERING.md +1 -1
- package/assets/memgineering-setup/SKILL.md +12 -0
- package/dist/index.js +77 -5
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,28 @@ language the reader wants. The bilingual rule the monorepo applies to
|
|
|
11
11
|
|
|
12
12
|
## [Unreleased]
|
|
13
13
|
|
|
14
|
+
## [0.17.0] — 2026-08-30
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **One sign-in, both halves.** `memgineering login` now links your registry
|
|
19
|
+
account automatically — same Google account, no extra step — and stores a
|
|
20
|
+
registry session next to the brain token. `memgineering whoami` shows both
|
|
21
|
+
identities (brain name and registry username); `memgineering logout` ends
|
|
22
|
+
both. If the registry half is ever unreachable during login, the brain
|
|
23
|
+
sign-in still completes and whoami says how to link later.
|
|
24
|
+
- **Approving a login is one click now.** The browser approval screen keys off
|
|
25
|
+
your memgineering.com sign-in: signed in, you see who is asking and press
|
|
26
|
+
승인 — no separate Google round trip. Signed out, the same page signs you in
|
|
27
|
+
first (Google in a popup) and then asks. Consent is requested exactly once,
|
|
28
|
+
when an account is first created.
|
|
29
|
+
|
|
30
|
+
### Agent guidance
|
|
31
|
+
|
|
32
|
+
- `memgineering-setup` explains the unified sign-in: one login carries brain
|
|
33
|
+
and registry credentials together, and `whoami` reports both. Restart agent
|
|
34
|
+
sessions after upgrading so the refreshed guidance loads.
|
|
35
|
+
|
|
14
36
|
## [0.16.0] — 2026-08-30
|
|
15
37
|
|
|
16
38
|
### Added
|
package/assets/MEMGINEERING.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: memgineering
|
|
3
3
|
description: Use whenever the user refers to something they told you before, asks what was decided, tells you something worth keeping, or settles something that should hold next time. The memory lives in their own folder and outlives this session; check it before answering from guesswork, and write to it when you learn something durable.
|
|
4
4
|
type: skill
|
|
5
|
-
version: 0.
|
|
5
|
+
version: 0.17.0
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# memgineering
|
|
@@ -45,6 +45,18 @@ opening nothing. Show the user the code — they approve it in their browser whi
|
|
|
45
45
|
you keep working, and the next command picks the token up. Nothing exists on the
|
|
46
46
|
server until they approve.
|
|
47
47
|
|
|
48
|
+
**One sign-in covers both halves (0.17.0+).** The approval also links the
|
|
49
|
+
user's registry account (same Google account, automatic), so the claim stores a
|
|
50
|
+
registry session next to the brain token. `memgineering whoami` reports both —
|
|
51
|
+
the brain identity and a `registry:` line with the username. If the registry
|
|
52
|
+
line says "not linked", the link simply did not happen at sign-in time (older
|
|
53
|
+
server, registry briefly down); a fresh `logout` + `login` links it. Never
|
|
54
|
+
treat a missing registry line as a failed brain sign-in — the brain token
|
|
55
|
+
stands on its own. On the approval page itself, a user already signed in to
|
|
56
|
+
memgineering.com approves with one click; a signed-out user signs in right
|
|
57
|
+
there first (Google in a popup). Consent is asked exactly once, when an
|
|
58
|
+
account is first created.
|
|
59
|
+
|
|
48
60
|
`--no-login` is how someone says "this machine only". Offer it, do not assume it:
|
|
49
61
|
a brain that lives in a folder here cannot follow them to their phone or their
|
|
50
62
|
next laptop, and that is discovered weeks later, by which time the notes are in
|
package/dist/index.js
CHANGED
|
@@ -4074,6 +4074,11 @@ var init_src = __esm({
|
|
|
4074
4074
|
import { mkdir as mkdir3, readFile as readFile3, unlink, writeFile as writeFile2 } from "fs/promises";
|
|
4075
4075
|
import { join as join5 } from "path";
|
|
4076
4076
|
import { z as z6 } from "zod";
|
|
4077
|
+
function registryFromClaim(raw) {
|
|
4078
|
+
const parsed = RegistryCredentialsSchema.omit({ linked_at: true }).safeParse(raw);
|
|
4079
|
+
if (!parsed.success) return void 0;
|
|
4080
|
+
return { ...parsed.data, linked_at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
4081
|
+
}
|
|
4077
4082
|
function credentialsPath() {
|
|
4078
4083
|
return join5(brandHome(), "credentials.json");
|
|
4079
4084
|
}
|
|
@@ -4098,18 +4103,33 @@ async function saveCredentials(credentials) {
|
|
|
4098
4103
|
async function clearCredentials() {
|
|
4099
4104
|
await unlink(credentialsPath()).catch(() => void 0);
|
|
4100
4105
|
}
|
|
4101
|
-
var CredentialsSchema;
|
|
4106
|
+
var RegistryCredentialsSchema, CredentialsSchema;
|
|
4102
4107
|
var init_credentials = __esm({
|
|
4103
4108
|
"src/lib/credentials.ts"() {
|
|
4104
4109
|
"use strict";
|
|
4105
4110
|
init_brand();
|
|
4111
|
+
RegistryCredentialsSchema = z6.object({
|
|
4112
|
+
api_url: z6.string().min(1),
|
|
4113
|
+
refresh_token: z6.string().min(1),
|
|
4114
|
+
session_id: z6.string().min(1),
|
|
4115
|
+
username: z6.string().min(1),
|
|
4116
|
+
user_id: z6.string().min(1),
|
|
4117
|
+
linked_at: z6.string().min(1)
|
|
4118
|
+
});
|
|
4106
4119
|
CredentialsSchema = z6.object({
|
|
4107
4120
|
/** The server that issued this token. Compared before the token is ever sent. */
|
|
4108
4121
|
api_url: z6.string().min(1),
|
|
4109
4122
|
token: z6.string().min(1),
|
|
4110
4123
|
account_id: z6.string().min(1),
|
|
4111
4124
|
display_name: z6.string().nullable().default(null),
|
|
4112
|
-
signed_in_at: z6.string().min(1)
|
|
4125
|
+
signed_in_at: z6.string().min(1),
|
|
4126
|
+
/**
|
|
4127
|
+
* Absent on pre-federation files and after an old CLI resaves this file
|
|
4128
|
+
* (strip mode drops keys it does not know). `catch` matters more than
|
|
4129
|
+
* `optional`: a mangled registry block must degrade to "not linked", never
|
|
4130
|
+
* to "not signed in" — the brain token above is still perfectly good.
|
|
4131
|
+
*/
|
|
4132
|
+
registry: RegistryCredentialsSchema.optional().catch(void 0)
|
|
4113
4133
|
});
|
|
4114
4134
|
}
|
|
4115
4135
|
});
|
|
@@ -4322,12 +4342,14 @@ async function claimOnce(pending, baseUrl = apiUrl()) {
|
|
|
4322
4342
|
);
|
|
4323
4343
|
}
|
|
4324
4344
|
const me = await apiRequest({ method: "GET", path: "/v1/me", token, baseUrl });
|
|
4345
|
+
const registry = registryFromClaim(res.body?.registry);
|
|
4325
4346
|
const credentials = {
|
|
4326
4347
|
api_url: baseUrl,
|
|
4327
4348
|
token,
|
|
4328
4349
|
account_id: me.body?.account?.id ?? "",
|
|
4329
4350
|
display_name: me.body?.account?.display_name ?? null,
|
|
4330
|
-
signed_in_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
4351
|
+
signed_in_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4352
|
+
...registry !== void 0 ? { registry } : {}
|
|
4331
4353
|
};
|
|
4332
4354
|
await saveCredentials(credentials);
|
|
4333
4355
|
await clearPendingLogin();
|
|
@@ -6152,7 +6174,8 @@ async function waitForApproval(pending, base) {
|
|
|
6152
6174
|
json: {
|
|
6153
6175
|
signed_in: true,
|
|
6154
6176
|
account: { id: claimed.account_id, display_name: claimed.display_name },
|
|
6155
|
-
api_url: base
|
|
6177
|
+
api_url: base,
|
|
6178
|
+
registry: claimed.registry ? { username: claimed.registry.username, api_url: claimed.registry.api_url } : null
|
|
6156
6179
|
},
|
|
6157
6180
|
human: () => {
|
|
6158
6181
|
printHuman(
|
|
@@ -6160,6 +6183,13 @@ async function waitForApproval(pending, base) {
|
|
|
6160
6183
|
Signed in${claimed.display_name ? ` as ${c.bold(oneLine(claimed.display_name, WIDTH.title))}` : ""}.`
|
|
6161
6184
|
);
|
|
6162
6185
|
printHuman(c.gray(" The token is stored on this machine only, readable by you alone."));
|
|
6186
|
+
if (claimed.registry) {
|
|
6187
|
+
printHuman(
|
|
6188
|
+
c.gray(
|
|
6189
|
+
` Registry linked: ${oneLine(claimed.registry.username, WIDTH.handle)} \u2014 one sign-in, both halves.`
|
|
6190
|
+
)
|
|
6191
|
+
);
|
|
6192
|
+
}
|
|
6163
6193
|
}
|
|
6164
6194
|
});
|
|
6165
6195
|
return;
|
|
@@ -15702,10 +15732,12 @@ function logoutCommand() {
|
|
|
15702
15732
|
}) : err;
|
|
15703
15733
|
}
|
|
15704
15734
|
}
|
|
15735
|
+
const registryRevoked = await revokeRegistrySession(credentials.registry);
|
|
15705
15736
|
await clearCredentials();
|
|
15706
15737
|
printDual({
|
|
15707
15738
|
json: {
|
|
15708
15739
|
signed_out: true,
|
|
15740
|
+
registry: credentials.registry === void 0 ? "not-linked" : registryRevoked ? "revoked" : "revoke-failed-expires-on-its-own",
|
|
15709
15741
|
revoked_on_server: true,
|
|
15710
15742
|
revoked,
|
|
15711
15743
|
all: opts.all === true,
|
|
@@ -15727,6 +15759,28 @@ function logoutCommand() {
|
|
|
15727
15759
|
});
|
|
15728
15760
|
});
|
|
15729
15761
|
}
|
|
15762
|
+
async function revokeRegistrySession(reg) {
|
|
15763
|
+
if (!reg) return false;
|
|
15764
|
+
try {
|
|
15765
|
+
const refreshed = await fetch(`${reg.api_url}/auth/sessions/refresh`, {
|
|
15766
|
+
method: "POST",
|
|
15767
|
+
headers: { "content-type": "application/json" },
|
|
15768
|
+
body: JSON.stringify({ refresh_token: reg.refresh_token }),
|
|
15769
|
+
signal: AbortSignal.timeout(3e3)
|
|
15770
|
+
});
|
|
15771
|
+
if (!refreshed.ok) return false;
|
|
15772
|
+
const pair = await refreshed.json();
|
|
15773
|
+
if (typeof pair.access_token !== "string" || typeof pair.session_id !== "string") return false;
|
|
15774
|
+
const del = await fetch(`${reg.api_url}/auth/sessions/${pair.session_id}`, {
|
|
15775
|
+
method: "DELETE",
|
|
15776
|
+
headers: { authorization: `Bearer ${pair.access_token}` },
|
|
15777
|
+
signal: AbortSignal.timeout(3e3)
|
|
15778
|
+
});
|
|
15779
|
+
return del.ok;
|
|
15780
|
+
} catch {
|
|
15781
|
+
return false;
|
|
15782
|
+
}
|
|
15783
|
+
}
|
|
15730
15784
|
|
|
15731
15785
|
// src/commands/onboard.ts
|
|
15732
15786
|
init_src();
|
|
@@ -18092,7 +18146,12 @@ function whoamiCommand() {
|
|
|
18092
18146
|
},
|
|
18093
18147
|
tokens: { live },
|
|
18094
18148
|
api_url: base,
|
|
18095
|
-
signed_in_at: credentials.signed_in_at
|
|
18149
|
+
signed_in_at: credentials.signed_in_at,
|
|
18150
|
+
registry: credentials.registry ? {
|
|
18151
|
+
username: credentials.registry.username,
|
|
18152
|
+
api_url: credentials.registry.api_url,
|
|
18153
|
+
linked_at: credentials.registry.linked_at
|
|
18154
|
+
} : null
|
|
18096
18155
|
},
|
|
18097
18156
|
human: () => {
|
|
18098
18157
|
printHuman(
|
|
@@ -18102,6 +18161,19 @@ function whoamiCommand() {
|
|
|
18102
18161
|
);
|
|
18103
18162
|
if (account.email) printHuman(c.gray(` ${oneLine(account.email, WIDTH.title)}`));
|
|
18104
18163
|
printHuman(c.gray(` on ${base}`));
|
|
18164
|
+
if (credentials.registry) {
|
|
18165
|
+
printHuman(
|
|
18166
|
+
c.gray(
|
|
18167
|
+
` registry: ${oneLine(credentials.registry.username, WIDTH.handle)} on ${credentials.registry.api_url}`
|
|
18168
|
+
)
|
|
18169
|
+
);
|
|
18170
|
+
} else {
|
|
18171
|
+
printHuman(
|
|
18172
|
+
c.gray(
|
|
18173
|
+
" registry: not linked \u2014 sign in again (`memgineering logout`, then `login`) to link it."
|
|
18174
|
+
)
|
|
18175
|
+
);
|
|
18176
|
+
}
|
|
18105
18177
|
if (live !== null) {
|
|
18106
18178
|
printHuman(
|
|
18107
18179
|
c.gray(
|
package/package.json
CHANGED