myagentmemory 0.4.15 → 0.4.17
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/LICENSE +1 -0
- package/README.md +17 -4
- package/dist/cli-spec.d.ts +1 -1
- package/dist/cli-spec.js +14 -2
- package/dist/cli.js +107 -42
- package/dist/plugin-bootstrap.js +2 -2
- package/dist/plugin-host.d.ts +18 -0
- package/dist/plugin-runtime.d.ts +8 -1
- package/dist/plugin-runtime.js +39 -0
- package/dist/plugin-service.js +129 -42
- package/docs/official-plugin-bootstrap.md +29 -21
- package/package.json +1 -1
- package/src/cli-spec.ts +14 -2
- package/src/cli.ts +112 -51
- package/src/plugin-bootstrap.ts +2 -2
- package/src/plugin-host.ts +20 -0
- package/src/plugin-runtime.ts +64 -0
- package/src/plugin-service.ts +133 -43
package/src/plugin-service.ts
CHANGED
|
@@ -29,12 +29,12 @@ const MISSING_ENTITLEMENT: PluginEntitlementStatusV1 = {
|
|
|
29
29
|
state: "missing",
|
|
30
30
|
features: [],
|
|
31
31
|
capabilities: {},
|
|
32
|
-
reason: "
|
|
32
|
+
reason: "Install the no-account Pro preview to activate local recall and learning",
|
|
33
33
|
};
|
|
34
34
|
|
|
35
35
|
interface TemporaryActivationV1 {
|
|
36
|
-
schemaVersion:
|
|
37
|
-
|
|
36
|
+
schemaVersion: 3;
|
|
37
|
+
installationId: string;
|
|
38
38
|
activatedAt: string;
|
|
39
39
|
usageCredential: string;
|
|
40
40
|
dailySessionLimit: number;
|
|
@@ -54,24 +54,23 @@ function cloneEntitlement(value: PluginEntitlementStatusV1): PluginEntitlementSt
|
|
|
54
54
|
return structuredClone(value);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
function freeEntitlement(
|
|
57
|
+
function freeEntitlement(): PluginEntitlementStatusV1 {
|
|
58
58
|
return {
|
|
59
59
|
plan: "free",
|
|
60
60
|
state: "active",
|
|
61
61
|
features: ["session-intelligence", "web-console"],
|
|
62
62
|
capabilities: {
|
|
63
63
|
"session-index": { enabled: true },
|
|
64
|
-
"
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
},
|
|
68
|
-
learning: { enabled: true },
|
|
64
|
+
recall: { enabled: true, quota: { limit: 10, window: "day", scope: "device" } },
|
|
65
|
+
"session-worker": { enabled: false },
|
|
66
|
+
learning: { enabled: true, quota: { limit: 1, window: "day", scope: "device" } },
|
|
69
67
|
"retrieval-evaluation": { enabled: true },
|
|
70
68
|
"operational-metrics": { enabled: true },
|
|
71
69
|
"web-console": { enabled: true },
|
|
72
70
|
"memory-explorer": { enabled: true },
|
|
73
71
|
},
|
|
74
|
-
reason:
|
|
72
|
+
reason:
|
|
73
|
+
"Free preview: 10 recalls and 1 learning scan per local day; indexing and dashboard access remain available",
|
|
75
74
|
};
|
|
76
75
|
}
|
|
77
76
|
|
|
@@ -94,13 +93,104 @@ function securityHeaders(contentType: string): Record<string, string> {
|
|
|
94
93
|
};
|
|
95
94
|
}
|
|
96
95
|
|
|
96
|
+
const ACTIVATION_PAGE_STYLES = `
|
|
97
|
+
:root{color-scheme:light;font-family:Inter,ui-sans-serif,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;color:#172033;background:#f4f7fb;font-synthesis:none}
|
|
98
|
+
*{box-sizing:border-box}
|
|
99
|
+
body{min-height:100vh;margin:0;padding:2rem 1.25rem;display:grid;place-items:center;background:radial-gradient(circle at 12% 0%,#e8f0ff 0,transparent 38rem),radial-gradient(circle at 100% 100%,#eef2ff 0,transparent 32rem),#f7f9fc}
|
|
100
|
+
main{width:min(100%,34rem)}
|
|
101
|
+
.skip-link{position:fixed;z-index:1;top:1rem;left:1rem;padding:.65rem .8rem;border-radius:.55rem;background:#172033;color:#fff;font-weight:700;transform:translateY(-200%)}
|
|
102
|
+
.skip-link:focus{transform:translateY(0)}
|
|
103
|
+
.brand{display:flex;align-items:center;gap:.75rem;margin:0 0 1.25rem .25rem;color:#33415c;font-size:.875rem;font-weight:700;letter-spacing:.01em}
|
|
104
|
+
.mark{width:2.25rem;height:2.25rem;display:grid;place-items:center;border-radius:.7rem;background:linear-gradient(145deg,#172033,#3159b8);color:#fff;box-shadow:0 .5rem 1.25rem rgba(35,65,130,.2);font-size:1.05rem;letter-spacing:-.08em}
|
|
105
|
+
.card{padding:clamp(1.5rem,5vw,2.5rem);border:1px solid rgba(205,216,231,.9);border-radius:1.5rem;background:rgba(255,255,255,.94);box-shadow:0 1.5rem 4rem rgba(32,51,84,.12),0 .125rem .375rem rgba(32,51,84,.06);backdrop-filter:blur(1rem)}
|
|
106
|
+
.eyebrow{display:inline-flex;align-items:center;gap:.45rem;margin:0 0 1rem;padding:.42rem .7rem;border:1px solid #d9e4f7;border-radius:999px;background:#f3f7ff;color:#315798;font-size:.78rem;font-weight:750;letter-spacing:.03em;text-transform:uppercase}
|
|
107
|
+
.eyebrow::before{content:"";width:.45rem;height:.45rem;border-radius:50%;background:#2b67d1;box-shadow:0 0 0 .22rem #dbe8ff}
|
|
108
|
+
h1{margin:0;color:#141d2c;font-size:clamp(2rem,7vw,2.75rem);line-height:1.06;letter-spacing:-.045em;text-wrap:balance}
|
|
109
|
+
.intro{margin:.9rem 0 1.75rem;color:#526176;font-size:1.05rem;line-height:1.55}
|
|
110
|
+
form{display:grid;gap:.75rem}
|
|
111
|
+
label{color:#29364a;font-size:.9rem;font-weight:700}
|
|
112
|
+
input{width:100%;min-height:3.25rem;padding:.8rem 3.5rem .8rem 1rem;border:1px solid #bcc8d8;border-radius:.8rem;background:#fff;color:#172033;font:inherit;box-shadow:inset 0 1px 2px rgba(24,39,65,.04)}
|
|
113
|
+
input::placeholder{color:#8a96a7}
|
|
114
|
+
input:hover{border-color:#92a2b8}
|
|
115
|
+
input:focus-visible{outline:0;border-color:#2b67d1;box-shadow:0 0 0 .25rem rgba(43,103,209,.15)}
|
|
116
|
+
button{min-height:3.35rem;margin-top:.25rem;padding:.85rem 1rem;border:1px solid #172033;border-radius:.8rem;background:linear-gradient(180deg,#24334a,#172033);color:#fff;font:inherit;font-weight:750;cursor:pointer;box-shadow:0 .55rem 1.2rem rgba(23,32,51,.18);touch-action:manipulation;transition:transform .15s}
|
|
117
|
+
button:hover{background:linear-gradient(180deg,#2d405c,#1d2a40);box-shadow:0 .7rem 1.4rem rgba(23,32,51,.23);transform:translateY(-1px)}
|
|
118
|
+
button:active{transform:translateY(0)}
|
|
119
|
+
button:focus-visible,summary:focus-visible{outline:.2rem solid rgba(43,103,209,.32);outline-offset:.18rem}
|
|
120
|
+
.terminal-note{margin:.8rem 0 0;color:#6a7789;font-size:.85rem;text-align:center}
|
|
121
|
+
.error{margin:0 0 1rem;padding:.8rem 1rem;border:1px solid #efb8bd;border-radius:.75rem;background:#fff3f4;color:#982631;font-size:.9rem;line-height:1.45}
|
|
122
|
+
details{margin-top:1.5rem;padding-top:1.25rem;border-top:1px solid #e2e8f0;color:#5b687a;font-size:.85rem;line-height:1.55}
|
|
123
|
+
summary{color:#46566d;font-weight:700;cursor:pointer;list-style-position:outside;touch-action:manipulation}
|
|
124
|
+
details p{margin:.8rem 0 0}
|
|
125
|
+
.never-sent{padding:.75rem .85rem;border-radius:.65rem;background:#f5f7fa;color:#536176}
|
|
126
|
+
.success{display:grid;place-items:center;width:3.5rem;height:3.5rem;margin-bottom:1.4rem;border-radius:1rem;background:#eaf7ef;color:#197542;font-size:1.65rem;font-weight:800;box-shadow:inset 0 0 0 1px #c9ead6}
|
|
127
|
+
.completion .intro{margin-bottom:0}
|
|
128
|
+
@media (max-width:30rem){body{padding:1rem}.brand{margin-left:.1rem}.card{border-radius:1.15rem}h1{font-size:2rem}}
|
|
129
|
+
@media (prefers-reduced-motion:reduce){input,button{transition:none}}
|
|
130
|
+
`;
|
|
131
|
+
|
|
97
132
|
function activationPage(action: string, error?: string): string {
|
|
98
|
-
const errorHtml = error ? `<p class="error">${error}</p>` : "";
|
|
99
|
-
|
|
133
|
+
const errorHtml = error ? `<p class="error" id="activation-error" role="alert">${error}</p>` : "";
|
|
134
|
+
const describedBy = error ? "activation-error terminal-note" : "terminal-note";
|
|
135
|
+
const autofocus = error ? " autofocus" : "";
|
|
136
|
+
return `<!doctype html>
|
|
137
|
+
<html lang="en">
|
|
138
|
+
<head>
|
|
139
|
+
<meta charset="utf-8">
|
|
140
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
141
|
+
<meta name="theme-color" content="#f4f7fb">
|
|
142
|
+
<title>Activate AgentMemory</title>
|
|
143
|
+
<style>${ACTIVATION_PAGE_STYLES}</style>
|
|
144
|
+
</head>
|
|
145
|
+
<body>
|
|
146
|
+
<a class="skip-link" href="#activation">Skip to Activation</a>
|
|
147
|
+
<main id="activation">
|
|
148
|
+
<div class="brand" translate="no"><span class="mark" aria-hidden="true">AM</span><span>AgentMemory</span></div>
|
|
149
|
+
<section class="card" aria-labelledby="activation-title">
|
|
150
|
+
<p class="eyebrow">Free daily allowance</p>
|
|
151
|
+
<h1 id="activation-title">Activate This Device</h1>
|
|
152
|
+
<p class="intro">Use your email to enable AgentMemory Pro for your local agent sessions.</p>
|
|
153
|
+
<form method="post" action="${action}">
|
|
154
|
+
<label for="email">Email address</label>
|
|
155
|
+
${errorHtml}
|
|
156
|
+
<input id="email" type="email" name="email" autocomplete="email" inputmode="email" spellcheck="false" placeholder="you@example.com…" maxlength="254" required${autofocus} aria-describedby="${describedBy}">
|
|
157
|
+
<button type="submit">Activate AgentMemory</button>
|
|
158
|
+
</form>
|
|
159
|
+
<p class="terminal-note" id="terminal-note">Your terminal will finish setup after activation.</p>
|
|
160
|
+
<details>
|
|
161
|
+
<summary>What’s shared during activation</summary>
|
|
162
|
+
<p>Your email identifies your free daily allowance. The CLI also sends core and bundle versions, platform, architecture, and release channel. The service stores a daily count of opaque session-start operations. Activation records expire after 365 days without use.</p>
|
|
163
|
+
<p class="never-sent"><strong>Never sent:</strong> The request never includes memory, session content, queries, repository paths, raw agent session identifiers, IP addresses, or user-agent strings.</p>
|
|
164
|
+
</details>
|
|
165
|
+
</section>
|
|
166
|
+
</main>
|
|
167
|
+
</body>
|
|
168
|
+
</html>`;
|
|
100
169
|
}
|
|
101
170
|
|
|
102
171
|
function completionPage(): string {
|
|
103
|
-
return
|
|
172
|
+
return `<!doctype html>
|
|
173
|
+
<html lang="en">
|
|
174
|
+
<head>
|
|
175
|
+
<meta charset="utf-8">
|
|
176
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
177
|
+
<meta name="theme-color" content="#f4f7fb">
|
|
178
|
+
<title>AgentMemory activated</title>
|
|
179
|
+
<style>${ACTIVATION_PAGE_STYLES}</style>
|
|
180
|
+
</head>
|
|
181
|
+
<body>
|
|
182
|
+
<a class="skip-link" href="#completion">Skip to Activation Status</a>
|
|
183
|
+
<main id="completion">
|
|
184
|
+
<div class="brand" translate="no"><span class="mark" aria-hidden="true">AM</span><span>AgentMemory</span></div>
|
|
185
|
+
<section class="card completion" aria-labelledby="completion-title">
|
|
186
|
+
<div class="success" aria-hidden="true">✓</div>
|
|
187
|
+
<p class="eyebrow">Device activated</p>
|
|
188
|
+
<h1 id="completion-title">You’re All Set</h1>
|
|
189
|
+
<p class="intro">Return to your terminal to finish installing AgentMemory Pro. You can close this tab.</p>
|
|
190
|
+
</section>
|
|
191
|
+
</main>
|
|
192
|
+
</body>
|
|
193
|
+
</html>`;
|
|
104
194
|
}
|
|
105
195
|
|
|
106
196
|
function send(response: ServerResponse, status: number, body: string, contentType = "text/html; charset=utf-8"): void {
|
|
@@ -297,12 +387,12 @@ export class TemporaryPluginBackend implements PluginBootstrapBackendV1 {
|
|
|
297
387
|
this.artifactOrigin = options.artifactOrigin ?? ARTIFACT_ORIGIN;
|
|
298
388
|
this.fetchImplementation = options.fetch ?? globalThis.fetch;
|
|
299
389
|
this.openUrl = options.openUrl ?? openLoopbackUrl;
|
|
300
|
-
this.activate = options.activate ?? (() =>
|
|
390
|
+
this.activate = options.activate ?? (async () => `am_install_${randomBytes(24).toString("base64url")}`);
|
|
301
391
|
}
|
|
302
392
|
|
|
303
393
|
async getLocalEntitlement(): Promise<PluginEntitlementStatusV1> {
|
|
304
394
|
const activation = this.readActivation();
|
|
305
|
-
return activation ? freeEntitlement(
|
|
395
|
+
return activation ? freeEntitlement() : cloneEntitlement(MISSING_ENTITLEMENT);
|
|
306
396
|
}
|
|
307
397
|
|
|
308
398
|
async resolveAccess(request: {
|
|
@@ -312,33 +402,19 @@ export class TemporaryPluginBackend implements PluginBootstrapBackendV1 {
|
|
|
312
402
|
allowAuthentication: boolean;
|
|
313
403
|
}): Promise<PluginAccessDecisionV1> {
|
|
314
404
|
const activation = this.readActivation();
|
|
315
|
-
|
|
316
|
-
if (!email) {
|
|
317
|
-
if (!request.allowAuthentication)
|
|
318
|
-
return {
|
|
319
|
-
kind: "auth_required",
|
|
320
|
-
entitlement: cloneEntitlement(MISSING_ENTITLEMENT),
|
|
321
|
-
nextAction: {
|
|
322
|
-
kind: "authenticate",
|
|
323
|
-
url: "https://jayzeng.github.io/agentmemory/",
|
|
324
|
-
message: "Run plugin install in an interactive terminal to enter an email address",
|
|
325
|
-
},
|
|
326
|
-
};
|
|
327
|
-
email = await this.activate();
|
|
328
|
-
}
|
|
405
|
+
const installationId = activation?.installationId ?? (await this.activate());
|
|
329
406
|
const response = await this.request(`${this.apiOrigin}/v1/plugin/access`, {
|
|
330
407
|
method: "POST",
|
|
331
408
|
headers: { "Content-Type": "application/json" },
|
|
332
409
|
body: JSON.stringify({
|
|
333
|
-
schemaVersion:
|
|
334
|
-
|
|
410
|
+
schemaVersion: 2,
|
|
411
|
+
installationId,
|
|
335
412
|
bundleId: request.bundleId,
|
|
336
413
|
installedVersion: request.installedVersion ?? null,
|
|
337
414
|
coreVersion: this.coreVersion,
|
|
338
415
|
channel: request.channel,
|
|
339
416
|
platform: process.platform,
|
|
340
417
|
architecture: process.arch,
|
|
341
|
-
consentVersion: "activation-v2",
|
|
342
418
|
}),
|
|
343
419
|
});
|
|
344
420
|
const value = (await readJson(response)) as {
|
|
@@ -354,16 +430,22 @@ export class TemporaryPluginBackend implements PluginBootstrapBackendV1 {
|
|
|
354
430
|
"service_response_invalid",
|
|
355
431
|
"The access response omitted its usage credential",
|
|
356
432
|
);
|
|
357
|
-
const
|
|
433
|
+
const recallQuota = value.entitlement.capabilities.recall?.quota;
|
|
434
|
+
const learningQuota = value.entitlement.capabilities.learning?.quota;
|
|
358
435
|
if (
|
|
359
436
|
value.entitlement.plan !== "free" ||
|
|
360
437
|
value.entitlement.state !== "active" ||
|
|
361
|
-
!
|
|
362
|
-
|
|
363
|
-
|
|
438
|
+
!recallQuota ||
|
|
439
|
+
recallQuota.scope !== "device" ||
|
|
440
|
+
recallQuota.window !== "day" ||
|
|
441
|
+
!learningQuota ||
|
|
442
|
+
learningQuota.scope !== "device" ||
|
|
443
|
+
learningQuota.window !== "day" ||
|
|
444
|
+
value.entitlement.capabilities["session-index"]?.enabled !== true ||
|
|
445
|
+
value.entitlement.capabilities["web-console"]?.enabled !== true
|
|
364
446
|
)
|
|
365
|
-
throw new PluginBootstrapFailure("service_response_invalid", "The free
|
|
366
|
-
this.writeActivation(
|
|
447
|
+
throw new PluginBootstrapFailure("service_response_invalid", "The free preview policy is invalid");
|
|
448
|
+
this.writeActivation(installationId, value.usageCredential, 1);
|
|
367
449
|
return { kind: "granted", entitlement: value.entitlement, artifactGrant: value.artifactGrant };
|
|
368
450
|
}
|
|
369
451
|
|
|
@@ -432,8 +514,9 @@ export class TemporaryPluginBackend implements PluginBootstrapBackendV1 {
|
|
|
432
514
|
return null;
|
|
433
515
|
const value = JSON.parse(fs.readFileSync(activationPath, "utf-8")) as TemporaryActivationV1;
|
|
434
516
|
if (
|
|
435
|
-
value.schemaVersion !==
|
|
436
|
-
|
|
517
|
+
value.schemaVersion !== 3 ||
|
|
518
|
+
typeof value.installationId !== "string" ||
|
|
519
|
+
!/^am_install_[A-Za-z0-9_-]{32}$/.test(value.installationId) ||
|
|
437
520
|
!Number.isFinite(Date.parse(value.activatedAt)) ||
|
|
438
521
|
!ACTIVATION_CREDENTIAL.test(value.usageCredential) ||
|
|
439
522
|
!Number.isSafeInteger(value.dailySessionLimit) ||
|
|
@@ -447,8 +530,9 @@ export class TemporaryPluginBackend implements PluginBootstrapBackendV1 {
|
|
|
447
530
|
}
|
|
448
531
|
}
|
|
449
532
|
|
|
450
|
-
private writeActivation(
|
|
451
|
-
if (
|
|
533
|
+
private writeActivation(installationId: string, usageCredential: string, dailySessionLimit: number): void {
|
|
534
|
+
if (!/^am_install_[A-Za-z0-9_-]{32}$/.test(installationId))
|
|
535
|
+
throw new PluginBootstrapFailure("activation_failed", "The installation identifier is invalid");
|
|
452
536
|
if (!ACTIVATION_CREDENTIAL.test(usageCredential))
|
|
453
537
|
throw new PluginBootstrapFailure("activation_failed", "The activation credential is invalid");
|
|
454
538
|
if (!Number.isSafeInteger(dailySessionLimit) || dailySessionLimit <= 0 || dailySessionLimit > 10_000)
|
|
@@ -467,7 +551,13 @@ export class TemporaryPluginBackend implements PluginBootstrapBackendV1 {
|
|
|
467
551
|
fs.writeFileSync(
|
|
468
552
|
temporary,
|
|
469
553
|
`${JSON.stringify(
|
|
470
|
-
{
|
|
554
|
+
{
|
|
555
|
+
schemaVersion: 3,
|
|
556
|
+
installationId,
|
|
557
|
+
activatedAt: new Date().toISOString(),
|
|
558
|
+
usageCredential,
|
|
559
|
+
dailySessionLimit,
|
|
560
|
+
},
|
|
471
561
|
null,
|
|
472
562
|
2,
|
|
473
563
|
)}\n`,
|