@veilio-inc/cli 0.5.3 → 0.6.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/README.md +45 -15
- package/dist/args.d.ts +20 -2
- package/dist/args.d.ts.map +1 -1
- package/dist/args.js +47 -2
- package/dist/args.js.map +1 -1
- package/dist/client/index.d.ts +8 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +12 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/keys.d.ts +22 -0
- package/dist/client/keys.d.ts.map +1 -0
- package/dist/client/keys.js +170 -0
- package/dist/client/keys.js.map +1 -0
- package/dist/client/maps.d.ts +107 -0
- package/dist/client/maps.d.ts.map +1 -0
- package/dist/client/maps.js +702 -0
- package/dist/client/maps.js.map +1 -0
- package/dist/client/personal-maps-key.d.ts +42 -0
- package/dist/client/personal-maps-key.d.ts.map +1 -0
- package/dist/client/personal-maps-key.js +168 -0
- package/dist/client/personal-maps-key.js.map +1 -0
- package/dist/client/projects.d.ts +23 -0
- package/dist/client/projects.d.ts.map +1 -0
- package/dist/client/projects.js +68 -0
- package/dist/client/projects.js.map +1 -0
- package/dist/client/status.d.ts +19 -0
- package/dist/client/status.d.ts.map +1 -0
- package/dist/client/status.js +62 -0
- package/dist/client/status.js.map +1 -0
- package/dist/client/team-key-bootstrap.d.ts +55 -0
- package/dist/client/team-key-bootstrap.d.ts.map +1 -0
- package/dist/client/team-key-bootstrap.js +127 -0
- package/dist/client/team-key-bootstrap.js.map +1 -0
- package/dist/client/vault-key.d.ts +5 -0
- package/dist/client/vault-key.d.ts.map +1 -0
- package/dist/client/vault-key.js +35 -0
- package/dist/client/vault-key.js.map +1 -0
- package/dist/client/wording.d.ts +59 -0
- package/dist/client/wording.d.ts.map +1 -0
- package/dist/client/wording.js +336 -0
- package/dist/client/wording.js.map +1 -0
- package/dist/cloud-commands.d.ts +30 -41
- package/dist/cloud-commands.d.ts.map +1 -1
- package/dist/cloud-commands.js +271 -461
- package/dist/cloud-commands.js.map +1 -1
- package/dist/cloud.d.ts +101 -2
- package/dist/cloud.d.ts.map +1 -1
- package/dist/cloud.js +91 -7
- package/dist/cloud.js.map +1 -1
- package/dist/commands.d.ts +1 -1
- package/dist/commands.d.ts.map +1 -1
- package/dist/commands.js +15 -0
- package/dist/commands.js.map +1 -1
- package/dist/credential.d.ts +7 -0
- package/dist/credential.d.ts.map +1 -1
- package/dist/credential.js +4 -0
- package/dist/credential.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +33 -16
- package/dist/index.js.map +1 -1
- package/dist/secret-input.d.ts +23 -0
- package/dist/secret-input.d.ts.map +1 -0
- package/dist/secret-input.js +47 -0
- package/dist/secret-input.js.map +1 -0
- package/dist/team-namespace.d.ts +37 -0
- package/dist/team-namespace.d.ts.map +1 -1
- package/dist/team-namespace.js +61 -17
- package/dist/team-namespace.js.map +1 -1
- package/package.json +3 -2
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// The team's FIRST key, minted here when the team has none (spec 052 FR-016).
|
|
2
|
+
//
|
|
3
|
+
// Somebody has to mint it, and it cannot be the server: by design it holds no
|
|
4
|
+
// team key and could not make one that meant anything. The web app does it for
|
|
5
|
+
// the first owner or admin who opens it (frontend `bootstrapTeamKey.ts`). A
|
|
6
|
+
// solo developer's team of one may never get there: with no maps the web app
|
|
7
|
+
// no longer asks for the vault, so no key was ever minted, and `team unlock`
|
|
8
|
+
// sent them to ask a teammate who does not exist (staging, 2026-10-26).
|
|
9
|
+
//
|
|
10
|
+
// So `team unlock`, which holds the vault key anyway, does what the browser
|
|
11
|
+
// does, byte for byte: a fresh 32-byte AES-GCM key, wrapped to this account's
|
|
12
|
+
// OWN public key with the same ECDH + HKDF context and the same
|
|
13
|
+
// `{v, iv, data, from}` wrap, posted as the next unused version.
|
|
14
|
+
//
|
|
15
|
+
// THE SERVER DECIDES whether it really is a bootstrap - no wraps at all, the
|
|
16
|
+
// next version, recipient = granter, an owner or admin. This client cannot
|
|
17
|
+
// check the first: `GET /key` returns only its own wraps. So it tries, and a
|
|
18
|
+
// refusal is the answer. Two members unlocking at once cannot both succeed; the
|
|
19
|
+
// loser stands down rather than mint a rival key that would strand every map
|
|
20
|
+
// sealed under the winner's.
|
|
21
|
+
import { deriveWrappingKey, exportTeamKey, fromBase64, importPublicKey, unwrapTeamKey, randomBytes, toBase64, webCryptoSubtle, } from '@veilio-inc/engine';
|
|
22
|
+
import { CloudError, getMe, getTeamKeyPending, getTeamKeyWraps, grantTeamKey } from '../cloud.js';
|
|
23
|
+
/** Wrap a team key for one recipient - the browser's `wrapTeamKeyFor`.
|
|
24
|
+
* `iv` is for the test vector only. */
|
|
25
|
+
export async function wrapTeamKeyFor(teamKeyRaw, myPrivateKey, myPublicKey, recipientPublicKey, ctx, iv = randomBytes(12)) {
|
|
26
|
+
const wrappingKey = await deriveWrappingKey(myPrivateKey, await importPublicKey(recipientPublicKey), {
|
|
27
|
+
teamId: ctx.teamId,
|
|
28
|
+
version: ctx.version,
|
|
29
|
+
granterPublicKey: myPublicKey,
|
|
30
|
+
recipientPublicKey,
|
|
31
|
+
});
|
|
32
|
+
const data = await webCryptoSubtle().encrypt({ name: 'AES-GCM', iv }, wrappingKey, teamKeyRaw);
|
|
33
|
+
return JSON.stringify({ v: 1, iv: toBase64(iv), data: toBase64(data), from: myPublicKey });
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* This account's keypair, opened with the vault key - and its public half
|
|
37
|
+
* DERIVED HERE from the private half, never taken from the server.
|
|
38
|
+
*
|
|
39
|
+
* Why (security review): the first team key is wrapped to this account's own
|
|
40
|
+
* public key. Taken as `GET /api/auth/keys` returned it, a dishonest or
|
|
41
|
+
* compromised server could hand back ITS public key and be wrapped the team
|
|
42
|
+
* key, which ends end-to-end confidentiality for every team map after. The
|
|
43
|
+
* private key comes out of a blob only the vault key opens, so the public key
|
|
44
|
+
* computed from it is the account's, whatever the server says. A server
|
|
45
|
+
* answer that differs is refused, fail closed.
|
|
46
|
+
*/
|
|
47
|
+
export async function openOwnKeypair(vaultKey, privateKeyEncrypted, servedPublicKey) {
|
|
48
|
+
const env = JSON.parse(privateKeyEncrypted);
|
|
49
|
+
if (env.v !== 1 ||
|
|
50
|
+
env.alg !== 'AES-GCM' ||
|
|
51
|
+
typeof env.iv !== 'string' ||
|
|
52
|
+
typeof env.data !== 'string') {
|
|
53
|
+
throw new Error('the stored private key is not an envelope this client knows');
|
|
54
|
+
}
|
|
55
|
+
const subtle = webCryptoSubtle();
|
|
56
|
+
const pkcs8 = await subtle.decrypt({ name: 'AES-GCM', iv: fromBase64(env.iv) }, vaultKey, fromBase64(env.data));
|
|
57
|
+
// Extractable only for this one export of the public half; the key used
|
|
58
|
+
// from here on is imported again, non-extractable.
|
|
59
|
+
const exportable = await subtle.importKey('pkcs8', pkcs8, { name: 'X25519' }, true, [
|
|
60
|
+
'deriveBits',
|
|
61
|
+
]);
|
|
62
|
+
const jwk = await subtle.exportKey('jwk', exportable);
|
|
63
|
+
if (typeof jwk.x !== 'string')
|
|
64
|
+
throw new Error('the private key did not yield a public key');
|
|
65
|
+
const derived = toBase64(fromBase64(jwk.x.replace(/-/g, '+').replace(/_/g, '/') + '='.repeat((4 - (jwk.x.length % 4)) % 4)));
|
|
66
|
+
if (derived !== servedPublicKey)
|
|
67
|
+
return { ok: false, reason: 'mismatch' };
|
|
68
|
+
const privateKey = await subtle.importKey('pkcs8', pkcs8, { name: 'X25519' }, false, [
|
|
69
|
+
'deriveBits',
|
|
70
|
+
]);
|
|
71
|
+
return { ok: true, privateKey: privateKey, publicKey: derived };
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Try to mint the team's first key. Throws only for a failure that is not an
|
|
75
|
+
* answer (the network, the server's own error), which the caller reports.
|
|
76
|
+
*/
|
|
77
|
+
export async function bootstrapTeamKey(credential, teamId, me) {
|
|
78
|
+
const user = await getMe(credential);
|
|
79
|
+
const userId = user.user?.id;
|
|
80
|
+
if (typeof userId !== 'string' || userId === '') {
|
|
81
|
+
throw new CloudError('server', 'the instance did not say which account this is');
|
|
82
|
+
}
|
|
83
|
+
// Not always 1: when the last holder of a key left, its number is leaked,
|
|
84
|
+
// and the server refuses a new key under it (spec 014).
|
|
85
|
+
const pending = await getTeamKeyPending(credential, teamId);
|
|
86
|
+
const version = typeof pending?.nextVersion === 'number' ? pending.nextVersion : 1;
|
|
87
|
+
const raw = randomBytes(32);
|
|
88
|
+
// Wrapped to ourselves; our own public key is not pinned (pinning is for
|
|
89
|
+
// teammates). A substituted key would give a wrap we cannot open, which the
|
|
90
|
+
// unwrap right after this reports at once.
|
|
91
|
+
const wrapped = await wrapTeamKeyFor(raw, me.privateKey, me.publicKey, me.publicKey, {
|
|
92
|
+
teamId,
|
|
93
|
+
version,
|
|
94
|
+
});
|
|
95
|
+
try {
|
|
96
|
+
await grantTeamKey(credential, teamId, { userId, version, wrappedKey: wrapped });
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
// 403: the team has wraps already, or this member may not start one.
|
|
100
|
+
// 409: a wrap for this version exists. Somebody got there first.
|
|
101
|
+
if (err instanceof CloudError && (err.status === 403 || err.status === 409)) {
|
|
102
|
+
return { status: 'refused', reason: err.message || null };
|
|
103
|
+
}
|
|
104
|
+
throw err;
|
|
105
|
+
}
|
|
106
|
+
// Read back and opened, and compared byte for byte with the key minted: the
|
|
107
|
+
// proof that what the team's maps will be sealed under is this key, and not
|
|
108
|
+
// one the server swapped in (security review). Anything else fails closed.
|
|
109
|
+
const { wraps } = await getTeamKeyWraps(credential, teamId);
|
|
110
|
+
const wrap = wraps.find((w) => w.version === version);
|
|
111
|
+
if (!wrap)
|
|
112
|
+
return { status: 'readback_mismatch' };
|
|
113
|
+
try {
|
|
114
|
+
const opened = await unwrapTeamKey(wrap.wrapped_key, me.privateKey, {
|
|
115
|
+
teamId,
|
|
116
|
+
version,
|
|
117
|
+
myPublicKey: me.publicKey,
|
|
118
|
+
});
|
|
119
|
+
if ((await exportTeamKey(opened)) !== toBase64(raw))
|
|
120
|
+
return { status: 'readback_mismatch' };
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return { status: 'readback_mismatch' };
|
|
124
|
+
}
|
|
125
|
+
return { status: 'created', version, raw };
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=team-key-bootstrap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"team-key-bootstrap.js","sourceRoot":"","sources":["../../src/client/team-key-bootstrap.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type CryptoKeyLike } from '@veilio-inc/engine';
|
|
2
|
+
import type { Credential } from '../credential.js';
|
|
3
|
+
import { type Outcome } from './wording.js';
|
|
4
|
+
export declare function vaultKeyFor(credential: Credential, passphrase: (() => Promise<string>) | undefined): Promise<Outcome<CryptoKeyLike>>;
|
|
5
|
+
//# sourceMappingURL=vault-key.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vault-key.d.ts","sourceRoot":"","sources":["../../src/client/vault-key.ts"],"names":[],"mappings":"AASA,OAAO,EAIL,KAAK,aAAa,EACnB,MAAM,oBAAoB,CAAA;AAE3B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,EAAiC,KAAK,OAAO,EAAE,MAAM,cAAc,CAAA;AAE1E,wBAAsB,WAAW,CAC/B,UAAU,EAAE,UAAU,EACtB,UAAU,EAAE,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,SAAS,GAC9C,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAsBjC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// The vault key, derived here from the passphrase and checked - once, for
|
|
2
|
+
// every caller: `team unlock`, `vault unlock`, and a personal map opened or
|
|
3
|
+
// sealed with the passphrase. Three copies of this had grown, and a KDF change
|
|
4
|
+
// or a new check fixed in one would have left the other two wrong (review).
|
|
5
|
+
//
|
|
6
|
+
// The server holds the salt, the KDF cost and a verifier - all it can hold.
|
|
7
|
+
// The passphrase never leaves this machine, and the key is never sent or
|
|
8
|
+
// written: it is non-extractable, used in memory and dropped.
|
|
9
|
+
import { checkVaultVerifier, deriveVaultKey, fromBase64, } from '@veilio-inc/engine';
|
|
10
|
+
import { getVault } from '../cloud.js';
|
|
11
|
+
import { refuse, refusalFromCloudError } from './wording.js';
|
|
12
|
+
export async function vaultKeyFor(credential, passphrase) {
|
|
13
|
+
let vault;
|
|
14
|
+
try {
|
|
15
|
+
vault = await getVault(credential);
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
return { ok: false, refusal: refusalFromCloudError(err, { project: credential.project }) };
|
|
19
|
+
}
|
|
20
|
+
// Said plainly rather than as a decryption failure: there is nothing to try
|
|
21
|
+
// a passphrase against.
|
|
22
|
+
if (!vault.initialized)
|
|
23
|
+
return refuse('no_vault');
|
|
24
|
+
const typed = passphrase ? await passphrase() : '';
|
|
25
|
+
if (typed === '')
|
|
26
|
+
return refuse('no_passphrase');
|
|
27
|
+
const key = await deriveVaultKey(typed, fromBase64(vault.salt), vault.kdf ? { name: 'PBKDF2-SHA256', iterations: vault.kdf.iterations } : undefined);
|
|
28
|
+
// Before anything is opened, sealed or written: a wrong passphrase is named
|
|
29
|
+
// as one, rather than surfacing later as an authentication-tag failure that
|
|
30
|
+
// reads like corruption - or as a key wrapped for good under the wrong one.
|
|
31
|
+
if (!(await checkVaultVerifier(key, vault.verifier)))
|
|
32
|
+
return refuse('wrong_passphrase');
|
|
33
|
+
return { ok: true, value: key };
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=vault-key.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vault-key.js","sourceRoot":"","sources":["../../src/client/vault-key.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,4EAA4E;AAC5E,+EAA+E;AAC/E,4EAA4E;AAC5E,EAAE;AACF,4EAA4E;AAC5E,yEAAyE;AACzE,8DAA8D;AAE9D,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,UAAU,GAEX,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAEtC,OAAO,EAAE,MAAM,EAAE,qBAAqB,EAAgB,MAAM,cAAc,CAAA;AAE1E,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,UAAsB,EACtB,UAA+C;IAE/C,IAAI,KAAK,CAAA;IACT,IAAI,CAAC;QACH,KAAK,GAAG,MAAM,QAAQ,CAAC,UAAU,CAAC,CAAA;IACpC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,qBAAqB,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,EAAE,CAAC,EAAE,CAAA;IAC5F,CAAC;IACD,4EAA4E;IAC5E,wBAAwB;IACxB,IAAI,CAAC,KAAK,CAAC,WAAW;QAAE,OAAO,MAAM,CAAC,UAAU,CAAC,CAAA;IACjD,MAAM,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,MAAM,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;IAClD,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,MAAM,CAAC,eAAe,CAAC,CAAA;IAChD,MAAM,GAAG,GAAG,MAAM,cAAc,CAC9B,KAAK,EACL,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,EACtB,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,UAAU,EAAE,KAAK,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,SAAS,CACpF,CAAA;IACD,4EAA4E;IAC5E,4EAA4E;IAC5E,4EAA4E;IAC5E,IAAI,CAAC,CAAC,MAAM,kBAAkB,CAAC,GAAG,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;QAAE,OAAO,MAAM,CAAC,kBAAkB,CAAC,CAAA;IACvF,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAA;AACjC,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export declare const REFUSAL_CODES: readonly ["not_signed_in", "which_map", "map_not_found", "map_not_in_project", "not_yours_remove", "not_yours_update", "no_local_map", "no_vault", "no_passphrase", "wrong_passphrase", "vault_locked", "no_pmk_yet", "pmk_unopenable", "keys_not_set_up", "team_key_waiting", "own_public_key_mismatch", "team_key_readback_mismatch", "team_key_not_startable", "team_key_solo_unavailable", "personal_old_seal", "personal_unopenable", "not_envelope", "team_locked", "team_key_unreadable", "team_key_mismatch", "not_in_team", "team_maps_unreadable", "team_map_unopenable", "clash", "would_drop", "pull_would_drop", "diverged", "stale", "rotation_owed", "too_large", "abuse_limit", "which_project", "project_not_found", "project_ambiguous", "project_gone", "no_projects", "destructive_off", "confirm_name", "confirm_unknown", "session_rejected", "unentitled", "suspended", "forbidden", "unreachable", "server"];
|
|
2
|
+
export type RefusalCode = (typeof REFUSAL_CODES)[number];
|
|
3
|
+
/** What a sentence may be filled with. Every field is optional; each entry
|
|
4
|
+
* uses the ones it needs. */
|
|
5
|
+
export interface WordingParams {
|
|
6
|
+
id?: string;
|
|
7
|
+
name?: string;
|
|
8
|
+
count?: number;
|
|
9
|
+
detail?: string;
|
|
10
|
+
path?: string;
|
|
11
|
+
from?: string;
|
|
12
|
+
to?: string;
|
|
13
|
+
/** A short, already-joined list - placeholders, map names, ids. */
|
|
14
|
+
list?: string;
|
|
15
|
+
project?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface Refusal {
|
|
18
|
+
code: RefusalCode;
|
|
19
|
+
/** The reason, as a sentence. */
|
|
20
|
+
message: string;
|
|
21
|
+
/** What resolves it, as a sentence that names something a person can do. */
|
|
22
|
+
resolve: string;
|
|
23
|
+
/** Extra lines for the terminal only - they may hold real identifiers, so the
|
|
24
|
+
* MCP server never renders them. */
|
|
25
|
+
details?: string[];
|
|
26
|
+
}
|
|
27
|
+
export type Outcome<T> = {
|
|
28
|
+
ok: true;
|
|
29
|
+
value: T;
|
|
30
|
+
} | {
|
|
31
|
+
ok: false;
|
|
32
|
+
refusal: Refusal;
|
|
33
|
+
};
|
|
34
|
+
type Words = (p: WordingParams) => {
|
|
35
|
+
message: string;
|
|
36
|
+
resolve: string;
|
|
37
|
+
};
|
|
38
|
+
export declare const WORDING: Record<RefusalCode, Words>;
|
|
39
|
+
/** A refusal outcome, worded from the table. */
|
|
40
|
+
export declare function refuse(code: RefusalCode, params?: WordingParams, details?: string[]): {
|
|
41
|
+
ok: false;
|
|
42
|
+
refusal: Refusal;
|
|
43
|
+
};
|
|
44
|
+
export declare function refusal(code: RefusalCode, params?: WordingParams, details?: string[]): Refusal;
|
|
45
|
+
/** How the terminal prints one. The MCP server has its own (no details). */
|
|
46
|
+
export declare function renderRefusal(r: Refusal): string;
|
|
47
|
+
/**
|
|
48
|
+
* A failed request, as a refusal with the remedy that fits it.
|
|
49
|
+
*
|
|
50
|
+
* `project` is the project the call carried. The server answers a project that
|
|
51
|
+
* is not the team's - or no longer is - with 404 "Project not found", and that
|
|
52
|
+
* must be said as such, never as "the instance reported an error", or nobody
|
|
53
|
+
* thinks to choose another (spec 052 edge case: the chosen project removed).
|
|
54
|
+
*/
|
|
55
|
+
export declare function refusalFromCloudError(err: unknown, ctx?: {
|
|
56
|
+
project?: string;
|
|
57
|
+
}): Refusal;
|
|
58
|
+
export {};
|
|
59
|
+
//# sourceMappingURL=wording.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wording.d.ts","sourceRoot":"","sources":["../../src/client/wording.ts"],"names":[],"mappings":"AAwBA,eAAO,MAAM,aAAa,s4BAmDhB,CAAA;AAEV,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAA;AAExD;8BAC8B;AAC9B,MAAM,WAAW,aAAa;IAC5B,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,mEAAmE;IACnE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,WAAW,CAAA;IACjB,iCAAiC;IACjC,OAAO,EAAE,MAAM,CAAA;IACf,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAA;IACf;yCACqC;IACrC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CACnB;AAED,MAAM,MAAM,OAAO,CAAC,CAAC,IAAI;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAA;AAajF,KAAK,KAAK,GAAG,CAAC,CAAC,EAAE,aAAa,KAAK;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAIvE,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,WAAW,EAAE,KAAK,CAyO9C,CAAA;AAED,gDAAgD;AAChD,wBAAgB,MAAM,CACpB,IAAI,EAAE,WAAW,EACjB,MAAM,GAAE,aAAkB,EAC1B,OAAO,CAAC,EAAE,MAAM,EAAE,GACjB;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAEjC;AAED,wBAAgB,OAAO,CACrB,IAAI,EAAE,WAAW,EACjB,MAAM,GAAE,aAAkB,EAC1B,OAAO,CAAC,EAAE,MAAM,EAAE,GACjB,OAAO,CAGT;AAED,4EAA4E;AAC5E,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CAGhD;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAwB3F"}
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
// Every refusal the Cloud commands and the MCP map tools can give, in one table
|
|
2
|
+
// (spec 052 FR-019).
|
|
3
|
+
//
|
|
4
|
+
// WHY ONE TABLE
|
|
5
|
+
//
|
|
6
|
+
// The CLI and the MCP server do the same things to the same maps, and a person
|
|
7
|
+
// who meets a refusal in an agent's reply will often go and try the command
|
|
8
|
+
// themselves. If the two said different things for the same reason, the second
|
|
9
|
+
// reads as a second problem. So both renderers take their words from here, and
|
|
10
|
+
// a test checks the table as a table: one entry per code, each naming its
|
|
11
|
+
// reason AND what resolves it. "Not permitted" alone leaves somebody exactly
|
|
12
|
+
// where they were (Constitution V).
|
|
13
|
+
//
|
|
14
|
+
// The words never carry the `veilio:` prefix. That belongs to the renderer -
|
|
15
|
+
// the terminal prints `veilio: ...` and the MCP server `Veilio: ...` - so the
|
|
16
|
+
// sentence itself is the same everywhere.
|
|
17
|
+
//
|
|
18
|
+
// Real identifiers never go in `message` or `resolve`. A clash names the
|
|
19
|
+
// placeholders; the names behind them go in `details`, which only the terminal
|
|
20
|
+
// prints, because the MCP server's reader is the model whose context the names
|
|
21
|
+
// must stay out of (spec 052, founder decision 2026-10-25).
|
|
22
|
+
import { CloudError } from '../cloud.js';
|
|
23
|
+
export const REFUSAL_CODES = [
|
|
24
|
+
'not_signed_in',
|
|
25
|
+
'which_map',
|
|
26
|
+
'map_not_found',
|
|
27
|
+
'map_not_in_project',
|
|
28
|
+
'not_yours_remove',
|
|
29
|
+
'not_yours_update',
|
|
30
|
+
'no_local_map',
|
|
31
|
+
'no_vault',
|
|
32
|
+
'no_passphrase',
|
|
33
|
+
'wrong_passphrase',
|
|
34
|
+
'vault_locked',
|
|
35
|
+
'no_pmk_yet',
|
|
36
|
+
'pmk_unopenable',
|
|
37
|
+
'keys_not_set_up',
|
|
38
|
+
'team_key_waiting',
|
|
39
|
+
'own_public_key_mismatch',
|
|
40
|
+
'team_key_readback_mismatch',
|
|
41
|
+
'team_key_not_startable',
|
|
42
|
+
'team_key_solo_unavailable',
|
|
43
|
+
'personal_old_seal',
|
|
44
|
+
'personal_unopenable',
|
|
45
|
+
'not_envelope',
|
|
46
|
+
'team_locked',
|
|
47
|
+
'team_key_unreadable',
|
|
48
|
+
'team_key_mismatch',
|
|
49
|
+
'not_in_team',
|
|
50
|
+
'team_maps_unreadable',
|
|
51
|
+
'team_map_unopenable',
|
|
52
|
+
'clash',
|
|
53
|
+
'would_drop',
|
|
54
|
+
'pull_would_drop',
|
|
55
|
+
'diverged',
|
|
56
|
+
'stale',
|
|
57
|
+
'rotation_owed',
|
|
58
|
+
'too_large',
|
|
59
|
+
'abuse_limit',
|
|
60
|
+
'which_project',
|
|
61
|
+
'project_not_found',
|
|
62
|
+
'project_ambiguous',
|
|
63
|
+
'project_gone',
|
|
64
|
+
'no_projects',
|
|
65
|
+
'destructive_off',
|
|
66
|
+
'confirm_name',
|
|
67
|
+
'confirm_unknown',
|
|
68
|
+
'session_rejected',
|
|
69
|
+
'unentitled',
|
|
70
|
+
'suspended',
|
|
71
|
+
'forbidden',
|
|
72
|
+
'unreachable',
|
|
73
|
+
'server',
|
|
74
|
+
];
|
|
75
|
+
/** A trailing full stop, unless the text already ends a sentence. Server
|
|
76
|
+
* messages arrive both ways. */
|
|
77
|
+
function sentence(text) {
|
|
78
|
+
const t = (text ?? '').trim();
|
|
79
|
+
return /[.!?…]$/.test(t) ? t : `${t}.`;
|
|
80
|
+
}
|
|
81
|
+
function plural(n, one, many) {
|
|
82
|
+
return n === 1 ? one : many;
|
|
83
|
+
}
|
|
84
|
+
const label = (p) => (p.name ? `"${p.name}" (${p.id})` : `${p.id}`);
|
|
85
|
+
export const WORDING = {
|
|
86
|
+
not_signed_in: () => ({
|
|
87
|
+
message: 'not signed in.',
|
|
88
|
+
resolve: 'Run `veilio login` first.',
|
|
89
|
+
}),
|
|
90
|
+
which_map: () => ({
|
|
91
|
+
message: 'which map? No id was given, so nothing was done.',
|
|
92
|
+
resolve: 'Run `veilio maps list` for the ids.',
|
|
93
|
+
}),
|
|
94
|
+
map_not_found: (p) => ({
|
|
95
|
+
message: `there is no map ${p.id} on this account.`,
|
|
96
|
+
resolve: 'Run `veilio maps list` for the ids.',
|
|
97
|
+
}),
|
|
98
|
+
map_not_in_project: (p) => ({
|
|
99
|
+
message: `No map ${p.id} in project ${p.project} (the project in use).`,
|
|
100
|
+
resolve: '`veilio projects use <other>` to work in another one.',
|
|
101
|
+
}),
|
|
102
|
+
not_yours_remove: (p) => ({
|
|
103
|
+
message: `${label(p)} belongs to someone else on your team, so it is theirs to remove.`,
|
|
104
|
+
resolve: 'Ask whoever published it to remove it.',
|
|
105
|
+
}),
|
|
106
|
+
not_yours_update: (p) => ({
|
|
107
|
+
message: `${label(p)} belongs to someone else on your team, so only they can update it. Nothing was written.`,
|
|
108
|
+
resolve: 'Ask whoever published it to update it, or publish your own with `veilio maps push --team`.',
|
|
109
|
+
}),
|
|
110
|
+
no_local_map: (p) => ({
|
|
111
|
+
message: `there is no local map at ${p.path}, so there is nothing to send.`,
|
|
112
|
+
resolve: 'Run `veilio scrub` on the project first, or pass --map with the map to use.',
|
|
113
|
+
}),
|
|
114
|
+
no_vault: () => ({
|
|
115
|
+
message: 'this account has no vault, so there is no key to open or seal its personal maps.',
|
|
116
|
+
resolve: 'Create one in the web app first — it is the passphrase your maps are encrypted under.',
|
|
117
|
+
}),
|
|
118
|
+
no_passphrase: () => ({
|
|
119
|
+
message: 'no passphrase given, so nothing was changed.',
|
|
120
|
+
resolve: 'Run the command again and type the vault passphrase you use in the web app.',
|
|
121
|
+
}),
|
|
122
|
+
wrong_passphrase: () => ({
|
|
123
|
+
message: 'that vault passphrase is not right. Nothing was written or uploaded.',
|
|
124
|
+
resolve: 'Run the command again with the passphrase you use in the web app.',
|
|
125
|
+
}),
|
|
126
|
+
vault_locked: () => ({
|
|
127
|
+
message: 'the personal-maps key is not unlocked on this machine, so personal maps cannot be opened or sealed here. Nothing was changed.',
|
|
128
|
+
resolve: 'Run `veilio vault unlock` in a terminal first.',
|
|
129
|
+
}),
|
|
130
|
+
no_pmk_yet: () => ({
|
|
131
|
+
message: 'this account has no personal-maps key yet, so a personal map cannot be sealed for it.',
|
|
132
|
+
resolve: 'Run `veilio vault unlock`, which creates it.',
|
|
133
|
+
}),
|
|
134
|
+
pmk_unopenable: () => ({
|
|
135
|
+
message: "the account's personal-maps key could not be opened with this vault key, so it looks damaged. Nothing was changed.",
|
|
136
|
+
resolve: 'Contact Veilio support with the account email; do not create a new vault.',
|
|
137
|
+
}),
|
|
138
|
+
keys_not_set_up: () => ({
|
|
139
|
+
message: "this account's keys are not set up yet (it has no keypair), so nothing was changed.",
|
|
140
|
+
resolve: 'Open the web app and unlock your vault once to set up your keys, then run the command again.',
|
|
141
|
+
}),
|
|
142
|
+
own_public_key_mismatch: () => ({
|
|
143
|
+
message: 'the public key Cloud returned for this account does not belong to its private key, so no team key was made, wrapped or unlocked.',
|
|
144
|
+
resolve: 'Contact Veilio support, and do not grant or unlock team keys for this account until it is explained.',
|
|
145
|
+
}),
|
|
146
|
+
team_key_readback_mismatch: () => ({
|
|
147
|
+
message: 'the team key Cloud handed back (read back after it was set up) is not the one made here, so nothing was unlocked and nothing will be sealed under it.',
|
|
148
|
+
resolve: 'Contact Veilio support, and do not publish team maps until it is explained.',
|
|
149
|
+
}),
|
|
150
|
+
team_key_waiting: () => ({
|
|
151
|
+
message: 'no one in the team has granted this account the team key yet, so nothing was unlocked.',
|
|
152
|
+
resolve: 'Ask a team member who holds it to open the web app, which performs the grant, then run `veilio team unlock` again.',
|
|
153
|
+
}),
|
|
154
|
+
team_key_not_startable: (p) => ({
|
|
155
|
+
message: `the team has no key yet, and this account may not set one up: ${sentence(p.detail)}`,
|
|
156
|
+
resolve: 'Ask the team owner or an admin to open Veilio once, then run `veilio team unlock` again.',
|
|
157
|
+
}),
|
|
158
|
+
team_key_solo_unavailable: (p) => ({
|
|
159
|
+
message: `this account is the team's only member, and its key could not be set up from here (${p.detail}). Nothing was unlocked.`,
|
|
160
|
+
resolve: 'Open the team page in the web app, which sets the key up, then run `veilio team unlock` again.',
|
|
161
|
+
}),
|
|
162
|
+
personal_old_seal: (p) => ({
|
|
163
|
+
message: `${label(p)} is still sealed with the vault key, which this machine does not hold. Nothing was written.`,
|
|
164
|
+
resolve: 'Run `veilio vault unlock`, which re-seals it under the personal-maps key.',
|
|
165
|
+
}),
|
|
166
|
+
personal_unopenable: (p) => ({
|
|
167
|
+
message: `${label(p)} could not be opened — ${sentence(p.detail)} Nothing was written.`,
|
|
168
|
+
resolve: 'Check whether it opens in the web app; if it does not, it is damaged and `veilio maps rm` removes it.',
|
|
169
|
+
}),
|
|
170
|
+
not_envelope: () => ({
|
|
171
|
+
message: 'that map is not an encrypted envelope, so it was not written.',
|
|
172
|
+
resolve: 'Open it in the web app and save it again, which seals it.',
|
|
173
|
+
}),
|
|
174
|
+
team_locked: () => ({
|
|
175
|
+
message: 'no team key is unlocked on this machine, so team maps cannot be opened or sealed here. Nothing was written or uploaded.',
|
|
176
|
+
resolve: 'Run `veilio team unlock` first.',
|
|
177
|
+
}),
|
|
178
|
+
team_key_unreadable: (p) => ({
|
|
179
|
+
message: `the unlocked team key could not be read (${p.detail}). Nothing was written or uploaded.`,
|
|
180
|
+
resolve: 'Run `veilio team unlock` again.',
|
|
181
|
+
}),
|
|
182
|
+
team_key_mismatch: () => ({
|
|
183
|
+
message: 'the team key on this machine does not match this team. Nothing was written or uploaded.',
|
|
184
|
+
resolve: 'Run `veilio team unlock` again.',
|
|
185
|
+
}),
|
|
186
|
+
not_in_team: () => ({
|
|
187
|
+
message: 'this account is not in a team, so there is no shared map to publish to. Nothing was uploaded.',
|
|
188
|
+
resolve: "Open the team page in the web app, where the account's team is set up.",
|
|
189
|
+
}),
|
|
190
|
+
team_maps_unreadable: (p) => ({
|
|
191
|
+
message: `could not read the team's maps (${p.detail}), so whether this map conflicts with the team cannot be known. Nothing was uploaded.`,
|
|
192
|
+
resolve: 'Run `veilio team unlock` again; a named map that still does not open can be removed by its publisher with `veilio maps rm <id>`.',
|
|
193
|
+
}),
|
|
194
|
+
team_map_unopenable: (p) => ({
|
|
195
|
+
message: `that team map, ${label(p)}, could not be opened with the unlocked team keys — ${sentence(p.detail)} Nothing was written.`,
|
|
196
|
+
resolve: 'If the team key was rotated since, run `veilio team unlock` again.',
|
|
197
|
+
}),
|
|
198
|
+
clash: (p) => ({
|
|
199
|
+
message: `${p.count} ${plural(p.count, 'placeholder', 'placeholders')} in this map already ` +
|
|
200
|
+
`${plural(p.count, 'means', 'mean')} something else to the team (${p.list}). Publishing would leave ` +
|
|
201
|
+
`${plural(p.count, 'it', 'them')} unrestorable for every member, so nothing was uploaded.`,
|
|
202
|
+
resolve: 'Re-run `veilio scrub --team` to number this project out of the team’s own allocation, then push the result.',
|
|
203
|
+
}),
|
|
204
|
+
would_drop: (p) => ({
|
|
205
|
+
message: `updating ${label(p)} would drop ${p.count} ${plural(p.count, 'entry', 'entries')} the Cloud map holds and this project's map does not (${p.list}). Nothing was written.`,
|
|
206
|
+
resolve: 'Pass --allow-drop (for the MCP tool, allowDrop: true) to drop them deliberately, or pull the map first with `veilio maps pull <id>`.',
|
|
207
|
+
}),
|
|
208
|
+
pull_would_drop: (p) => ({
|
|
209
|
+
message: `pulling "${p.name}" would drop ${p.count} placeholder(s) this project's map has: ${p.list}. Nothing was written.`,
|
|
210
|
+
resolve: 'Push this map first (`veilio maps push`), pass --force to replace it, or export it with `veilio map --json`.',
|
|
211
|
+
}),
|
|
212
|
+
diverged: (p) => ({
|
|
213
|
+
message: `this map changed in Cloud (${p.from} → ${p.to}) and locally since it was pulled. Neither copy has been touched.`,
|
|
214
|
+
resolve: 'Pass --force to overwrite the local copy, or export it first with `veilio map --json`.',
|
|
215
|
+
}),
|
|
216
|
+
stale: (p) => ({
|
|
217
|
+
message: `${label(p)} changed in Cloud since it was read (now ${p.to}), so this update was not written over it.`,
|
|
218
|
+
resolve: 'Pull it (`veilio maps pull <id>`), check the change, then update again.',
|
|
219
|
+
}),
|
|
220
|
+
rotation_owed: () => ({
|
|
221
|
+
message: 'a teammate left while holding the team key, so no new team content may be written until the key is changed. Nothing was uploaded.',
|
|
222
|
+
resolve: 'A member who holds the current key has to change it in the web app — it cannot be done from here. Ask one of them.',
|
|
223
|
+
}),
|
|
224
|
+
too_large: () => ({
|
|
225
|
+
message: 'this map is too large for Cloud to accept, so nothing was stored.',
|
|
226
|
+
resolve: 'Split it, or remove entries you no longer need.',
|
|
227
|
+
}),
|
|
228
|
+
abuse_limit: (p) => ({
|
|
229
|
+
message: `${sentence(p.detail)} This ceiling is the same on every paid plan, so upgrading will not raise it. Nothing was uploaded.`,
|
|
230
|
+
resolve: 'Remove maps you no longer need with `veilio maps rm <id>`.',
|
|
231
|
+
}),
|
|
232
|
+
which_project: () => ({
|
|
233
|
+
message: 'which project? None was named, so nothing was changed.',
|
|
234
|
+
resolve: 'Run `veilio projects` for their names, then `veilio projects use <name>`.',
|
|
235
|
+
}),
|
|
236
|
+
project_not_found: (p) => ({
|
|
237
|
+
message: `"${p.project}" is not a project your team works in, so nothing was changed.`,
|
|
238
|
+
resolve: 'Run `veilio projects` to see the ones it does.',
|
|
239
|
+
}),
|
|
240
|
+
project_ambiguous: (p) => ({
|
|
241
|
+
message: `${p.count} projects are called "${p.project}", so nothing was changed.`,
|
|
242
|
+
resolve: 'Choose one by its id: `veilio projects` lists them.',
|
|
243
|
+
}),
|
|
244
|
+
project_gone: (p) => ({
|
|
245
|
+
message: `the project chosen for this sign-in (${p.project}) is not one your team works in any more, so nothing was done. No other project was used instead.`,
|
|
246
|
+
resolve: 'Run `veilio projects`, then `veilio projects use <name>`.',
|
|
247
|
+
}),
|
|
248
|
+
no_projects: () => ({
|
|
249
|
+
message: "this account's team works in no project yet, so there is none to choose.",
|
|
250
|
+
resolve: 'Share a first team map with `veilio maps push --team`, which creates the default project, or make one in the web app.',
|
|
251
|
+
}),
|
|
252
|
+
destructive_off: () => ({
|
|
253
|
+
message: 'removing a map, or an update that drops entries, is turned off for this MCP server, so nothing was removed or dropped.',
|
|
254
|
+
resolve: 'The person running it can allow it by starting the server with VEILIO_MCP_DESTRUCTIVE=1, or do it in a terminal with `veilio maps rm` or `veilio maps update --allow-drop`.',
|
|
255
|
+
}),
|
|
256
|
+
confirm_name: (p) => ({
|
|
257
|
+
message: `confirmName must repeat the map's exact name ("${p.name}"), so nothing was removed.`,
|
|
258
|
+
resolve: 'Ask the person to confirm which map, then call again with its exact name.',
|
|
259
|
+
}),
|
|
260
|
+
confirm_unknown: (p) => ({
|
|
261
|
+
message: `the name of ${p.id} could not be read to check confirmName against, so nothing was removed.`,
|
|
262
|
+
resolve: 'Check the id with maps_list, then call again.',
|
|
263
|
+
}),
|
|
264
|
+
session_rejected: () => ({
|
|
265
|
+
message: 'not signed in, or the session was revoked or expired.',
|
|
266
|
+
resolve: 'Run `veilio login` again.',
|
|
267
|
+
}),
|
|
268
|
+
unentitled: (p) => ({
|
|
269
|
+
message: `this account’s plan does not include that — ${sentence(p.detail)}`,
|
|
270
|
+
resolve: 'Change the plan in the web app to use it from here.',
|
|
271
|
+
}),
|
|
272
|
+
suspended: (p) => ({
|
|
273
|
+
message: sentence(p.detail),
|
|
274
|
+
resolve: 'Export and deletion still work in the web app; nothing else will until the review ends. Contact Veilio support about the review.',
|
|
275
|
+
}),
|
|
276
|
+
forbidden: (p) => ({
|
|
277
|
+
message: sentence(p.detail),
|
|
278
|
+
resolve: 'Ask a team owner or admin for the access this needs.',
|
|
279
|
+
}),
|
|
280
|
+
unreachable: (p) => ({
|
|
281
|
+
message: sentence(p.detail),
|
|
282
|
+
resolve: 'Check the network and the instance address, then run the command again.',
|
|
283
|
+
}),
|
|
284
|
+
server: (p) => ({
|
|
285
|
+
message: `the instance reported an error — ${sentence(p.detail)}`,
|
|
286
|
+
resolve: 'Run the command again in a minute; if it keeps failing, contact the instance’s support.',
|
|
287
|
+
}),
|
|
288
|
+
};
|
|
289
|
+
/** A refusal outcome, worded from the table. */
|
|
290
|
+
export function refuse(code, params = {}, details) {
|
|
291
|
+
return { ok: false, refusal: refusal(code, params, details) };
|
|
292
|
+
}
|
|
293
|
+
export function refusal(code, params = {}, details) {
|
|
294
|
+
const words = WORDING[code](params);
|
|
295
|
+
return details && details.length > 0 ? { code, ...words, details } : { code, ...words };
|
|
296
|
+
}
|
|
297
|
+
/** How the terminal prints one. The MCP server has its own (no details). */
|
|
298
|
+
export function renderRefusal(r) {
|
|
299
|
+
const lines = (r.details ?? []).map((d) => ` ${d}\n`).join('');
|
|
300
|
+
return `veilio: ${r.message} ${r.resolve}\n${lines}`;
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* A failed request, as a refusal with the remedy that fits it.
|
|
304
|
+
*
|
|
305
|
+
* `project` is the project the call carried. The server answers a project that
|
|
306
|
+
* is not the team's - or no longer is - with 404 "Project not found", and that
|
|
307
|
+
* must be said as such, never as "the instance reported an error", or nobody
|
|
308
|
+
* thinks to choose another (spec 052 edge case: the chosen project removed).
|
|
309
|
+
*/
|
|
310
|
+
export function refusalFromCloudError(err, ctx = {}) {
|
|
311
|
+
if (!(err instanceof CloudError)) {
|
|
312
|
+
return refusal('server', { detail: err instanceof Error ? err.message : String(err) });
|
|
313
|
+
}
|
|
314
|
+
if (err.status === 404 && /project not found/i.test(err.message)) {
|
|
315
|
+
return refusal('project_gone', { project: ctx.project ?? 'the default' });
|
|
316
|
+
}
|
|
317
|
+
switch (err.kind) {
|
|
318
|
+
case 'unauthenticated':
|
|
319
|
+
return refusal('session_rejected');
|
|
320
|
+
case 'unentitled':
|
|
321
|
+
return refusal('unentitled', { detail: err.message });
|
|
322
|
+
case 'suspended':
|
|
323
|
+
return refusal('suspended', { detail: err.message });
|
|
324
|
+
case 'forbidden':
|
|
325
|
+
// The ceiling is an abuse limit, not a plan feature. Saying "upgrade"
|
|
326
|
+
// here would take money for a change that does nothing.
|
|
327
|
+
if (/limit/i.test(err.message))
|
|
328
|
+
return refusal('abuse_limit', { detail: err.message });
|
|
329
|
+
return refusal('forbidden', { detail: err.message });
|
|
330
|
+
case 'unreachable':
|
|
331
|
+
return refusal('unreachable', { detail: err.message });
|
|
332
|
+
case 'server':
|
|
333
|
+
return refusal('server', { detail: err.message });
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
//# sourceMappingURL=wording.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wording.js","sourceRoot":"","sources":["../../src/client/wording.ts"],"names":[],"mappings":"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"}
|