@modelprofile.com/authswitch 5.1.0 → 5.2.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_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.operations.d.ts +6 -14
- package/dist_ts/classes.operations.js +7 -17
- package/dist_ts/mutation.d.ts +23 -0
- package/dist_ts/mutation.js +26 -0
- package/package.json +13 -2
- package/readme.md +15 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.operations.ts +7 -22
- package/ts/mutation.ts +32 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: "@modelprofile.com/authswitch",
|
|
6
|
-
version: "5.
|
|
6
|
+
version: "5.2.0",
|
|
7
7
|
description: "Manage Codex, OpenCode and Claude Code accounts with guided switching and live usage status"
|
|
8
8
|
};
|
|
9
9
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSw4QkFBOEI7SUFDcEMsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLDZGQUE2RjtDQUMzRyxDQUFBIn0=
|
|
@@ -1,14 +1,10 @@
|
|
|
1
|
+
import { type TAuthSwitchMutation } from './mutation.js';
|
|
1
2
|
import type { IAuthHarness, IHarnessOutcome } from './interfaces.harness.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
keepActive: boolean;
|
|
8
|
-
} | {
|
|
9
|
-
action: 'switch' | 'remove';
|
|
10
|
-
accountId: string;
|
|
11
|
-
});
|
|
3
|
+
/**
|
|
4
|
+
* The mutation command and its rules live in a leaf module so that a browser host can import them
|
|
5
|
+
* through `@modelprofile.com/authswitch/mutation`; they stay exported here for every Node caller.
|
|
6
|
+
*/
|
|
7
|
+
export { assertAuthSwitchMutation, authSwitchMutationReplacesLogin, type TAuthSwitchMutation } from './mutation.js';
|
|
12
8
|
export interface IAuthSwitchCoordinationRequest {
|
|
13
9
|
protocolVersion: 1;
|
|
14
10
|
/** Identifies credential locations; a host must refuse a different environment. */
|
|
@@ -37,10 +33,6 @@ export type TAuthSwitchConfirmation = (message: string) => Promise<boolean>;
|
|
|
37
33
|
*/
|
|
38
34
|
export type TAuthSwitchLocalPreparation = () => Promise<void>;
|
|
39
35
|
export declare const authSwitchEnvironmentId: (env?: NodeJS.ProcessEnv) => string;
|
|
40
|
-
/** Validate the secret-free command boundary, including callers that do not use TypeScript. */
|
|
41
|
-
export declare const assertAuthSwitchMutation: (value: unknown) => asserts value is TAuthSwitchMutation;
|
|
42
|
-
/** Whether a mutation replaces the harness's active login, and therefore concerns running instances. */
|
|
43
|
-
export declare const authSwitchMutationReplacesLogin: (mutationArg: TAuthSwitchMutation) => boolean;
|
|
44
36
|
export declare const runAuthSwitchMutation: (harness: IAuthHarness, mutation: TAuthSwitchMutation) => Promise<IHarnessOutcome>;
|
|
45
37
|
/** One mutation path for command, guide and TUI. Supervisors own their complete restart transaction. */
|
|
46
38
|
export declare class AuthSwitchOperations {
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import * as plugins from './plugins.js';
|
|
2
2
|
import { authSwitchHome } from './classes.credentialstore.js';
|
|
3
|
+
import { assertAuthSwitchMutation } from './mutation.js';
|
|
4
|
+
/**
|
|
5
|
+
* The mutation command and its rules live in a leaf module so that a browser host can import them
|
|
6
|
+
* through `@modelprofile.com/authswitch/mutation`; they stay exported here for every Node caller.
|
|
7
|
+
*/
|
|
8
|
+
export { assertAuthSwitchMutation, authSwitchMutationReplacesLogin } from './mutation.js';
|
|
3
9
|
export const authSwitchEnvironmentId = (env = process.env) => plugins.crypto.createHash('sha256').update(JSON.stringify({
|
|
4
10
|
home: plugins.os.homedir(),
|
|
5
11
|
stash: plugins.path.resolve(authSwitchHome(env)),
|
|
@@ -8,22 +14,6 @@ export const authSwitchEnvironmentId = (env = process.env) => plugins.crypto.cre
|
|
|
8
14
|
claude: plugins.path.resolve(env.CLAUDE_CONFIG_DIR || plugins.path.join(plugins.os.homedir(), '.claude')),
|
|
9
15
|
openCodeOverride: env.OPENCODE_AUTH_CONTENT !== undefined,
|
|
10
16
|
})).digest('hex');
|
|
11
|
-
/** Validate the secret-free command boundary, including callers that do not use TypeScript. */
|
|
12
|
-
export const assertAuthSwitchMutation = (value) => {
|
|
13
|
-
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
14
|
-
throw new Error('Invalid account operation.');
|
|
15
|
-
const item = value;
|
|
16
|
-
const keys = item.action === 'save' ? ['harnessId', 'action', 'accountId', 'keepActive'] : ['harnessId', 'action', 'accountId'];
|
|
17
|
-
if (Object.keys(item).some(key => !keys.includes(key))
|
|
18
|
-
|| typeof item.harnessId !== 'string' || !/^[a-z][a-z0-9-]{0,63}$/.test(item.harnessId)
|
|
19
|
-
|| !['save', 'switch', 'remove'].includes(String(item.action))
|
|
20
|
-
|| (item.accountId !== undefined && (typeof item.accountId !== 'string' || !item.accountId.length || item.accountId.length > 512))
|
|
21
|
-
|| (item.action === 'save' ? typeof item.keepActive !== 'boolean' : typeof item.accountId !== 'string')) {
|
|
22
|
-
throw new Error('Invalid account operation.');
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
/** Whether a mutation replaces the harness's active login, and therefore concerns running instances. */
|
|
26
|
-
export const authSwitchMutationReplacesLogin = (mutationArg) => mutationArg.action === 'switch' || (mutationArg.action === 'save' && !mutationArg.keepActive);
|
|
27
17
|
export const runAuthSwitchMutation = async (harness, mutation) => {
|
|
28
18
|
assertAuthSwitchMutation(mutation);
|
|
29
19
|
if (harness.id !== mutation.harnessId)
|
|
@@ -99,4 +89,4 @@ export class AglAuthSwitchCoordinator {
|
|
|
99
89
|
throw new Error('AGL returned an unsupported coordination response. Check account status before retrying.');
|
|
100
90
|
}
|
|
101
91
|
}
|
|
102
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
92
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5vcGVyYXRpb25zLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvY2xhc3Nlcy5vcGVyYXRpb25zLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBQ3hDLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSw4QkFBOEIsQ0FBQztBQUM5RCxPQUFPLEVBQUUsd0JBQXdCLEVBQTRCLE1BQU0sZUFBZSxDQUFDO0FBR25GOzs7R0FHRztBQUNILE9BQU8sRUFBRSx3QkFBd0IsRUFBRSwrQkFBK0IsRUFBNEIsTUFBTSxlQUFlLENBQUM7QUF5QnBILE1BQU0sQ0FBQyxNQUFNLHVCQUF1QixHQUFHLENBQUMsTUFBeUIsT0FBTyxDQUFDLEdBQUcsRUFBVSxFQUFFLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUM7SUFDakosSUFBSSxFQUFFLE9BQU8sQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFO0lBQzFCLEtBQUssRUFBRSxPQUFPLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxjQUFjLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDaEQsS0FBSyxFQUFFLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxVQUFVLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxPQUFPLEVBQUUsRUFBRSxRQUFRLENBQUMsQ0FBQztJQUNoRyxJQUFJLEVBQUUsT0FBTyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLGFBQWEsSUFBSSxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxFQUFFLGNBQWMsQ0FBQyxDQUFDO0lBQ3hHLE1BQU0sRUFBRSxPQUFPLENBQUMs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
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The account mutation command and the rules that read it, with no dependency of any kind.
|
|
3
|
+
*
|
|
4
|
+
* A host decides what a mutation means to the instances it runs long before it can touch a
|
|
5
|
+
* credential, and a browser is such a host. This module therefore imports nothing -- no Node
|
|
6
|
+
* built-in, no package, not even another module of this package -- so that it can be bundled
|
|
7
|
+
* for a browser through the `@modelprofile.com/authswitch/mutation` entry point. Everything
|
|
8
|
+
* that reads a credential stays behind the main entry point, which is Node-only.
|
|
9
|
+
*/
|
|
10
|
+
export type TAuthSwitchMutation = {
|
|
11
|
+
harnessId: string;
|
|
12
|
+
} & ({
|
|
13
|
+
action: 'save';
|
|
14
|
+
accountId?: string;
|
|
15
|
+
keepActive: boolean;
|
|
16
|
+
} | {
|
|
17
|
+
action: 'switch' | 'remove';
|
|
18
|
+
accountId: string;
|
|
19
|
+
});
|
|
20
|
+
/** Validate the secret-free command boundary, including callers that do not use TypeScript. */
|
|
21
|
+
export declare const assertAuthSwitchMutation: (value: unknown) => asserts value is TAuthSwitchMutation;
|
|
22
|
+
/** Whether a mutation replaces the harness's active login, and therefore concerns running instances. */
|
|
23
|
+
export declare const authSwitchMutationReplacesLogin: (mutationArg: TAuthSwitchMutation) => boolean;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The account mutation command and the rules that read it, with no dependency of any kind.
|
|
3
|
+
*
|
|
4
|
+
* A host decides what a mutation means to the instances it runs long before it can touch a
|
|
5
|
+
* credential, and a browser is such a host. This module therefore imports nothing -- no Node
|
|
6
|
+
* built-in, no package, not even another module of this package -- so that it can be bundled
|
|
7
|
+
* for a browser through the `@modelprofile.com/authswitch/mutation` entry point. Everything
|
|
8
|
+
* that reads a credential stays behind the main entry point, which is Node-only.
|
|
9
|
+
*/
|
|
10
|
+
/** Validate the secret-free command boundary, including callers that do not use TypeScript. */
|
|
11
|
+
export const assertAuthSwitchMutation = (value) => {
|
|
12
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
13
|
+
throw new Error('Invalid account operation.');
|
|
14
|
+
const item = value;
|
|
15
|
+
const keys = item.action === 'save' ? ['harnessId', 'action', 'accountId', 'keepActive'] : ['harnessId', 'action', 'accountId'];
|
|
16
|
+
if (Object.keys(item).some(key => !keys.includes(key))
|
|
17
|
+
|| typeof item.harnessId !== 'string' || !/^[a-z][a-z0-9-]{0,63}$/.test(item.harnessId)
|
|
18
|
+
|| !['save', 'switch', 'remove'].includes(String(item.action))
|
|
19
|
+
|| (item.accountId !== undefined && (typeof item.accountId !== 'string' || !item.accountId.length || item.accountId.length > 512))
|
|
20
|
+
|| (item.action === 'save' ? typeof item.keepActive !== 'boolean' : typeof item.accountId !== 'string')) {
|
|
21
|
+
throw new Error('Invalid account operation.');
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
/** Whether a mutation replaces the harness's active login, and therefore concerns running instances. */
|
|
25
|
+
export const authSwitchMutationReplacesLogin = (mutationArg) => mutationArg.action === 'switch' || (mutationArg.action === 'save' && !mutationArg.keepActive);
|
|
26
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibXV0YXRpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9tdXRhdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7R0FRRztBQU9ILCtGQUErRjtBQUMvRixNQUFNLENBQUMsTUFBTSx3QkFBd0IsR0FBNkQsQ0FBQyxLQUFLLEVBQUUsRUFBRTtJQUMxRyxJQUFJLENBQUMsS0FBSyxJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVEsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQztRQUFFLE1BQU0sSUFBSSxLQUFLLENBQUMsNEJBQTRCLENBQUMsQ0FBQztJQUMvRyxNQUFNLElBQUksR0FBRyxLQUFnQyxDQUFDO0lBQzlDLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxNQUFNLEtBQUssTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLFdBQVcsRUFBRSxRQUFRLEVBQUUsV0FBVyxFQUFFLFlBQVksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLFdBQVcsRUFBRSxRQUFRLEVBQUUsV0FBVyxDQUFDLENBQUM7SUFDaEksSUFBSSxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQztXQUNqRCxPQUFPLElBQUksQ0FBQyxTQUFTLEtBQUssUUFBUSxJQUFJLENBQUMsd0JBQXdCLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUM7V0FDcEYsQ0FBQyxDQUFDLE1BQU0sRUFBRSxRQUFRLEVBQUUsUUFBUSxDQUFDLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7V0FDM0QsQ0FBQyxJQUFJLENBQUMsU0FBUyxLQUFLLFNBQVMsSUFBSSxDQUFDLE9BQU8sSUFBSSxDQUFDLFNBQVMsS0FBSyxRQUFRLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLE1BQU0sSUFBSSxJQUFJLENBQUMsU0FBUyxDQUFDLE1BQU0sR0FBRyxHQUFHLENBQUMsQ0FBQztXQUMvSCxDQUFDLElBQUksQ0FBQyxNQUFNLEtBQUssTUFBTSxDQUFDLENBQUMsQ0FBQyxPQUFPLElBQUksQ0FBQyxVQUFVLEtBQUssU0FBUyxDQUFDLENBQUMsQ0FBQyxPQUFPLElBQUksQ0FBQyxTQUFTLEtBQUssUUFBUSxDQUFDLEVBQUUsQ0FBQztRQUMxRyxNQUFNLElBQUksS0FBSyxDQUFDLDRCQUE0QixDQUFDLENBQUM7SUFDaEQsQ0FBQztBQUNILENBQUMsQ0FBQztBQUVGLHdHQUF3RztBQUN4RyxNQUFNLENBQUMsTUFBTSwrQkFBK0IsR0FBRyxDQUFDLFdBQWdDLEVBQVcsRUFBRSxDQUMzRixXQUFXLENBQUMsTUFBTSxLQUFLLFFBQVEsSUFBSSxDQUFDLFdBQVcsQ0FBQyxNQUFNLEtBQUssTUFBTSxJQUFJLENBQUMsV0FBVyxDQUFDLFVBQVUsQ0FBQyxDQUFDIn0=
|
package/package.json
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@modelprofile.com/authswitch",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.2.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Manage Codex, OpenCode and Claude Code accounts with guided switching and live usage status",
|
|
6
6
|
"main": "dist_ts/index.js",
|
|
7
7
|
"typings": "dist_ts/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist_ts/index.d.ts",
|
|
11
|
+
"import": "./dist_ts/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./mutation": {
|
|
14
|
+
"types": "./dist_ts/mutation.d.ts",
|
|
15
|
+
"import": "./dist_ts/mutation.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
8
18
|
"type": "module",
|
|
9
19
|
"author": "Task Venture Capital GmbH",
|
|
10
20
|
"license": "MIT",
|
|
@@ -26,7 +36,8 @@
|
|
|
26
36
|
"@git.zone/tsbuild": "^4.5.0",
|
|
27
37
|
"@git.zone/tsrun": "^2.0.6",
|
|
28
38
|
"@git.zone/tstest": "^6.1.1",
|
|
29
|
-
"@types/node": "26.5.1"
|
|
39
|
+
"@types/node": "26.5.1",
|
|
40
|
+
"esbuild": "^0.28.2"
|
|
30
41
|
},
|
|
31
42
|
"dependencies": {
|
|
32
43
|
"@modelprofile.com/flexharness-models": "^7.0.1",
|
package/readme.md
CHANGED
|
@@ -193,6 +193,21 @@ concerns the instances running on it. The command line, the dashboard and a host
|
|
|
193
193
|
such as AGL all read this one rule; `remove` and a save that keeps the login
|
|
194
194
|
active leave the loaded login in place.
|
|
195
195
|
|
|
196
|
+
That rule, `assertAuthSwitchMutation` and the `TAuthSwitchMutation` command are also
|
|
197
|
+
published on their own at `@modelprofile.com/authswitch/mutation`, which is the one
|
|
198
|
+
import of this package a browser can bundle:
|
|
199
|
+
|
|
200
|
+
```ts
|
|
201
|
+
import { authSwitchMutationReplacesLogin, type TAuthSwitchMutation } from '@modelprofile.com/authswitch/mutation';
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
That module imports nothing at all -- no Node built-in, no package, no other module of
|
|
205
|
+
this one -- so a web client decides what a mutation means to the instances it shows
|
|
206
|
+
without pulling a credential store into the bundle. The package root stays Node-only:
|
|
207
|
+
it reaches the stash, and with it `node:fs`, `node:child_process` and `node:crypto`.
|
|
208
|
+
Both entry points export the same three names from the same source, so a Node caller
|
|
209
|
+
can keep importing them from the package root.
|
|
210
|
+
|
|
196
211
|
OpenCode credentials live in `${XDG_DATA_HOME:-~/.local/share}/opencode/auth.json`.
|
|
197
212
|
Several providers can be active at once. Saving or switching `openai` preserves
|
|
198
213
|
every other provider, and `stash openai` without `--keep` clears only OpenAI.
|
package/ts/00_commitinfo_data.ts
CHANGED
package/ts/classes.operations.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import * as plugins from './plugins.js';
|
|
2
2
|
import { authSwitchHome } from './classes.credentialstore.js';
|
|
3
|
+
import { assertAuthSwitchMutation, type TAuthSwitchMutation } from './mutation.js';
|
|
3
4
|
import type { IAuthHarness, IHarnessOutcome } from './interfaces.harness.js';
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
/**
|
|
7
|
+
* The mutation command and its rules live in a leaf module so that a browser host can import them
|
|
8
|
+
* through `@modelprofile.com/authswitch/mutation`; they stay exported here for every Node caller.
|
|
9
|
+
*/
|
|
10
|
+
export { assertAuthSwitchMutation, authSwitchMutationReplacesLogin, type TAuthSwitchMutation } from './mutation.js';
|
|
11
|
+
|
|
9
12
|
export interface IAuthSwitchCoordinationRequest {
|
|
10
13
|
protocolVersion: 1;
|
|
11
14
|
/** Identifies credential locations; a host must refuse a different environment. */
|
|
@@ -38,24 +41,6 @@ export const authSwitchEnvironmentId = (env: NodeJS.ProcessEnv = process.env): s
|
|
|
38
41
|
openCodeOverride: env.OPENCODE_AUTH_CONTENT !== undefined,
|
|
39
42
|
})).digest('hex');
|
|
40
43
|
|
|
41
|
-
/** Validate the secret-free command boundary, including callers that do not use TypeScript. */
|
|
42
|
-
export const assertAuthSwitchMutation: (value: unknown) => asserts value is TAuthSwitchMutation = (value) => {
|
|
43
|
-
if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('Invalid account operation.');
|
|
44
|
-
const item = value as Record<string, unknown>;
|
|
45
|
-
const keys = item.action === 'save' ? ['harnessId', 'action', 'accountId', 'keepActive'] : ['harnessId', 'action', 'accountId'];
|
|
46
|
-
if (Object.keys(item).some(key => !keys.includes(key))
|
|
47
|
-
|| typeof item.harnessId !== 'string' || !/^[a-z][a-z0-9-]{0,63}$/.test(item.harnessId)
|
|
48
|
-
|| !['save', 'switch', 'remove'].includes(String(item.action))
|
|
49
|
-
|| (item.accountId !== undefined && (typeof item.accountId !== 'string' || !item.accountId.length || item.accountId.length > 512))
|
|
50
|
-
|| (item.action === 'save' ? typeof item.keepActive !== 'boolean' : typeof item.accountId !== 'string')) {
|
|
51
|
-
throw new Error('Invalid account operation.');
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
/** Whether a mutation replaces the harness's active login, and therefore concerns running instances. */
|
|
56
|
-
export const authSwitchMutationReplacesLogin = (mutationArg: TAuthSwitchMutation): boolean =>
|
|
57
|
-
mutationArg.action === 'switch' || (mutationArg.action === 'save' && !mutationArg.keepActive);
|
|
58
|
-
|
|
59
44
|
export const runAuthSwitchMutation = async (harness: IAuthHarness, mutation: TAuthSwitchMutation): Promise<IHarnessOutcome> => {
|
|
60
45
|
assertAuthSwitchMutation(mutation);
|
|
61
46
|
if (harness.id !== mutation.harnessId) throw new Error('Account operation targets a different harness.');
|
package/ts/mutation.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The account mutation command and the rules that read it, with no dependency of any kind.
|
|
3
|
+
*
|
|
4
|
+
* A host decides what a mutation means to the instances it runs long before it can touch a
|
|
5
|
+
* credential, and a browser is such a host. This module therefore imports nothing -- no Node
|
|
6
|
+
* built-in, no package, not even another module of this package -- so that it can be bundled
|
|
7
|
+
* for a browser through the `@modelprofile.com/authswitch/mutation` entry point. Everything
|
|
8
|
+
* that reads a credential stays behind the main entry point, which is Node-only.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export type TAuthSwitchMutation = { harnessId: string } & (
|
|
12
|
+
| { action: 'save'; accountId?: string; keepActive: boolean }
|
|
13
|
+
| { action: 'switch' | 'remove'; accountId: string }
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
/** Validate the secret-free command boundary, including callers that do not use TypeScript. */
|
|
17
|
+
export const assertAuthSwitchMutation: (value: unknown) => asserts value is TAuthSwitchMutation = (value) => {
|
|
18
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('Invalid account operation.');
|
|
19
|
+
const item = value as Record<string, unknown>;
|
|
20
|
+
const keys = item.action === 'save' ? ['harnessId', 'action', 'accountId', 'keepActive'] : ['harnessId', 'action', 'accountId'];
|
|
21
|
+
if (Object.keys(item).some(key => !keys.includes(key))
|
|
22
|
+
|| typeof item.harnessId !== 'string' || !/^[a-z][a-z0-9-]{0,63}$/.test(item.harnessId)
|
|
23
|
+
|| !['save', 'switch', 'remove'].includes(String(item.action))
|
|
24
|
+
|| (item.accountId !== undefined && (typeof item.accountId !== 'string' || !item.accountId.length || item.accountId.length > 512))
|
|
25
|
+
|| (item.action === 'save' ? typeof item.keepActive !== 'boolean' : typeof item.accountId !== 'string')) {
|
|
26
|
+
throw new Error('Invalid account operation.');
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/** Whether a mutation replaces the harness's active login, and therefore concerns running instances. */
|
|
31
|
+
export const authSwitchMutationReplacesLogin = (mutationArg: TAuthSwitchMutation): boolean =>
|
|
32
|
+
mutationArg.action === 'switch' || (mutationArg.action === 'save' && !mutationArg.keepActive);
|