@mnemom/mnemom 0.14.6 → 0.15.1-next.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/dist/commands/activity.d.ts +2 -0
- package/dist/commands/{integrity.js → activity.js} +4 -1
- package/dist/commands/agents.js +18 -1
- package/dist/commands/api-key.js +20 -4
- package/dist/commands/auth.js +39 -32
- package/dist/commands/card.js +17 -2
- package/dist/commands/org.js +15 -2
- package/dist/commands/protection.d.ts +2 -1
- package/dist/commands/protection.js +163 -3
- package/dist/commands/status.js +4 -1
- package/dist/commands/try-me.d.ts +52 -0
- package/dist/commands/try-me.js +374 -0
- package/dist/index.js +63 -3
- package/dist/lib/api.js +5 -6
- package/dist/lib/auth.d.ts +33 -10
- package/dist/lib/auth.js +71 -201
- package/dist/lib/oauth.d.ts +132 -0
- package/dist/lib/oauth.js +490 -0
- package/dist/lib/prompt.js +19 -7
- package/dist/lib/try-me.d.ts +181 -0
- package/dist/lib/try-me.js +245 -0
- package/package.json +3 -2
- package/dist/commands/integrity.d.ts +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { resolveAgentId, getIntegrity, MnemomApiError } from "../lib/api.js";
|
|
2
2
|
import { fmt } from "../lib/format.js";
|
|
3
3
|
import { TRACES_PENDING_NOTE } from "../lib/onboarding.js";
|
|
4
|
-
export async function
|
|
4
|
+
export async function activityCommand(agentName) {
|
|
5
5
|
const agentId = await resolveAgentId(agentName);
|
|
6
6
|
console.log("\nFetching agent activity...\n");
|
|
7
7
|
try {
|
|
@@ -47,6 +47,9 @@ export async function integrityCommand(agentName) {
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
|
+
// `mnemom integrity` is retained as a deprecated alias of `mnemom activity`
|
|
51
|
+
// (AAP = Activity). It still works and prints a deprecation notice; no breaking change.
|
|
52
|
+
export const integrityCommand = activityCommand;
|
|
50
53
|
function generateScoreBar(score) {
|
|
51
54
|
const filled = Math.round(score * 10);
|
|
52
55
|
const empty = 10 - filled;
|
package/dist/commands/agents.js
CHANGED
|
@@ -66,7 +66,7 @@ const FULL_PROOF_RE = /^[0-9a-f]{64}$/;
|
|
|
66
66
|
// digest and break the claim handshake entirely, so SHA-256 is mandated by the
|
|
67
67
|
// protocol — the "insufficient computational effort" alert is a false positive.
|
|
68
68
|
function sha256Hex(input) {
|
|
69
|
-
//
|
|
69
|
+
// codeql[js/insufficient-password-hash] — protocol proof, not password storage; see block comment above
|
|
70
70
|
return createHash("sha256").update(input, "utf8").digest("hex"); // codeql[js/insufficient-password-hash]
|
|
71
71
|
}
|
|
72
72
|
/**
|
|
@@ -289,8 +289,25 @@ export async function agentsClaimCommand(idOrName, options = {}) {
|
|
|
289
289
|
}
|
|
290
290
|
console.log(fmt.success(`Claimed ${result.agent_id}.`));
|
|
291
291
|
console.log(fmt.label(" Landed in:", where));
|
|
292
|
+
if (derivationName) {
|
|
293
|
+
console.log(fmt.label(" Name:", derivationName));
|
|
294
|
+
}
|
|
292
295
|
if (result.claimed_at) {
|
|
293
296
|
console.log(fmt.label(" Claimed at:", new Date(result.claimed_at).toLocaleString()));
|
|
294
297
|
}
|
|
298
|
+
// Bridge to the next step (MNE-610). The dogfood dead-end was: claim with a
|
|
299
|
+
// friendly --name, then `mnemom card publish --agent <name>` returns an
|
|
300
|
+
// opaque "agent not found" while the freshly-claimed agent propagates into
|
|
301
|
+
// the listing the name-resolver reads. Always surface a ready-to-paste
|
|
302
|
+
// command keyed on the CANONICAL agent id, which `resolveAgentId` accepts
|
|
303
|
+
// verbatim (no list lookup) — so the publish step never dead-ends regardless
|
|
304
|
+
// of listing propagation. The friendly name is shown as the eventual
|
|
305
|
+
// shorthand, not the recommended first move.
|
|
306
|
+
console.log();
|
|
307
|
+
console.log(fmt.dim(" Next — publish your alignment card with the canonical id:"));
|
|
308
|
+
console.log(fmt.dim(` mnemom card publish <file.yaml> --agent ${result.agent_id}`));
|
|
309
|
+
if (derivationName) {
|
|
310
|
+
console.log(fmt.dim(` Once it appears in \`mnemom agents\`, you can use the name instead: --agent ${derivationName}`));
|
|
311
|
+
}
|
|
295
312
|
console.log();
|
|
296
313
|
}
|
package/dist/commands/api-key.js
CHANGED
|
@@ -64,7 +64,11 @@ function formatRow(key) {
|
|
|
64
64
|
}
|
|
65
65
|
// ─── mnemom api-key list ─────────────────────────────────────────────────
|
|
66
66
|
export async function apiKeyListCommand(opts) {
|
|
67
|
-
await requireAuth();
|
|
67
|
+
const cred = await requireAuth();
|
|
68
|
+
if (cred.type === "api-key") {
|
|
69
|
+
console.log(fmt.warn("API keys are agent-scoped; run mnemom login to manage personal API keys"));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
68
72
|
let keys;
|
|
69
73
|
try {
|
|
70
74
|
keys = await listApiKeys();
|
|
@@ -92,7 +96,11 @@ export async function apiKeyListCommand(opts) {
|
|
|
92
96
|
}
|
|
93
97
|
// ─── mnemom api-key create ───────────────────────────────────────────────
|
|
94
98
|
export async function apiKeyCreateCommand(opts) {
|
|
95
|
-
await requireAuth();
|
|
99
|
+
const cred = await requireAuth();
|
|
100
|
+
if (cred.type === "api-key") {
|
|
101
|
+
console.log(fmt.warn("API keys are agent-scoped; run mnemom login to manage personal API keys"));
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
96
104
|
if (!opts.name || opts.name.trim() === "") {
|
|
97
105
|
console.error(fmt.error("--name is required. Example: --name 'ci-prod'"));
|
|
98
106
|
process.exit(1);
|
|
@@ -133,7 +141,11 @@ export async function apiKeyCreateCommand(opts) {
|
|
|
133
141
|
}
|
|
134
142
|
// ─── mnemom api-key rotate ───────────────────────────────────────────────
|
|
135
143
|
export async function apiKeyRotateCommand(keyId, opts) {
|
|
136
|
-
await requireAuth();
|
|
144
|
+
const cred = await requireAuth();
|
|
145
|
+
if (cred.type === "api-key") {
|
|
146
|
+
console.log(fmt.warn("API keys are agent-scoped; run mnemom login to manage personal API keys"));
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
137
149
|
if (!keyId) {
|
|
138
150
|
console.error(fmt.error("Usage: mnemom api-key rotate <key_id>"));
|
|
139
151
|
process.exit(1);
|
|
@@ -163,7 +175,11 @@ export async function apiKeyRotateCommand(keyId, opts) {
|
|
|
163
175
|
}
|
|
164
176
|
// ─── mnemom api-key revoke ───────────────────────────────────────────────
|
|
165
177
|
export async function apiKeyRevokeCommand(keyId) {
|
|
166
|
-
await requireAuth();
|
|
178
|
+
const cred = await requireAuth();
|
|
179
|
+
if (cred.type === "api-key") {
|
|
180
|
+
console.log(fmt.warn("API keys are agent-scoped; run mnemom login to manage personal API keys"));
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
167
183
|
if (!keyId) {
|
|
168
184
|
console.error(fmt.error("Usage: mnemom api-key revoke <key_id>"));
|
|
169
185
|
process.exit(1);
|
package/dist/commands/auth.js
CHANGED
|
@@ -1,38 +1,13 @@
|
|
|
1
|
-
import { getAuthInfo, clearAuthTokens, loginWithBrowser,
|
|
1
|
+
import { getAuthInfo, clearAuthTokens, loginWithBrowser, loginWithDeviceFlow, resolveAuth, } from "../lib/auth.js";
|
|
2
2
|
import { fmt } from "../lib/format.js";
|
|
3
|
-
import { askInput } from "../lib/prompt.js";
|
|
4
3
|
export async function loginCommand(options = {}) {
|
|
5
4
|
try {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
// lines (email, then password). If either is missing we fail loudly with
|
|
10
|
-
// a non-zero exit and never touch the stored session (MNE-269).
|
|
11
|
-
const piped = !process.stdin.isTTY;
|
|
12
|
-
const pipeHint = "Pipe credentials as two lines: printf 'EMAIL\\nPASSWORD\\n' | mnemom login --no-browser";
|
|
13
|
-
const email = await askInput("Email:");
|
|
14
|
-
if (!email) {
|
|
15
|
-
console.log(fmt.error("Email is required."));
|
|
16
|
-
if (piped)
|
|
17
|
-
console.log(fmt.error(`No email received on stdin. ${pipeHint}`));
|
|
18
|
-
process.exit(1);
|
|
19
|
-
}
|
|
20
|
-
const password = await askInput("Password:", true);
|
|
21
|
-
if (!password) {
|
|
22
|
-
console.log(fmt.error("Password is required."));
|
|
23
|
-
if (piped)
|
|
24
|
-
console.log(fmt.error(`No password received on stdin. ${pipeHint}`));
|
|
25
|
-
process.exit(1);
|
|
26
|
-
}
|
|
27
|
-
tokens = await loginWithPassword(email, password);
|
|
28
|
-
}
|
|
29
|
-
else {
|
|
30
|
-
tokens = await loginWithBrowser();
|
|
31
|
-
}
|
|
5
|
+
// --no-browser → RFC 8628 device flow (headless / SSH / no local browser).
|
|
6
|
+
// Default → OAuth 2.1 authorization-code + PKCE with a loopback redirect.
|
|
7
|
+
const tokens = options.noBrowser ? await loginWithDeviceFlow() : await loginWithBrowser();
|
|
32
8
|
console.log();
|
|
33
9
|
console.log(fmt.success("Logged in successfully!"));
|
|
34
|
-
|
|
35
|
-
console.log(fmt.label(" User ID:", ` ${tokens.userId}`));
|
|
10
|
+
printGrant(tokens);
|
|
36
11
|
console.log();
|
|
37
12
|
}
|
|
38
13
|
catch (error) {
|
|
@@ -41,11 +16,37 @@ export async function loginCommand(options = {}) {
|
|
|
41
16
|
process.exit(1);
|
|
42
17
|
}
|
|
43
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Print what the issued token actually grants. OAuth access tokens are scoped
|
|
21
|
+
* and (deliberately) carry no user identity, so we show scope + expiry rather
|
|
22
|
+
* than fabricating an email/user id we don't have.
|
|
23
|
+
*/
|
|
24
|
+
function printGrant(tokens) {
|
|
25
|
+
if (tokens.email)
|
|
26
|
+
console.log(fmt.label(" Email: ", ` ${tokens.email}`));
|
|
27
|
+
if (tokens.scope)
|
|
28
|
+
console.log(fmt.label(" Scope: ", ` ${tokens.scope}`));
|
|
29
|
+
const expiresDate = new Date(tokens.expiresAt * 1000).toISOString();
|
|
30
|
+
console.log(fmt.label(" Expires:", ` ${expiresDate}`));
|
|
31
|
+
}
|
|
44
32
|
export async function logoutCommand() {
|
|
45
33
|
clearAuthTokens();
|
|
46
34
|
console.log(fmt.success("Logged out."));
|
|
47
35
|
}
|
|
48
36
|
export async function whoamiCommand() {
|
|
37
|
+
const cred = await resolveAuth();
|
|
38
|
+
if (cred.type === "none") {
|
|
39
|
+
console.log("\nNot logged in. Run `mnemom login` to authenticate.\n");
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (cred.type === "api-key") {
|
|
43
|
+
console.log(fmt.header("Auth Status"));
|
|
44
|
+
console.log();
|
|
45
|
+
console.log(fmt.label(" Credential Type:", " API key"));
|
|
46
|
+
console.log(fmt.label(" Status: ", " valid (no expiry)"));
|
|
47
|
+
console.log();
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
49
50
|
const auth = getAuthInfo();
|
|
50
51
|
if (!auth) {
|
|
51
52
|
console.log("\nNot logged in. Run `mnemom login` to authenticate.\n");
|
|
@@ -56,8 +57,14 @@ export async function whoamiCommand() {
|
|
|
56
57
|
const expiresDate = new Date(auth.expiresAt * 1000).toISOString();
|
|
57
58
|
console.log(fmt.header("Auth Status"));
|
|
58
59
|
console.log();
|
|
59
|
-
|
|
60
|
-
|
|
60
|
+
// OAuth access tokens are opaque and carry no identity; legacy sessions may
|
|
61
|
+
// still have email/userId. Show whatever the stored grant actually has.
|
|
62
|
+
if (auth.email)
|
|
63
|
+
console.log(fmt.label(" Email: ", ` ${auth.email}`));
|
|
64
|
+
if (auth.userId)
|
|
65
|
+
console.log(fmt.label(" User ID:", ` ${auth.userId}`));
|
|
66
|
+
if (auth.scope)
|
|
67
|
+
console.log(fmt.label(" Scope: ", ` ${auth.scope}`));
|
|
61
68
|
console.log(fmt.label(" Token: ", expired ? " expired" : ` valid until ${expiresDate}`));
|
|
62
69
|
console.log();
|
|
63
70
|
}
|
package/dist/commands/card.js
CHANGED
|
@@ -654,8 +654,23 @@ export async function cardPublishCommand(file, agentName, options = {}) {
|
|
|
654
654
|
console.log();
|
|
655
655
|
}
|
|
656
656
|
catch (error) {
|
|
657
|
-
|
|
658
|
-
|
|
657
|
+
if (error instanceof MnemomApiError) {
|
|
658
|
+
if (error.effectiveStatus === 404) {
|
|
659
|
+
console.log("\n" +
|
|
660
|
+
fmt.error("Agent found but not writable: you can see this agent locally, but cannot publish to it in its current organization context.") +
|
|
661
|
+
"\n");
|
|
662
|
+
}
|
|
663
|
+
else if (error.effectiveStatus === 401) {
|
|
664
|
+
console.log("\n" + fmt.error(`Authentication failed: ${error.message}`) + "\n");
|
|
665
|
+
}
|
|
666
|
+
else {
|
|
667
|
+
console.log("\n" + fmt.error(`Failed to publish card: ${error.message}`) + "\n");
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
else {
|
|
671
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
672
|
+
console.log("\n" + fmt.error(`Failed to publish card: ${message}`) + "\n");
|
|
673
|
+
}
|
|
659
674
|
process.exit(1);
|
|
660
675
|
}
|
|
661
676
|
}
|
package/dist/commands/org.js
CHANGED
|
@@ -10,7 +10,11 @@ import { fmt } from "../lib/format.js";
|
|
|
10
10
|
* machine-readable output.
|
|
11
11
|
*/
|
|
12
12
|
export async function orgListCommand(opts) {
|
|
13
|
-
await requireAuth();
|
|
13
|
+
const cred = await requireAuth();
|
|
14
|
+
if (cred.type === "api-key") {
|
|
15
|
+
console.log(fmt.warn("API keys are agent-scoped; run mnemom login for org/key management"));
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
14
18
|
let orgs;
|
|
15
19
|
try {
|
|
16
20
|
orgs = await listMyOrgs();
|
|
@@ -56,7 +60,16 @@ export async function orgListCommand(opts) {
|
|
|
56
60
|
* exactly one membership, that one).
|
|
57
61
|
*/
|
|
58
62
|
export async function orgShowCommand(orgIdArg, opts) {
|
|
59
|
-
await requireAuth();
|
|
63
|
+
const cred = await requireAuth();
|
|
64
|
+
if (cred.type === "api-key") {
|
|
65
|
+
if (opts.personal || orgIdArg) {
|
|
66
|
+
console.log(fmt.warn("API keys cannot filter by organization. Run mnemom login to authenticate as a user and list org-scoped agents."));
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
console.log(fmt.warn("API keys are agent-scoped; run mnemom login for org/key management"));
|
|
70
|
+
}
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
60
73
|
let target;
|
|
61
74
|
try {
|
|
62
75
|
if (opts.personal) {
|
|
@@ -9,7 +9,8 @@ export interface ValidationCheck {
|
|
|
9
9
|
* Required: card_version, agent_id, mode (off|observe|nudge|enforce).
|
|
10
10
|
* Optional: thresholds (warn ≤ quarantine ≤ block, all in [0,1]),
|
|
11
11
|
* screen_surfaces (object of bools with the four named keys),
|
|
12
|
-
* trusted_sources (object of typed buckets, per-bucket deny-lists)
|
|
12
|
+
* trusted_sources (object of typed buckets, per-bucket deny-lists),
|
|
13
|
+
* protected_surface (org-declared asset policy — MNE-830/833).
|
|
13
14
|
*/
|
|
14
15
|
export declare function validateProtectionCard(card: Record<string, unknown>): ValidationCheck[];
|
|
15
16
|
export declare function protectionShowCommand(agentName?: string): Promise<void>;
|
|
@@ -9,6 +9,8 @@ import { fmt } from "../lib/format.js";
|
|
|
9
9
|
import { askYesNo, isInteractive } from "../lib/prompt.js";
|
|
10
10
|
const PROTECTION_MODES = ["off", "observe", "nudge", "enforce"];
|
|
11
11
|
const SURFACE_KEYS = ["incoming", "outgoing", "tool_calls", "tool_responses"];
|
|
12
|
+
// Mirrors mnemom-api/src/composition/validate.ts OP_SEVERITIES (MNE-833).
|
|
13
|
+
const OP_SEVERITIES = ["low", "medium", "high", "critical"];
|
|
12
14
|
// Per ADR-037 Decision 4: deny public LLM endpoints + public DNS providers,
|
|
13
15
|
// and the any-host CIDRs, at write time.
|
|
14
16
|
// T8-4 (2026-05-19) extended the corpus per
|
|
@@ -133,13 +135,152 @@ function validateTrustedBucket(name, bucket, checks, perEntry) {
|
|
|
133
135
|
});
|
|
134
136
|
}
|
|
135
137
|
}
|
|
138
|
+
// ── protected_surface helpers (MNE-833) ─────────────────────────────────────
|
|
139
|
+
// Mirrors mnemom-api/src/composition/validate.ts validateProtectedSurface +
|
|
140
|
+
// validateOpArray. Both codebases live in different repos; keep in sync.
|
|
141
|
+
function validateOpArray(name, value, checks, withSeverity) {
|
|
142
|
+
if (value === undefined)
|
|
143
|
+
return;
|
|
144
|
+
if (!Array.isArray(value)) {
|
|
145
|
+
checks.push({
|
|
146
|
+
name: `protected_surface.${name}`,
|
|
147
|
+
passed: false,
|
|
148
|
+
message: `protected_surface.${name} must be an array.`,
|
|
149
|
+
});
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
value.forEach((entry, i) => {
|
|
153
|
+
if (!isObject(entry)) {
|
|
154
|
+
checks.push({
|
|
155
|
+
name: `protected_surface.${name}[${i}]`,
|
|
156
|
+
passed: false,
|
|
157
|
+
message: `each protected_surface.${name} entry must be an object.`,
|
|
158
|
+
});
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const op = entry;
|
|
162
|
+
if (typeof op.pattern !== "string" || op.pattern.length === 0) {
|
|
163
|
+
checks.push({
|
|
164
|
+
name: `protected_surface.${name}[${i}].pattern`,
|
|
165
|
+
passed: false,
|
|
166
|
+
message: `protected_surface.${name}[].pattern is required (non-empty string).`,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
if (op.applies_to !== undefined) {
|
|
170
|
+
if (!Array.isArray(op.applies_to)) {
|
|
171
|
+
checks.push({
|
|
172
|
+
name: `protected_surface.${name}[${i}].applies_to`,
|
|
173
|
+
passed: false,
|
|
174
|
+
message: `protected_surface.${name}[].applies_to must be an array of asset-identity strings.`,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
else if (!op.applies_to.every((x) => typeof x === "string")) {
|
|
178
|
+
checks.push({
|
|
179
|
+
name: `protected_surface.${name}[${i}].applies_to`,
|
|
180
|
+
passed: false,
|
|
181
|
+
message: `protected_surface.${name}[].applies_to entries must be strings.`,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (withSeverity &&
|
|
186
|
+
op.severity !== undefined &&
|
|
187
|
+
!OP_SEVERITIES.includes(String(op.severity))) {
|
|
188
|
+
checks.push({
|
|
189
|
+
name: `protected_surface.${name}[${i}].severity`,
|
|
190
|
+
passed: false,
|
|
191
|
+
message: `protected_surface.${name}[].severity must be one of: ${OP_SEVERITIES.join(", ")}.`,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
if (op.reason !== undefined && typeof op.reason !== "string") {
|
|
195
|
+
checks.push({
|
|
196
|
+
name: `protected_surface.${name}[${i}].reason`,
|
|
197
|
+
passed: false,
|
|
198
|
+
message: `protected_surface.${name}[].reason must be a string.`,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
function validateProtectedSurface(input, checks) {
|
|
204
|
+
if (!isObject(input)) {
|
|
205
|
+
checks.push({
|
|
206
|
+
name: "protected_surface",
|
|
207
|
+
passed: false,
|
|
208
|
+
message: "protected_surface must be an object with assets, forbidden_operations, escalation_required arrays.",
|
|
209
|
+
});
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
const ps = input;
|
|
213
|
+
// assets[] — each needs a non-empty string kind + selector (intrinsic identity).
|
|
214
|
+
if (ps.assets !== undefined) {
|
|
215
|
+
if (!Array.isArray(ps.assets)) {
|
|
216
|
+
checks.push({
|
|
217
|
+
name: "protected_surface.assets",
|
|
218
|
+
passed: false,
|
|
219
|
+
message: "protected_surface.assets must be an array.",
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
ps.assets.forEach((entry, i) => {
|
|
224
|
+
if (!isObject(entry)) {
|
|
225
|
+
checks.push({
|
|
226
|
+
name: `protected_surface.assets[${i}]`,
|
|
227
|
+
passed: false,
|
|
228
|
+
message: "each protected_surface.assets entry must be an object.",
|
|
229
|
+
});
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
const a = entry;
|
|
233
|
+
if (typeof a.kind !== "string" || a.kind.length === 0) {
|
|
234
|
+
checks.push({
|
|
235
|
+
name: `protected_surface.assets[${i}].kind`,
|
|
236
|
+
passed: false,
|
|
237
|
+
message: "protected_surface.assets[].kind is required (non-empty string).",
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
if (typeof a.selector !== "string" || a.selector.length === 0) {
|
|
241
|
+
checks.push({
|
|
242
|
+
name: `protected_surface.assets[${i}].selector`,
|
|
243
|
+
passed: false,
|
|
244
|
+
message: "protected_surface.assets[].selector is required (non-empty string).",
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
if (a.label !== undefined && typeof a.label !== "string") {
|
|
248
|
+
checks.push({
|
|
249
|
+
name: `protected_surface.assets[${i}].label`,
|
|
250
|
+
passed: false,
|
|
251
|
+
message: "protected_surface.assets[].label must be a string.",
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
if (a.reason !== undefined && typeof a.reason !== "string") {
|
|
255
|
+
checks.push({
|
|
256
|
+
name: `protected_surface.assets[${i}].reason`,
|
|
257
|
+
passed: false,
|
|
258
|
+
message: "protected_surface.assets[].reason must be a string.",
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
validateOpArray("forbidden_operations", ps.forbidden_operations, checks, /* withSeverity */ true);
|
|
265
|
+
validateOpArray("escalation_required", ps.escalation_required, checks, /* withSeverity */ false);
|
|
266
|
+
for (const key of Object.keys(ps)) {
|
|
267
|
+
if (!["assets", "forbidden_operations", "escalation_required"].includes(key)) {
|
|
268
|
+
checks.push({
|
|
269
|
+
name: `protected_surface.${key}`,
|
|
270
|
+
passed: false,
|
|
271
|
+
message: `protected_surface.${key} is not a recognized key (allowed: assets, forbidden_operations, escalation_required).`,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
136
276
|
/**
|
|
137
277
|
* Validate a protection card against ADR-037 canonical form.
|
|
138
278
|
*
|
|
139
279
|
* Required: card_version, agent_id, mode (off|observe|nudge|enforce).
|
|
140
280
|
* Optional: thresholds (warn ≤ quarantine ≤ block, all in [0,1]),
|
|
141
281
|
* screen_surfaces (object of bools with the four named keys),
|
|
142
|
-
* trusted_sources (object of typed buckets, per-bucket deny-lists)
|
|
282
|
+
* trusted_sources (object of typed buckets, per-bucket deny-lists),
|
|
283
|
+
* protected_surface (org-declared asset policy — MNE-830/833).
|
|
143
284
|
*/
|
|
144
285
|
export function validateProtectionCard(card) {
|
|
145
286
|
const checks = [];
|
|
@@ -324,6 +465,10 @@ export function validateProtectionCard(card) {
|
|
|
324
465
|
}
|
|
325
466
|
}
|
|
326
467
|
}
|
|
468
|
+
// ── protected_surface (optional, MNE-830/833) ──
|
|
469
|
+
if (card.protected_surface !== undefined) {
|
|
470
|
+
validateProtectedSurface(card.protected_surface, checks);
|
|
471
|
+
}
|
|
327
472
|
return checks;
|
|
328
473
|
}
|
|
329
474
|
// ============================================================================
|
|
@@ -438,8 +583,23 @@ export async function protectionPublishCommand(file, agentName, options = {}) {
|
|
|
438
583
|
console.log();
|
|
439
584
|
}
|
|
440
585
|
catch (error) {
|
|
441
|
-
|
|
442
|
-
|
|
586
|
+
if (error instanceof MnemomApiError) {
|
|
587
|
+
if (error.effectiveStatus === 404) {
|
|
588
|
+
console.log("\n" +
|
|
589
|
+
fmt.error("Agent found but not writable: you can see this agent locally, but cannot publish to it in its current organization context.") +
|
|
590
|
+
"\n");
|
|
591
|
+
}
|
|
592
|
+
else if (error.effectiveStatus === 401) {
|
|
593
|
+
console.log("\n" + fmt.error(`Authentication failed: ${error.message}`) + "\n");
|
|
594
|
+
}
|
|
595
|
+
else {
|
|
596
|
+
console.log("\n" + fmt.error(`Failed to publish protection card: ${error.message}`) + "\n");
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
else {
|
|
600
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
601
|
+
console.log("\n" + fmt.error(`Failed to publish protection card: ${message}`) + "\n");
|
|
602
|
+
}
|
|
443
603
|
process.exit(1);
|
|
444
604
|
}
|
|
445
605
|
}
|
package/dist/commands/status.js
CHANGED
|
@@ -64,10 +64,13 @@ async function checkAuthStatus() {
|
|
|
64
64
|
}
|
|
65
65
|
const auth = getAuthInfo();
|
|
66
66
|
if (auth) {
|
|
67
|
+
// OAuth tokens carry no identity; fall back to the granted scope (or a
|
|
68
|
+
// generic message) rather than printing "Logged in as undefined".
|
|
69
|
+
const who = auth.email ?? (auth.scope ? `scope ${auth.scope}` : "OAuth session");
|
|
67
70
|
return {
|
|
68
71
|
name: "Authentication",
|
|
69
72
|
status: "ok",
|
|
70
|
-
message: `Logged in as ${
|
|
73
|
+
message: `Logged in as ${who}`,
|
|
71
74
|
};
|
|
72
75
|
}
|
|
73
76
|
// API key auth (no email available)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `mnemom try-me <token>` — the universal, deterministic Dojo-onboarding runner
|
|
3
|
+
* (MNE-934, epic MNE-931).
|
|
4
|
+
*
|
|
5
|
+
* Executes the shipped v5.0 briefing manifest's flow so an arbitrary agent/human
|
|
6
|
+
* on a fresh machine doesn't have to improvise it: resolve → name → be born →
|
|
7
|
+
* hand off (human claims in the browser) → set alignment → open a one-time grant
|
|
8
|
+
* (human approves in the browser) → set protection → hand off to the Dojo.
|
|
9
|
+
*
|
|
10
|
+
* DEMO-SAFE BY CONSTRUCTION:
|
|
11
|
+
* - Additive: a NEW verb; it modifies no existing command and touches no
|
|
12
|
+
* backend/website. It consumes Alex's shipped endpoints READ-ONLY (resolve,
|
|
13
|
+
* gateway birth, public claim poll) + the canonical card-write client.
|
|
14
|
+
* - The two privileged human acts stay in the human's browser: the claim
|
|
15
|
+
* sign-in (step 4) and the one-time protection grant (step 6). The command
|
|
16
|
+
* only orchestrates + polls — it never forges the human's session.
|
|
17
|
+
*
|
|
18
|
+
* AUTH MODEL (see the runner report / MNE-934): the canonical card-write PUTs
|
|
19
|
+
* (`/v1/{alignment,protection}/agent/:id`) authorize on the human's ORG
|
|
20
|
+
* MEMBERSHIP (ADR-062) and accept the human's `mnemom login` JWT or
|
|
21
|
+
* `MNEMOM_API_KEY` — NOT the birth token. So before the card writes the runner
|
|
22
|
+
* ensures a CLI session (offering `mnemom login`, one-click after the claim
|
|
23
|
+
* sign-in). That session IS the human's own — it is the headless twin of the
|
|
24
|
+
* host-connector authorization in the MCP flow, not a forged credential.
|
|
25
|
+
*/
|
|
26
|
+
import { type BriefingManifest } from "../lib/try-me.js";
|
|
27
|
+
export interface TryMeOptions {
|
|
28
|
+
/** Print the resolved plan and exit — no birth, claim, card writes, or sim. */
|
|
29
|
+
dryRun?: boolean;
|
|
30
|
+
/** Machine-readable output (implies non-interactive). */
|
|
31
|
+
json?: boolean;
|
|
32
|
+
/** Non-interactive: auto-pick defaults and auto-open URLs (no prompts). */
|
|
33
|
+
yes?: boolean;
|
|
34
|
+
/** Pre-choose the agent's name (skips the name prompt). */
|
|
35
|
+
name?: string;
|
|
36
|
+
/** Resume from an already-born agent (skip birth) — e.g. a manual/legacy birth. */
|
|
37
|
+
agent?: string;
|
|
38
|
+
/** Override the API base used to resolve the token (default: env). */
|
|
39
|
+
api?: string;
|
|
40
|
+
/** Commander `--no-open` sets this false → never auto-open URLs, just print. */
|
|
41
|
+
open?: boolean;
|
|
42
|
+
/** Seconds to keep polling the claim / grant before giving up (default 600). */
|
|
43
|
+
pollTimeout?: number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Entry point for `mnemom try-me <token>`. Throws on fatal misconfiguration
|
|
47
|
+
* (caught by the index.ts wrapper, which prints + exits non-zero); soft failures
|
|
48
|
+
* are surfaced as human guidance.
|
|
49
|
+
*/
|
|
50
|
+
export declare function tryMeCommand(token: string, options?: TryMeOptions): Promise<void>;
|
|
51
|
+
/** Render the resolved plan as human-readable text (no side effects). */
|
|
52
|
+
export declare function renderPlan(manifest: BriefingManifest): string;
|