@seamapi/cli 0.22.0 → 0.23.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/bin/cli.js +3 -3
- package/bin/cli.js.map +1 -1
- package/lib/auth/operations.d.ts +7 -7
- package/lib/auth/operations.js +15 -28
- package/lib/auth/operations.js.map +1 -1
- package/lib/blueprint/source-npm.js +2 -2
- package/lib/blueprint/source-npm.js.map +1 -1
- package/lib/commands/local/wizard.d.ts +7 -0
- package/lib/commands/local/wizard.js +31 -0
- package/lib/commands/local/wizard.js.map +1 -1
- package/lib/config/cli-config.d.ts +19 -0
- package/lib/config/cli-config.js +51 -0
- package/lib/config/cli-config.js.map +1 -0
- package/lib/config/config-store.d.ts +8 -5
- package/lib/config/config-store.js +15 -13
- package/lib/config/config-store.js.map +1 -1
- package/lib/config/index.d.ts +3 -2
- package/lib/config/index.js +3 -2
- package/lib/config/index.js.map +1 -1
- package/lib/config/memory-config-store.d.ts +6 -0
- package/lib/config/memory-config-store.js +6 -0
- package/lib/config/memory-config-store.js.map +1 -1
- package/lib/context.d.ts +3 -3
- package/lib/context.js +6 -16
- package/lib/context.js.map +1 -1
- package/lib/interactions/endpoint-selection.js +2 -2
- package/lib/interactions/endpoint-selection.js.map +1 -1
- package/lib/interactions/login.js +2 -2
- package/lib/interactions/login.js.map +1 -1
- package/lib/interactions/workspace-id.js +2 -2
- package/lib/interactions/workspace-id.js.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +2 -2
- package/src/bin/cli.ts +3 -3
- package/src/lib/auth/operations.ts +15 -29
- package/src/lib/blueprint/source-npm.ts +2 -3
- package/src/lib/commands/local/wizard.ts +48 -0
- package/src/lib/config/cli-config.ts +83 -0
- package/src/lib/config/config-store.ts +16 -13
- package/src/lib/config/index.ts +9 -4
- package/src/lib/config/memory-config-store.ts +9 -0
- package/src/lib/context.ts +7 -24
- package/src/lib/interactions/endpoint-selection.ts +2 -2
- package/src/lib/interactions/login.ts +2 -2
- package/src/lib/interactions/workspace-id.ts +2 -2
- package/src/lib/version.ts +1 -1
package/lib/context.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/lib/context.ts"],"names":[],"mappings":"AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/lib/context.ts"],"names":[],"mappings":"AAEA,OAAO,EAAkB,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAC7D,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,qBAAqB,GACtB,MAAM,UAAU,CAAA;AAGjB,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAEjD,MAAM,CAAC,MAAM,eAAe,GAAG,6BAA6B,CAAA;AAsB5D,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,SAAoB,SAAS,EAAE,EAAe,EAAE;IAC1E,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,GAC5D,gBAAgB,EAAE,CAAA;IAEpB,MAAM,WAAW,GAAG,kBAAkB,EAAE,CAAA;IACxC,MAAM,cAAc,GAAG,MAAM,CAAC,WAAW,EAAE,CAAA;IAC3C,MAAM,QAAQ,GAAG,YAAY,IAAI,WAAW,IAAI,cAAc,CAAA;IAE9D,MAAM,QAAQ,GAAG,eAAe,EAAE,CAAA;IAClC,2EAA2E;IAC3E,6DAA6D;IAC7D,MAAM,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,IAAI,eAAe,CAAC,CAAA;IAEhE,MAAM,cAAc,GAAG,qBAAqB,EAAE,CAAA;IAC9C,MAAM,iBAAiB,GAAG,MAAM,CAAC,YAAY,EAAE,CAAA;IAC/C,MAAM,WAAW,GAAG,eAAe,IAAI,cAAc,IAAI,iBAAiB,CAAA;IAE1E,OAAO;QACL,QAAQ,EAAE,QAAQ,IAAI,eAAe;QACrC,cAAc,EACZ,YAAY,IAAI,IAAI;YAClB,CAAC,CAAC,MAAM;YACR,CAAC,CAAC,WAAW,IAAI,IAAI;gBACnB,CAAC,CAAC,KAAK;gBACP,CAAC,CAAC,QAAQ,IAAI,IAAI;oBAChB,CAAC,CAAC,QAAQ;oBACV,CAAC,CAAC,SAAS;QACnB,KAAK,EAAE,QAAQ,IAAI,WAAW;QAC9B,WAAW,EACT,QAAQ,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,IAAI,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI;QAClE,WAAW;QACX,iBAAiB,EACf,eAAe,IAAI,IAAI;YACrB,CAAC,CAAC,MAAM;YACR,CAAC,CAAC,cAAc,IAAI,IAAI;gBACtB,CAAC,CAAC,KAAK;gBACP,CAAC,CAAC,iBAAiB,IAAI,IAAI;oBACzB,CAAC,CAAC,QAAQ;oBACV,CAAC,CAAC,IAAI;KACf,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { assertMutable, selectEndpoint } from '../auth/operations.js';
|
|
2
|
-
import {
|
|
2
|
+
import { getConfig } from '../config/index.js';
|
|
3
3
|
import { resolveAuth } from '../context.js';
|
|
4
4
|
import { getOutput } from '../output/get-output.js';
|
|
5
5
|
import { promptAutocomplete } from '../prompt.js';
|
|
6
6
|
export async function interactForEndpointSelection() {
|
|
7
|
-
const config =
|
|
7
|
+
const config = getConfig();
|
|
8
8
|
assertMutable(resolveAuth(config), 'endpoint', 'select an endpoint');
|
|
9
9
|
const endpoints = ['http://localhost:3020', 'https://connect.getseam.com'];
|
|
10
10
|
// Searchable, as selecting a device or a command is.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"endpoint-selection.js","sourceRoot":"","sources":["../../src/lib/interactions/endpoint-selection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AACtE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"endpoint-selection.js","sourceRoot":"","sources":["../../src/lib/interactions/endpoint-selection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AACtE,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAA;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAElD,MAAM,CAAC,KAAK,UAAU,4BAA4B;IAChD,MAAM,MAAM,GAAG,SAAS,EAAE,CAAA;IAC1B,aAAa,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,oBAAoB,CAAC,CAAA;IAEpE,MAAM,SAAS,GAAG,CAAC,uBAAuB,EAAE,6BAA6B,CAAC,CAAA;IAE1E,qDAAqD;IACrD,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CAAC;QACxC,OAAO,EAAE,qBAAqB;QAC9B,OAAO,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;YACpC,KAAK,EAAE,QAAQ;YACf,KAAK,EAAE,QAAQ;SAChB,CAAC,CAAC;KACJ,CAAC,CAAA;IAEF,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;IAChC,SAAS,EAAE,CAAC,IAAI,CAAC,mBAAmB,QAAQ,EAAE,CAAC,CAAA;AACjD,CAAC"}
|
|
@@ -2,14 +2,14 @@ import { isApiKey, isPersonalAccessToken } from '@seamapi/http/connect';
|
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import { assertMutable, storeToken } from '../auth/operations.js';
|
|
4
4
|
import { validateToken } from '../auth/validate-token.js';
|
|
5
|
-
import {
|
|
5
|
+
import { getConfig } from '../config/index.js';
|
|
6
6
|
import { resolveAuth } from '../context.js';
|
|
7
7
|
import { getOutput } from '../output/get-output.js';
|
|
8
8
|
import { withLoading } from '../output/with-loading.js';
|
|
9
9
|
import { promptText } from '../prompt.js';
|
|
10
10
|
import { interactForWorkspaceId } from './workspace-id.js';
|
|
11
11
|
export const interactForLogin = async () => {
|
|
12
|
-
const config =
|
|
12
|
+
const config = getConfig();
|
|
13
13
|
const output = getOutput();
|
|
14
14
|
const auth = resolveAuth(config);
|
|
15
15
|
// Refuse before prompting: nothing typed here could be stored.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/lib/interactions/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AACvE,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAC1D,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/lib/interactions/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AACvE,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAA;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAA;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAE1C,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AAE1D,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,IAAI,EAAE;IACzC,MAAM,MAAM,GAAG,SAAS,EAAE,CAAA;IAC1B,MAAM,MAAM,GAAG,SAAS,EAAE,CAAA;IAC1B,MAAM,IAAI,GAAG,WAAW,CAAC,MAAM,CAAC,CAAA;IAEhC,+DAA+D;IAC/D,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAA;IAEtC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QACxC,MAAM,CAAC,IAAI,CACT,gIAAgI,IAAI,CAAC,QAAQ,iCAAiC,CAC/K,CAAA;IACH,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,IAAI,CACT,+KAA+K,CAChL,CAAA;IACH,CAAC;IAED,MAAM,CAAC,IAAI,CACT,KAAK,CAAC,IAAI,CACR,mEAAmE,CACpE,CACF,CAAA;IAED,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC;QAC3B,OAAO,EAAE,wBAAwB;KAClC,CAAC,CAAA;IACF,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,EAAE,CAAA;IAExB,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAA;IACtC,CAAC;IAED,IAAI,qBAAqB,CAAC,KAAK,CAAC,EAAE,CAAC;QACjC,MAAM,sBAAsB,CAAC,KAAK,CAAC,CAAA;IACrC,CAAC;SAAM,IAAI,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3B,MAAM,WAAW,CAAC,uBAAuB,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAA;IACxE,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,KAAK,CACb,4DAA4D,CAC7D,CAAA;IACH,CAAC;IAED,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;IACzB,MAAM,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAA;AAC1D,CAAC,CAAA"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { SeamHttpWithoutWorkspace } from '@seamapi/http/connect';
|
|
2
2
|
import { assertMutable, selectWorkspace } from '../auth/operations.js';
|
|
3
|
-
import {
|
|
3
|
+
import { getConfig } from '../config/index.js';
|
|
4
4
|
import { resolveAuth } from '../context.js';
|
|
5
5
|
import { getSeamMultiWorkspace } from '../http/client.js';
|
|
6
6
|
import { withLoading } from '../output/with-loading.js';
|
|
7
7
|
import { promptAutocomplete } from '../prompt.js';
|
|
8
8
|
export const interactForWorkspaceId = async (personalAccessToken) => {
|
|
9
|
-
const config =
|
|
9
|
+
const config = getConfig();
|
|
10
10
|
// Refuse before prompting: nothing selected here could be stored.
|
|
11
11
|
assertMutable(resolveAuth(config), 'workspaceId', 'select a workspace');
|
|
12
12
|
const seam = personalAccessToken
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-id.js","sourceRoot":"","sources":["../../src/lib/interactions/workspace-id.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAA;AAEhE,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AACvE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"workspace-id.js","sourceRoot":"","sources":["../../src/lib/interactions/workspace-id.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAA;AAEhE,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AACvE,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAA;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAElD,MAAM,CAAC,MAAM,sBAAsB,GAAG,KAAK,EAAE,mBAA4B,EAAE,EAAE;IAC3E,MAAM,MAAM,GAAG,SAAS,EAAE,CAAA;IAE1B,kEAAkE;IAClE,aAAa,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,aAAa,EAAE,oBAAoB,CAAC,CAAA;IAEvE,MAAM,IAAI,GAAG,mBAAmB;QAC9B,CAAC,CAAC,wBAAwB,CAAC,uBAAuB,CAAC,mBAAmB,EAAE;YACpE,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,QAAQ;SACvC,CAAC;QACJ,CAAC,CAAC,MAAM,qBAAqB,EAAE,CAAA;IAEjC,MAAM,UAAU,GAAG,MAAM,WAAW,CAAC,wBAAwB,EAAE,GAAG,EAAE,CAClE,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CACvB,CAAA;IACD,yEAAyE;IACzE,wCAAwC;IACxC,MAAM,WAAW,GAAG,MAAM,kBAAkB,CAAS;QACnD,OAAO,EAAE,qBAAqB;QAC9B,OAAO,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,SAAc,EAAE,EAAE,CAAC,CAAC;YAC3C,KAAK,EAAE,SAAS,CAAC,IAAI;YACrB,KAAK,EAAE,SAAS,CAAC,YAAY;YAC7B,IAAI,EAAE,SAAS,CAAC,YAAY;SAC7B,CAAC,CAAC;KACJ,CAAC,CAAA;IAEF,eAAe,CAAC,WAAW,EAAE,MAAM,CAAC,CAAA;IACpC,OAAO,WAAW,CAAA;AACpB,CAAC,CAAA"}
|
package/lib/version.d.ts
CHANGED
package/lib/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seamapi/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "A command-line interface (CLI) for interacting with the Seam API.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
"@clack/prompts": "^1.7.0",
|
|
96
96
|
"@seamapi/blueprint": "1.5.0",
|
|
97
97
|
"@seamapi/http": "2.2.0",
|
|
98
|
-
"@seamapi/wizard": "0.
|
|
98
|
+
"@seamapi/wizard": "0.7.0",
|
|
99
99
|
"chalk": "^6.0.0",
|
|
100
100
|
"command-line-usage": "^7.0.4",
|
|
101
101
|
"configstore": "^8.0.0",
|
package/src/bin/cli.ts
CHANGED
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
findLocalCommand,
|
|
22
22
|
findLocalCommandTakingPositional,
|
|
23
23
|
} from 'lib/commands/registry.js'
|
|
24
|
-
import {
|
|
24
|
+
import { getConfig } from 'lib/config/index.js'
|
|
25
25
|
import { type CliContext, resolveAuth } from 'lib/context.js'
|
|
26
26
|
import { tokenEnvVar } from 'lib/env.js'
|
|
27
27
|
import { reportErrorAndExit } from 'lib/errors.js'
|
|
@@ -41,7 +41,7 @@ import { renderHelp } from 'lib/render/help.js'
|
|
|
41
41
|
import seamapiCliVersion from 'lib/version.js'
|
|
42
42
|
|
|
43
43
|
async function cli(args: ParsedArgs, argv: string[]) {
|
|
44
|
-
const config =
|
|
44
|
+
const config = getConfig()
|
|
45
45
|
const output = getOutput()
|
|
46
46
|
|
|
47
47
|
// Scoped to this one command, and read wherever auth resolves, so they are
|
|
@@ -158,7 +158,7 @@ async function cli(args: ParsedArgs, argv: string[]) {
|
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
const useRemoteApiDefs =
|
|
161
|
-
args['remote_api_defs'] ?? config.
|
|
161
|
+
args['remote_api_defs'] ?? config.getUseRemoteApiDefs()
|
|
162
162
|
|
|
163
163
|
const blueprint = await getApiBlueprint({
|
|
164
164
|
useRemoteDefinitions: useRemoteApiDefs ?? false,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type CliConfig, getConfig } from 'lib/config/index.js'
|
|
2
2
|
import { type AuthContext, resolveAuth } from 'lib/context.js'
|
|
3
3
|
import {
|
|
4
4
|
assertEnvVarUnset,
|
|
@@ -57,7 +57,7 @@ export const assertMutable = (
|
|
|
57
57
|
*/
|
|
58
58
|
export const login = async (
|
|
59
59
|
token: string,
|
|
60
|
-
config:
|
|
60
|
+
config: CliConfig = getConfig(),
|
|
61
61
|
validate: typeof validateToken = validateToken,
|
|
62
62
|
): Promise<void> => {
|
|
63
63
|
const auth = resolveAuth(config)
|
|
@@ -68,30 +68,27 @@ export const login = async (
|
|
|
68
68
|
|
|
69
69
|
await validate(token, auth.workspaceId ?? undefined)
|
|
70
70
|
|
|
71
|
-
config.
|
|
71
|
+
config.setToken(auth.endpoint, token)
|
|
72
72
|
// The selection belongs to whoever was logged in before.
|
|
73
|
-
config.
|
|
73
|
+
config.unsetWorkspace()
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
/** Store the token for the current endpoint, e.g., one just prompted for. */
|
|
77
77
|
export const storeToken = (
|
|
78
78
|
token: string,
|
|
79
|
-
config:
|
|
79
|
+
config: CliConfig = getConfig(),
|
|
80
80
|
): void => {
|
|
81
81
|
const auth = resolveAuth(config)
|
|
82
82
|
assertMutable(auth, 'token', 'log in')
|
|
83
|
-
config.
|
|
83
|
+
config.setToken(auth.endpoint, token)
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
/** Remove the stored token and workspace selection. */
|
|
87
|
-
export const logout = (config:
|
|
87
|
+
export const logout = (config: CliConfig = getConfig()): void => {
|
|
88
88
|
const auth = resolveAuth(config)
|
|
89
89
|
assertMutable(auth, 'token', 'log out')
|
|
90
|
-
config.
|
|
91
|
-
|
|
92
|
-
// un-namespaced token, so drop that too.
|
|
93
|
-
config.delete('pat')
|
|
94
|
-
config.delete('current_workspace_id')
|
|
90
|
+
config.unsetToken(auth.endpoint)
|
|
91
|
+
config.unsetWorkspace()
|
|
95
92
|
}
|
|
96
93
|
|
|
97
94
|
/**
|
|
@@ -101,36 +98,25 @@ export const logout = (config: ConfigStore = getConfigStore()): void => {
|
|
|
101
98
|
*/
|
|
102
99
|
export const selectEndpoint = (
|
|
103
100
|
endpoint: string,
|
|
104
|
-
config:
|
|
101
|
+
config: CliConfig = getConfig(),
|
|
105
102
|
): void => {
|
|
106
103
|
assertMutable(resolveAuth(config), 'endpoint', 'select an endpoint')
|
|
107
|
-
|
|
104
|
+
config.setEndpoint(endpoint)
|
|
108
105
|
}
|
|
109
106
|
|
|
110
107
|
/** Store the workspace requests are made against. */
|
|
111
108
|
export const selectWorkspace = (
|
|
112
109
|
workspaceId: string,
|
|
113
|
-
config:
|
|
110
|
+
config: CliConfig = getConfig(),
|
|
114
111
|
): void => {
|
|
115
112
|
assertMutable(resolveAuth(config), 'workspaceId', 'select a workspace')
|
|
116
|
-
config.
|
|
113
|
+
config.setWorkspace(workspaceId)
|
|
117
114
|
}
|
|
118
115
|
|
|
119
116
|
/** Store whether API definitions come from the endpoint instead of npm. */
|
|
120
117
|
export const setUseRemoteApiDefs = (
|
|
121
118
|
useRemoteApiDefs: boolean,
|
|
122
|
-
config:
|
|
119
|
+
config: CliConfig = getConfig(),
|
|
123
120
|
): void => {
|
|
124
|
-
config.
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Write the endpoint, dropping what belonged to the previous one: the
|
|
129
|
-
* workspace selection, and any value left under the legacy `server` key that
|
|
130
|
-
* {@link resolveAuth} would otherwise still fall back to.
|
|
131
|
-
*/
|
|
132
|
-
const storeEndpoint = (endpoint: string, config: ConfigStore): void => {
|
|
133
|
-
config.set('endpoint', endpoint)
|
|
134
|
-
config.delete('server')
|
|
135
|
-
config.delete('current_workspace_id')
|
|
121
|
+
config.setUseRemoteApiDefs(useRemoteApiDefs)
|
|
136
122
|
}
|
|
@@ -3,9 +3,9 @@ import { join } from 'node:path'
|
|
|
3
3
|
import { pathToFileURL } from 'node:url'
|
|
4
4
|
|
|
5
5
|
import type { Blueprint, TypesModuleInput } from '@seamapi/blueprint'
|
|
6
|
-
import envPaths from 'env-paths'
|
|
7
6
|
import { extract } from 'tar'
|
|
8
7
|
|
|
8
|
+
import { rootPaths } from 'lib/config/index.js'
|
|
9
9
|
import { withLoading } from 'lib/output/with-loading.js'
|
|
10
10
|
|
|
11
11
|
import {
|
|
@@ -38,8 +38,7 @@ export const getBlueprint = async (
|
|
|
38
38
|
options: GetBlueprintOptions = {},
|
|
39
39
|
): Promise<Blueprint> => {
|
|
40
40
|
const update = options.update ?? false
|
|
41
|
-
const cacheDirectory =
|
|
42
|
-
options.cacheDirectory ?? envPaths('seam', { suffix: '' }).cache
|
|
41
|
+
const cacheDirectory = options.cacheDirectory ?? rootPaths.cache
|
|
43
42
|
const cacheFile = getCacheFile(cacheDirectory)
|
|
44
43
|
const blueprintVersion = await getBlueprintVersion()
|
|
45
44
|
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
+
import { join } from 'node:path'
|
|
2
|
+
|
|
3
|
+
import { isApiKey } from '@seamapi/http/connect'
|
|
4
|
+
import type { StorageAdapter, WizardAdapter, WizardAuth } from '@seamapi/wizard'
|
|
5
|
+
import Configstore from 'configstore'
|
|
6
|
+
|
|
1
7
|
import type { Command } from 'lib/commands/registry.js'
|
|
8
|
+
import { type CliConfig, getConfig, rootPaths } from 'lib/config/index.js'
|
|
9
|
+
import { resolveAuth } from 'lib/context.js'
|
|
2
10
|
|
|
3
11
|
/**
|
|
4
12
|
* Run the Seam setup wizard.
|
|
@@ -11,6 +19,7 @@ export const runWizard = async (argv: string[]): Promise<void> => {
|
|
|
11
19
|
await wizard({
|
|
12
20
|
argv,
|
|
13
21
|
commandName: 'seam wizard',
|
|
22
|
+
adapter: createWizardAdapter(),
|
|
14
23
|
})
|
|
15
24
|
}
|
|
16
25
|
|
|
@@ -29,3 +38,42 @@ export const wizardCommand: Command = {
|
|
|
29
38
|
return { kind: 'done' }
|
|
30
39
|
},
|
|
31
40
|
}
|
|
41
|
+
|
|
42
|
+
const wizardFileName = 'wizard.json'
|
|
43
|
+
|
|
44
|
+
export const createWizardAdapter = ({
|
|
45
|
+
cliConfig = getConfig(),
|
|
46
|
+
configPath = join(rootPaths.config, wizardFileName),
|
|
47
|
+
statePath = join(rootPaths.log, wizardFileName),
|
|
48
|
+
}: {
|
|
49
|
+
cliConfig?: CliConfig
|
|
50
|
+
configPath?: string
|
|
51
|
+
statePath?: string
|
|
52
|
+
} = {}): WizardAdapter => ({
|
|
53
|
+
getAuth: async () => toWizardAuth(cliConfig),
|
|
54
|
+
config: createStorage(configPath),
|
|
55
|
+
state: createStorage(statePath),
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
// Only a workspace-scoped key may go in a project, so a personal access
|
|
59
|
+
// token is not handed over at all.
|
|
60
|
+
const toWizardAuth = (cliConfig: CliConfig): WizardAuth => {
|
|
61
|
+
const { endpoint, token, workspaceId } = resolveAuth(cliConfig)
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
endpoint,
|
|
65
|
+
apiKey: token != null && isApiKey(token) ? token : null,
|
|
66
|
+
workspaceId,
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const createStorage = (path: string): StorageAdapter => {
|
|
71
|
+
const store = new Configstore('seam-cli', undefined, { configPath: path })
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
get: async (key) => store.get(key),
|
|
75
|
+
set: async (key, value) => {
|
|
76
|
+
store.set(key, value)
|
|
77
|
+
},
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { ConfigStore } from './config-store.js'
|
|
2
|
+
|
|
3
|
+
/** Tokens are stored per endpoint, e.g. `https://connect.getseam.com.pat`. */
|
|
4
|
+
const tokenKey = (endpoint: string): `${string}.pat` => `${endpoint}.pat`
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The CLI's configuration, in the terms the CLI thinks in. Keys are named
|
|
8
|
+
* here alone: a caller says what it wants stored, not where it goes.
|
|
9
|
+
*/
|
|
10
|
+
export interface CliConfig {
|
|
11
|
+
readonly path: string
|
|
12
|
+
getEndpoint: () => string | null
|
|
13
|
+
setEndpoint: (endpoint: string) => void
|
|
14
|
+
getToken: (endpoint: string) => string | null
|
|
15
|
+
setToken: (endpoint: string, token: string) => void
|
|
16
|
+
unsetToken: (endpoint: string) => void
|
|
17
|
+
getWorkspace: () => string | null
|
|
18
|
+
setWorkspace: (workspaceId: string) => void
|
|
19
|
+
unsetWorkspace: () => void
|
|
20
|
+
getUseRemoteApiDefs: () => boolean | null
|
|
21
|
+
setUseRemoteApiDefs: (useRemoteApiDefs: boolean) => void
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const createCliConfig = (store: ConfigStore): CliConfig => ({
|
|
25
|
+
get path() {
|
|
26
|
+
return store.path
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
getEndpoint: () =>
|
|
30
|
+
// `server` is what an older CLI called the endpoint.
|
|
31
|
+
readString(store.get('endpoint')) ?? readString(store.get('server')),
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Store the endpoint, dropping what belonged to the previous one: the
|
|
35
|
+
* workspace selection, and any value left under the legacy key that
|
|
36
|
+
* {@link CliConfig.getEndpoint} would otherwise still fall back to.
|
|
37
|
+
*/
|
|
38
|
+
setEndpoint: (endpoint) => {
|
|
39
|
+
store.set('endpoint', endpoint)
|
|
40
|
+
store.delete('server')
|
|
41
|
+
store.delete('current_workspace_id')
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
getToken: (endpoint) => readString(store.get(tokenKey(endpoint))),
|
|
45
|
+
|
|
46
|
+
setToken: (endpoint, token) => {
|
|
47
|
+
store.set(tokenKey(endpoint), token)
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
unsetToken: (endpoint) => {
|
|
51
|
+
store.delete(tokenKey(endpoint))
|
|
52
|
+
// Configs written before tokens were stored per endpoint may still hold
|
|
53
|
+
// an un-namespaced one.
|
|
54
|
+
store.delete('pat')
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
getWorkspace: () => readString(store.get('current_workspace_id')),
|
|
58
|
+
|
|
59
|
+
setWorkspace: (workspaceId) => {
|
|
60
|
+
store.set('current_workspace_id', workspaceId)
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
unsetWorkspace: () => {
|
|
64
|
+
store.delete('current_workspace_id')
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
getUseRemoteApiDefs: () => {
|
|
68
|
+
const useRemoteApiDefs = store.get('use_remote_api_defs')
|
|
69
|
+
return typeof useRemoteApiDefs === 'boolean' ? useRemoteApiDefs : null
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
setUseRemoteApiDefs: (useRemoteApiDefs) => {
|
|
73
|
+
store.set('use_remote_api_defs', useRemoteApiDefs)
|
|
74
|
+
},
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
const readString = (value: unknown): string | null => {
|
|
78
|
+
if (typeof value !== 'string') return null
|
|
79
|
+
|
|
80
|
+
const trimmedValue = value.trim()
|
|
81
|
+
|
|
82
|
+
return trimmedValue === '' ? null : trimmedValue
|
|
83
|
+
}
|
|
@@ -3,12 +3,15 @@ import { join } from 'node:path'
|
|
|
3
3
|
import Configstore from 'configstore'
|
|
4
4
|
import envPaths from 'env-paths'
|
|
5
5
|
|
|
6
|
+
import { type CliConfig, createCliConfig } from './cli-config.js'
|
|
6
7
|
import { migrateConfigStore } from './migrate.js'
|
|
7
8
|
import { isStateKey, mergeConfig, splitConfig } from './values.js'
|
|
8
9
|
|
|
9
10
|
const configFileName = 'cli.json'
|
|
10
11
|
const legacyConfigStoreId = 'seam-cli'
|
|
11
|
-
|
|
12
|
+
|
|
13
|
+
/** Every directory the CLI keeps files in, and what it mounts keeps its own. */
|
|
14
|
+
export const rootPaths = envPaths('seam', { suffix: '' })
|
|
12
15
|
|
|
13
16
|
/**
|
|
14
17
|
* What a config store can do, regardless of where it keeps the values.
|
|
@@ -28,21 +31,21 @@ export interface ConfigStore {
|
|
|
28
31
|
clear: () => void
|
|
29
32
|
}
|
|
30
33
|
|
|
31
|
-
let
|
|
34
|
+
let config: CliConfig | null = null
|
|
32
35
|
|
|
33
|
-
export const
|
|
34
|
-
|
|
35
|
-
return
|
|
36
|
+
export const getConfig = (): CliConfig => {
|
|
37
|
+
config ??= createCliConfig(createConfigStore())
|
|
38
|
+
return config
|
|
36
39
|
}
|
|
37
40
|
|
|
38
|
-
/** Replace the
|
|
39
|
-
export const
|
|
40
|
-
|
|
41
|
+
/** Replace the config, e.g., with an in-memory one for a test. */
|
|
42
|
+
export const setConfig = (nextConfig: CliConfig): void => {
|
|
43
|
+
config = nextConfig
|
|
41
44
|
}
|
|
42
45
|
|
|
43
|
-
/** Drop the current
|
|
44
|
-
export const
|
|
45
|
-
|
|
46
|
+
/** Drop the current config so the next read builds the real one. */
|
|
47
|
+
export const resetConfig = (): void => {
|
|
48
|
+
config = null
|
|
46
49
|
}
|
|
47
50
|
|
|
48
51
|
const createConfigStore = (): PersistentConfigStore => {
|
|
@@ -121,11 +124,11 @@ export class PersistentConfigStore implements ConfigStore {
|
|
|
121
124
|
}
|
|
122
125
|
|
|
123
126
|
const getConfigPath = (): string => {
|
|
124
|
-
return join(
|
|
127
|
+
return join(rootPaths.config, configFileName)
|
|
125
128
|
}
|
|
126
129
|
|
|
127
130
|
const getStateConfigPath = (): string => {
|
|
128
|
-
return join(
|
|
131
|
+
return join(rootPaths.log, configFileName)
|
|
129
132
|
}
|
|
130
133
|
|
|
131
134
|
const isRecord = (value: unknown): value is Record<string, unknown> => {
|
package/src/lib/config/index.ts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
+
export { type CliConfig, createCliConfig } from './cli-config.js'
|
|
1
2
|
export {
|
|
2
3
|
type ConfigStore,
|
|
3
|
-
|
|
4
|
+
getConfig,
|
|
4
5
|
type PersistentConfigStore,
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
resetConfig,
|
|
7
|
+
rootPaths,
|
|
8
|
+
setConfig,
|
|
7
9
|
} from './config-store.js'
|
|
8
|
-
export {
|
|
10
|
+
export {
|
|
11
|
+
createMemoryConfig,
|
|
12
|
+
createMemoryConfigStore,
|
|
13
|
+
} from './memory-config-store.js'
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type CliConfig, createCliConfig } from './cli-config.js'
|
|
1
2
|
import type { ConfigStore } from './config-store.js'
|
|
2
3
|
|
|
3
4
|
/**
|
|
@@ -60,3 +61,11 @@ export class MemoryConfigStore implements ConfigStore {
|
|
|
60
61
|
export const createMemoryConfigStore = (
|
|
61
62
|
initialValues: Record<string, unknown> = {},
|
|
62
63
|
): ConfigStore => new MemoryConfigStore(initialValues)
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* A config held in memory, for tests. Values may be seeded by key, e.g., to
|
|
67
|
+
* stand for a config an older CLI wrote.
|
|
68
|
+
*/
|
|
69
|
+
export const createMemoryConfig = (
|
|
70
|
+
initialValues: Record<string, unknown> = {},
|
|
71
|
+
): CliConfig => createCliConfig(createMemoryConfigStore(initialValues))
|
package/src/lib/context.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Interactivity } from './args/parse.js'
|
|
2
2
|
import type { ApiBlueprint } from './blueprint/index.js'
|
|
3
|
-
import { type
|
|
3
|
+
import { type CliConfig, getConfig } from './config/index.js'
|
|
4
4
|
import {
|
|
5
5
|
getEndpointFromEnv,
|
|
6
6
|
getTokenFromEnv,
|
|
@@ -32,30 +32,21 @@ export interface AuthContext {
|
|
|
32
32
|
workspaceIdSource: Exclude<ValueSource, 'default'> | null
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
export const resolveAuth = (
|
|
36
|
-
config: ConfigStore = getConfigStore(),
|
|
37
|
-
): AuthContext => {
|
|
35
|
+
export const resolveAuth = (config: CliConfig = getConfig()): AuthContext => {
|
|
38
36
|
const { endpoint: flagEndpoint, workspaceId: flagWorkspaceId } =
|
|
39
37
|
getAuthOverrides()
|
|
40
38
|
|
|
41
39
|
const envEndpoint = getEndpointFromEnv()
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const storedEndpoint = config.get('endpoint') ?? config.get('server')
|
|
45
|
-
const endpoint =
|
|
46
|
-
flagEndpoint ??
|
|
47
|
-
envEndpoint ??
|
|
48
|
-
(typeof storedEndpoint === 'string' ? storedEndpoint : null)
|
|
40
|
+
const storedEndpoint = config.getEndpoint()
|
|
41
|
+
const endpoint = flagEndpoint ?? envEndpoint ?? storedEndpoint
|
|
49
42
|
|
|
50
43
|
const envToken = getTokenFromEnv()
|
|
51
44
|
// The token is stored per endpoint, so an overridden endpoint is read with
|
|
52
45
|
// the token belonging to it rather than the one it replaced.
|
|
53
|
-
const storedToken =
|
|
54
|
-
config.get(`${endpoint ?? defaultEndpoint}.pat`),
|
|
55
|
-
)
|
|
46
|
+
const storedToken = config.getToken(endpoint ?? defaultEndpoint)
|
|
56
47
|
|
|
57
48
|
const envWorkspaceId = getWorkspaceIdFromEnv()
|
|
58
|
-
const storedWorkspaceId =
|
|
49
|
+
const storedWorkspaceId = config.getWorkspace()
|
|
59
50
|
const workspaceId = flagWorkspaceId ?? envWorkspaceId ?? storedWorkspaceId
|
|
60
51
|
|
|
61
52
|
return {
|
|
@@ -88,7 +79,7 @@ export const resolveAuth = (
|
|
|
88
79
|
* shape it acts on, and how it may interact with the user.
|
|
89
80
|
*/
|
|
90
81
|
export interface CliContext {
|
|
91
|
-
config:
|
|
82
|
+
config: CliConfig
|
|
92
83
|
auth: AuthContext
|
|
93
84
|
output: Output
|
|
94
85
|
blueprint: ApiBlueprint
|
|
@@ -96,11 +87,3 @@ export interface CliContext {
|
|
|
96
87
|
/** The Seam API, constructed on first use and shared for the run. */
|
|
97
88
|
api: () => Promise<SeamApi>
|
|
98
89
|
}
|
|
99
|
-
|
|
100
|
-
const readString = (value: unknown): string | null => {
|
|
101
|
-
if (typeof value !== 'string') return null
|
|
102
|
-
|
|
103
|
-
const trimmedValue = value.trim()
|
|
104
|
-
|
|
105
|
-
return trimmedValue === '' ? null : trimmedValue
|
|
106
|
-
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { assertMutable, selectEndpoint } from 'lib/auth/operations.js'
|
|
2
|
-
import {
|
|
2
|
+
import { getConfig } from 'lib/config/index.js'
|
|
3
3
|
import { resolveAuth } from 'lib/context.js'
|
|
4
4
|
import { getOutput } from 'lib/output/get-output.js'
|
|
5
5
|
import { promptAutocomplete } from 'lib/prompt.js'
|
|
6
6
|
|
|
7
7
|
export async function interactForEndpointSelection() {
|
|
8
|
-
const config =
|
|
8
|
+
const config = getConfig()
|
|
9
9
|
assertMutable(resolveAuth(config), 'endpoint', 'select an endpoint')
|
|
10
10
|
|
|
11
11
|
const endpoints = ['http://localhost:3020', 'https://connect.getseam.com']
|
|
@@ -3,7 +3,7 @@ import chalk from 'chalk'
|
|
|
3
3
|
|
|
4
4
|
import { assertMutable, storeToken } from 'lib/auth/operations.js'
|
|
5
5
|
import { validateToken } from 'lib/auth/validate-token.js'
|
|
6
|
-
import {
|
|
6
|
+
import { getConfig } from 'lib/config/index.js'
|
|
7
7
|
import { resolveAuth } from 'lib/context.js'
|
|
8
8
|
import { getOutput } from 'lib/output/get-output.js'
|
|
9
9
|
import { withLoading } from 'lib/output/with-loading.js'
|
|
@@ -12,7 +12,7 @@ import { promptText } from 'lib/prompt.js'
|
|
|
12
12
|
import { interactForWorkspaceId } from './workspace-id.js'
|
|
13
13
|
|
|
14
14
|
export const interactForLogin = async () => {
|
|
15
|
-
const config =
|
|
15
|
+
const config = getConfig()
|
|
16
16
|
const output = getOutput()
|
|
17
17
|
const auth = resolveAuth(config)
|
|
18
18
|
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { SeamHttpWithoutWorkspace } from '@seamapi/http/connect'
|
|
2
2
|
|
|
3
3
|
import { assertMutable, selectWorkspace } from 'lib/auth/operations.js'
|
|
4
|
-
import {
|
|
4
|
+
import { getConfig } from 'lib/config/index.js'
|
|
5
5
|
import { resolveAuth } from 'lib/context.js'
|
|
6
6
|
import { getSeamMultiWorkspace } from 'lib/http/client.js'
|
|
7
7
|
import { withLoading } from 'lib/output/with-loading.js'
|
|
8
8
|
import { promptAutocomplete } from 'lib/prompt.js'
|
|
9
9
|
|
|
10
10
|
export const interactForWorkspaceId = async (personalAccessToken?: string) => {
|
|
11
|
-
const config =
|
|
11
|
+
const config = getConfig()
|
|
12
12
|
|
|
13
13
|
// Refuse before prompting: nothing selected here could be stored.
|
|
14
14
|
assertMutable(resolveAuth(config), 'workspaceId', 'select a workspace')
|
package/src/lib/version.ts
CHANGED