@mintlify/cli 4.0.1813 → 4.1.2
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/assets/export-scripts/README.classic.md +15 -0
- package/dist/assets/export-scripts/README.md +14 -0
- package/dist/assets/export-scripts/serve.js +114 -0
- package/dist/assets/export-scripts/start-docs.bat +10 -0
- package/dist/assets/export-scripts/start-docs.sh +9 -0
- package/dist/assets/og/Inter-Bold.ttf +0 -0
- package/dist/assets/og/Inter-Medium.ttf +0 -0
- package/dist/assets/og/Inter-Regular.ttf +0 -0
- package/dist/assets/og/Inter-SemiBold.ttf +0 -0
- package/dist/assets/og/PaperMono-Regular.ttf +0 -0
- package/dist/assets/og/index_bg.wasm +0 -0
- package/dist/assets/openChrome.applescript +94 -0
- package/dist/assets/socket.io/socket.io.esm.min.js +7 -0
- package/dist/assets/socket.io/socket.io.esm.min.js.map +1 -0
- package/dist/assets/socket.io/socket.io.js +4907 -0
- package/dist/assets/socket.io/socket.io.js.map +1 -0
- package/dist/assets/socket.io/socket.io.min.js +7 -0
- package/dist/assets/socket.io/socket.io.min.js.map +1 -0
- package/dist/assets/socket.io/socket.io.msgpack.min.js +7 -0
- package/dist/assets/socket.io/socket.io.msgpack.min.js.map +1 -0
- package/dist/assets/xdg-open +1066 -0
- package/dist/assets/yargs-locales/be.json +46 -0
- package/dist/assets/yargs-locales/cs.json +51 -0
- package/dist/assets/yargs-locales/de.json +46 -0
- package/dist/assets/yargs-locales/en.json +55 -0
- package/dist/assets/yargs-locales/es.json +46 -0
- package/dist/assets/yargs-locales/fi.json +49 -0
- package/dist/assets/yargs-locales/fr.json +53 -0
- package/dist/assets/yargs-locales/hi.json +49 -0
- package/dist/assets/yargs-locales/hu.json +46 -0
- package/dist/assets/yargs-locales/id.json +50 -0
- package/dist/assets/yargs-locales/it.json +46 -0
- package/dist/assets/yargs-locales/ja.json +51 -0
- package/dist/assets/yargs-locales/ko.json +49 -0
- package/dist/assets/yargs-locales/nb.json +44 -0
- package/dist/assets/yargs-locales/nl.json +49 -0
- package/dist/assets/yargs-locales/nn.json +44 -0
- package/dist/assets/yargs-locales/pirate.json +13 -0
- package/dist/assets/yargs-locales/pl.json +49 -0
- package/dist/assets/yargs-locales/pt.json +45 -0
- package/dist/assets/yargs-locales/pt_BR.json +48 -0
- package/dist/assets/yargs-locales/ru.json +51 -0
- package/dist/assets/yargs-locales/th.json +46 -0
- package/dist/assets/yargs-locales/tr.json +48 -0
- package/dist/assets/yargs-locales/uk_UA.json +51 -0
- package/dist/assets/yargs-locales/uz.json +52 -0
- package/dist/assets/yargs-locales/zh_CN.json +48 -0
- package/dist/assets/yargs-locales/zh_TW.json +51 -0
- package/dist/converter-d8O3xkdU.js +102 -0
- package/dist/devtools-CGNZQKFV.js +24 -0
- package/dist/index.js +8 -0
- package/dist/rolldown-runtime-CEAe2Uac.js +7 -0
- package/dist/schemas-CBsmCNpK.js +372 -0
- package/dist/start.js +2310 -0
- package/package.json +38 -31
- package/.oxlintrc.json +0 -5
- package/.prettierignore +0 -2
- package/.turbo/turbo-build.log +0 -1
- package/CONTRIBUTING.md +0 -45
- package/__test__/agent-auth.test.ts +0 -114
- package/__test__/agent-claim.test.ts +0 -164
- package/__test__/agent-credentials.test.ts +0 -149
- package/__test__/agent-deployment.test.ts +0 -106
- package/__test__/agent-setup-config.test.ts +0 -324
- package/__test__/agent-setup.test.ts +0 -207
- package/__test__/agent.test.ts +0 -64
- package/__test__/analytics/client.test.ts +0 -180
- package/__test__/analytics/dates.test.ts +0 -12
- package/__test__/analytics/filters.test.ts +0 -116
- package/__test__/analytics/format.test.ts +0 -137
- package/__test__/authenticatedFetch.test.ts +0 -182
- package/__test__/brokenLinks.test.ts +0 -283
- package/__test__/checkPort.test.ts +0 -92
- package/__test__/deploy-github-actions.test.ts +0 -202
- package/__test__/deploy-github-setup.test.ts +0 -240
- package/__test__/deploy-instructions.test.ts +0 -155
- package/__test__/deploy.test.ts +0 -202
- package/__test__/feedback.test.ts +0 -34
- package/__test__/foreignSyntax.test.ts +0 -73
- package/__test__/format.test.ts +0 -72
- package/__test__/helpers.test.ts +0 -52
- package/__test__/init.test.ts +0 -119
- package/__test__/instructions.test.ts +0 -53
- package/__test__/keyring.test.ts +0 -82
- package/__test__/mdxAccessibility.test.ts +0 -81
- package/__test__/migrate.test.ts +0 -186
- package/__test__/openApiCheck.test.ts +0 -185
- package/__test__/pathValidation.test.ts +0 -25
- package/__test__/resolveFiles.test.ts +0 -70
- package/__test__/sessionId.test.ts +0 -46
- package/__test__/signup.test.ts +0 -36
- package/__test__/update.test.ts +0 -117
- package/__test__/utils.ts +0 -25
- package/__test__/validateCommand.test.ts +0 -647
- package/__test__/validateEngine.test.ts +0 -245
- package/__test__/validateRules.test.ts +0 -1275
- package/__test__/welcome.test.ts +0 -15
- package/__test__/workflows/client.test.ts +0 -126
- package/bin/accessibility.js +0 -111
- package/bin/accessibilityCheck.js +0 -82
- package/bin/addDomain.js +0 -130
- package/bin/agent-auth.js +0 -104
- package/bin/agent-claim.js +0 -97
- package/bin/agent-credentials.js +0 -124
- package/bin/agent-deployment.js +0 -96
- package/bin/agent-http.js +0 -45
- package/bin/agent-setup-config.js +0 -141
- package/bin/agent-setup.js +0 -146
- package/bin/agent.js +0 -42
- package/bin/analytics/client.js +0 -60
- package/bin/analytics/dates.js +0 -6
- package/bin/analytics/filters.js +0 -27
- package/bin/analytics/format.js +0 -13
- package/bin/analytics/index.js +0 -496
- package/bin/analytics/output.js +0 -75
- package/bin/analytics/types.js +0 -1
- package/bin/authenticatedFetch.js +0 -46
- package/bin/brokenLinks.js +0 -122
- package/bin/callbackServer.js +0 -65
- package/bin/cli.js +0 -813
- package/bin/comingSoon.js +0 -26
- package/bin/config.js +0 -78
- package/bin/constants.js +0 -16
- package/bin/deploy-base.js +0 -20
- package/bin/deploy-github-actions.js +0 -102
- package/bin/deploy-github-setup.js +0 -165
- package/bin/deploy-instructions.js +0 -71
- package/bin/deploy.js +0 -123
- package/bin/feedback.js +0 -36
- package/bin/format.js +0 -121
- package/bin/helpers.js +0 -238
- package/bin/idToken.js +0 -14
- package/bin/index.js +0 -102
- package/bin/init.js +0 -182
- package/bin/instructions.js +0 -101
- package/bin/keyring.js +0 -78
- package/bin/login.js +0 -123
- package/bin/logout.js +0 -18
- package/bin/mdxAccessibility.js +0 -113
- package/bin/mdxLinter.js +0 -55
- package/bin/middlewares/subdomainMiddleware.js +0 -28
- package/bin/middlewares/telemetryMiddleware.js +0 -61
- package/bin/migrate.js +0 -54
- package/bin/og.js +0 -145
- package/bin/openApiCheck.js +0 -99
- package/bin/resolveFiles.js +0 -114
- package/bin/score/client.js +0 -40
- package/bin/score/index.js +0 -211
- package/bin/score/types.js +0 -1
- package/bin/signup.js +0 -118
- package/bin/start.js +0 -36
- package/bin/status.js +0 -106
- package/bin/telemetry/client.js +0 -53
- package/bin/telemetry/distinctId.js +0 -68
- package/bin/telemetry/sessionId.js +0 -36
- package/bin/telemetry/track.js +0 -149
- package/bin/templates.js +0 -127
- package/bin/tokenRefresh.js +0 -50
- package/bin/tsconfig.build.tsbuildinfo +0 -1
- package/bin/update.js +0 -65
- package/bin/validate/command.js +0 -127
- package/bin/validate/engine.js +0 -154
- package/bin/validate/files.js +0 -163
- package/bin/validate/mdx.js +0 -238
- package/bin/validate/output.js +0 -44
- package/bin/validate/report.js +0 -11
- package/bin/validate/rules/build.js +0 -102
- package/bin/validate/rules/content/duplicateH1.js +0 -57
- package/bin/validate/rules/content/foreignSyntax.js +0 -120
- package/bin/validate/rules/content/katex.js +0 -34
- package/bin/validate/rules/content/missingSnippet.js +0 -34
- package/bin/validate/rules/content/undefinedVariable.js +0 -13
- package/bin/validate/rules/content/unknownComponent.js +0 -121
- package/bin/validate/rules/content/voidTags.js +0 -38
- package/bin/validate/rules/index.js +0 -21
- package/bin/validate/rules/openapi.js +0 -50
- package/bin/validate/rules/parseError.js +0 -7
- package/bin/validate/site.js +0 -57
- package/bin/validate/types.js +0 -1
- package/bin/vendor/converter.js +0 -30882
- package/bin/welcome.js +0 -108
- package/bin/workflows/client.js +0 -34
- package/bin/workflows/index.js +0 -269
- package/bin/workflows/types.js +0 -11
- package/scripts/bundle-converter.mjs +0 -26
- package/scripts/generate-logo.mjs +0 -79
- package/src/accessibility.ts +0 -180
- package/src/accessibilityCheck.tsx +0 -150
- package/src/addDomain.tsx +0 -206
- package/src/agent-auth.ts +0 -119
- package/src/agent-claim.ts +0 -124
- package/src/agent-credentials.ts +0 -104
- package/src/agent-deployment.ts +0 -116
- package/src/agent-http.ts +0 -34
- package/src/agent-setup-config.ts +0 -144
- package/src/agent-setup.ts +0 -165
- package/src/agent.ts +0 -56
- package/src/analytics/client.ts +0 -153
- package/src/analytics/dates.ts +0 -6
- package/src/analytics/filters.ts +0 -50
- package/src/analytics/format.ts +0 -13
- package/src/analytics/index.tsx +0 -599
- package/src/analytics/output.ts +0 -99
- package/src/analytics/types.ts +0 -145
- package/src/authenticatedFetch.ts +0 -42
- package/src/brokenLinks.tsx +0 -152
- package/src/callbackServer.ts +0 -64
- package/src/cli.tsx +0 -1105
- package/src/comingSoon.tsx +0 -29
- package/src/config.ts +0 -75
- package/src/constants.ts +0 -28
- package/src/deploy-base.ts +0 -18
- package/src/deploy-github-actions.ts +0 -139
- package/src/deploy-github-setup.ts +0 -189
- package/src/deploy-instructions.ts +0 -76
- package/src/deploy.ts +0 -165
- package/src/feedback.tsx +0 -31
- package/src/format.tsx +0 -82
- package/src/helpers.tsx +0 -279
- package/src/idToken.ts +0 -17
- package/src/index.ts +0 -109
- package/src/init.tsx +0 -237
- package/src/instructions.ts +0 -99
- package/src/keyring.ts +0 -65
- package/src/login.tsx +0 -166
- package/src/logout.tsx +0 -8
- package/src/mdxAccessibility.ts +0 -134
- package/src/mdxLinter.tsx +0 -88
- package/src/middlewares/subdomainMiddleware.ts +0 -20
- package/src/middlewares/telemetryMiddleware.ts +0 -55
- package/src/migrate.ts +0 -76
- package/src/og.tsx +0 -185
- package/src/openApiCheck.tsx +0 -125
- package/src/resolveFiles.ts +0 -101
- package/src/score/client.ts +0 -36
- package/src/score/index.tsx +0 -205
- package/src/score/types.ts +0 -15
- package/src/signup.tsx +0 -172
- package/src/start.ts +0 -27
- package/src/status.tsx +0 -129
- package/src/telemetry/client.ts +0 -46
- package/src/telemetry/distinctId.ts +0 -70
- package/src/telemetry/sessionId.ts +0 -40
- package/src/telemetry/track.ts +0 -163
- package/src/templates.tsx +0 -143
- package/src/tokenRefresh.ts +0 -53
- package/src/update.tsx +0 -89
- package/src/validate/command.tsx +0 -135
- package/src/validate/engine.ts +0 -210
- package/src/validate/files.ts +0 -249
- package/src/validate/mdx.ts +0 -290
- package/src/validate/output.tsx +0 -76
- package/src/validate/report.ts +0 -40
- package/src/validate/rules/build.ts +0 -146
- package/src/validate/rules/content/duplicateH1.ts +0 -78
- package/src/validate/rules/content/foreignSyntax.ts +0 -144
- package/src/validate/rules/content/katex.ts +0 -33
- package/src/validate/rules/content/missingSnippet.ts +0 -36
- package/src/validate/rules/content/undefinedVariable.ts +0 -20
- package/src/validate/rules/content/unknownComponent.ts +0 -130
- package/src/validate/rules/content/voidTags.ts +0 -43
- package/src/validate/rules/index.ts +0 -23
- package/src/validate/rules/openapi.ts +0 -59
- package/src/validate/rules/parseError.ts +0 -9
- package/src/validate/site.ts +0 -53
- package/src/validate/types.ts +0 -102
- package/src/vendor/converter.ts +0 -1
- package/src/welcome.ts +0 -154
- package/src/workflows/client.ts +0 -39
- package/src/workflows/index.tsx +0 -287
- package/src/workflows/types.ts +0 -73
- package/tsconfig.build.json +0 -4
- package/tsconfig.json +0 -10
- package/vitest.config.ts +0 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mintlify/cli",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.1.2",
|
|
4
4
|
"description": "The Mintlify CLI",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=18.0.0"
|
|
@@ -25,18 +25,22 @@
|
|
|
25
25
|
"url": "https://github.com/mintlify/mint",
|
|
26
26
|
"directory": "packages/cli"
|
|
27
27
|
},
|
|
28
|
-
"
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"migrate"
|
|
31
|
+
],
|
|
32
|
+
"exports": "./dist/index.js",
|
|
29
33
|
"bin": {
|
|
30
|
-
"mint": "
|
|
31
|
-
"mintlify": "
|
|
34
|
+
"mint": "dist/index.js",
|
|
35
|
+
"mintlify": "dist/index.js"
|
|
32
36
|
},
|
|
33
37
|
"scripts": {
|
|
34
38
|
"dev": "pnpm build && NODE_NO_WARNINGS=1 node bin/index.js",
|
|
35
39
|
"prepack": "npm run build",
|
|
36
|
-
"build": "tsc --project tsconfig.build.json && node scripts/bundle-converter.mjs",
|
|
37
|
-
"clean:build": "rimraf bin",
|
|
40
|
+
"build": "tsc --project tsconfig.build.json && node scripts/bundle-converter.mjs && node scripts/bundle.ts",
|
|
41
|
+
"clean:build": "rimraf bin dist",
|
|
38
42
|
"clean:all": "rimraf node_modules .eslintcache && pnpm clean:build",
|
|
39
|
-
"watch": "
|
|
43
|
+
"watch": "node scripts/watch.ts",
|
|
40
44
|
"lint": "oxlint src",
|
|
41
45
|
"test": "vitest run",
|
|
42
46
|
"type": "tsgo --noEmit",
|
|
@@ -44,18 +48,35 @@
|
|
|
44
48
|
"format:check": "oxfmt --check"
|
|
45
49
|
},
|
|
46
50
|
"dependencies": {
|
|
51
|
+
"sharp": "0.33.5"
|
|
52
|
+
},
|
|
53
|
+
"optionalDependencies": {
|
|
54
|
+
"@mintlify/package-builder": "0.0.138",
|
|
55
|
+
"fsevents": "2.3.3",
|
|
56
|
+
"keytar": "7.9.0"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
47
59
|
"@inquirer/prompts": "7.9.0",
|
|
48
60
|
"@mintlify/common": "1.0.1349",
|
|
61
|
+
"@mintlify/editor": "0.0.627",
|
|
49
62
|
"@mintlify/link-rot": "3.0.1591",
|
|
50
63
|
"@mintlify/models": "0.0.389",
|
|
51
64
|
"@mintlify/og": "0.0.148",
|
|
52
65
|
"@mintlify/prebuild": "1.0.1522",
|
|
53
66
|
"@mintlify/previewing": "4.0.1609",
|
|
67
|
+
"@mintlify/ts-config": "2.0.2",
|
|
54
68
|
"@mintlify/validation": "0.1.962",
|
|
69
|
+
"@tsconfig/recommended": "1.0.2",
|
|
70
|
+
"@types/detect-port": "1.3.2",
|
|
71
|
+
"@types/keytar": "4.4.2",
|
|
72
|
+
"@types/mdast": "4.0.4",
|
|
73
|
+
"@types/node": "18.15.0",
|
|
74
|
+
"@types/yargs": "17.0.22",
|
|
55
75
|
"adm-zip": "0.6.0",
|
|
56
76
|
"chalk": "5.2.0",
|
|
57
77
|
"color": "4.2.3",
|
|
58
78
|
"detect-port": "1.5.1",
|
|
79
|
+
"esbuild": "0.25.12",
|
|
59
80
|
"fs-extra": "11.2.0",
|
|
60
81
|
"html-void-elements": "3.0.0",
|
|
61
82
|
"ink": "6.3.0",
|
|
@@ -65,7 +86,11 @@
|
|
|
65
86
|
"katex": "0.16.47",
|
|
66
87
|
"mdast-util-mdx-jsx": "3.2.0",
|
|
67
88
|
"open": "8.4.2",
|
|
89
|
+
"openapi-types": "12.1.3",
|
|
68
90
|
"openid-client": "6.8.2",
|
|
91
|
+
"oxfmt": "^0.51.0",
|
|
92
|
+
"oxlint": "1.66.0",
|
|
93
|
+
"oxlint-tsgolint": "^0.23.0",
|
|
69
94
|
"posthog-node": "5.17.2",
|
|
70
95
|
"prosemirror-model": "1.25.4",
|
|
71
96
|
"react": "19.2.3",
|
|
@@ -76,35 +101,17 @@
|
|
|
76
101
|
"remark-mdx": "3.1.1",
|
|
77
102
|
"remark-smartypants": "3.0.2",
|
|
78
103
|
"remark-stringify": "11.0.0",
|
|
104
|
+
"rimraf": "5.0.1",
|
|
105
|
+
"rolldown": "1.2.14",
|
|
79
106
|
"smol-toml": "1.6.0",
|
|
107
|
+
"typescript": "5.5.3",
|
|
80
108
|
"unified": "11.0.5",
|
|
81
109
|
"unist-util-visit": "5.0.0",
|
|
110
|
+
"vitest": "2.1.9",
|
|
111
|
+
"vitest-mock-process": "1.0.4",
|
|
82
112
|
"yaml": "2.8.3",
|
|
83
113
|
"yargs": "17.7.1",
|
|
84
114
|
"zod": "4.3.6"
|
|
85
115
|
},
|
|
86
|
-
"
|
|
87
|
-
"keytar": "7.9.0"
|
|
88
|
-
},
|
|
89
|
-
"devDependencies": {
|
|
90
|
-
"@mintlify/editor": "0.0.627",
|
|
91
|
-
"@mintlify/ts-config": "2.0.2",
|
|
92
|
-
"@tsconfig/recommended": "1.0.2",
|
|
93
|
-
"@types/detect-port": "1.3.2",
|
|
94
|
-
"@types/keytar": "4.4.2",
|
|
95
|
-
"@types/mdast": "4.0.4",
|
|
96
|
-
"@types/node": "18.15.0",
|
|
97
|
-
"@types/yargs": "17.0.22",
|
|
98
|
-
"esbuild": "0.25.12",
|
|
99
|
-
"openapi-types": "12.1.3",
|
|
100
|
-
"oxfmt": "^0.51.0",
|
|
101
|
-
"oxlint": "1.66.0",
|
|
102
|
-
"oxlint-tsgolint": "^0.23.0",
|
|
103
|
-
"rimraf": "5.0.1",
|
|
104
|
-
"sharp": "^0.33.1",
|
|
105
|
-
"typescript": "5.5.3",
|
|
106
|
-
"vitest": "2.1.9",
|
|
107
|
-
"vitest-mock-process": "1.0.4"
|
|
108
|
-
},
|
|
109
|
-
"gitHead": "8ba12109669f80e5d70d3639460a775040ffba54"
|
|
116
|
+
"gitHead": "a4608233dd751191e146dd173c8e4d61656b1fbd"
|
|
110
117
|
}
|
package/.oxlintrc.json
DELETED
package/.prettierignore
DELETED
package/.turbo/turbo-build.log
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
$ tsc --project tsconfig.build.json && node scripts/bundle-converter.mjs
|
package/CONTRIBUTING.md
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# Running CLI Locally
|
|
2
|
-
|
|
3
|
-
Note: contributing requires `pnpm` and it's recommended you install it as a global installation. If you don't have pnpm installed already run `npm install -g pnpm` in your terminal.
|
|
4
|
-
|
|
5
|
-
Run `./scripts/with-registry-auth.sh pnpm install` from the repository root to install dependencies. Then, run `npm link` once to link to your local version of the CLI in the npm global namespace (`npm list -g`). Make sure you are working in `packages/cli` and not its alias package, `packages/mintlify`.
|
|
6
|
-
|
|
7
|
-
If you already have a non-local version of the CLI, you will need to uninstall it (`npm uninstall -g mint`) or force overwrite the executable file. Otherwise, you will get an `EEXIST` error on the mintlify binary. You can overwrite the file using the `--force` flag (`npm link --force`), but always double check the file(s) you are overwriting.
|
|
8
|
-
|
|
9
|
-
Make sure to build the CLI using `pnpm build` or `pnpm watch` to see your local changes reflected. Tip: keep `pnpm watch` running in a separate terminal for changes to be quickly and continuously reflected while developing.
|
|
10
|
-
|
|
11
|
-
To uninstall locally, run `npm uninstall -g @mintlify/cli`.
|
|
12
|
-
|
|
13
|
-
If you wish to continue using the production version of the CLI, you will need to reinstall it (`npm install -g mint`). If you do not uninstall `@mintlify/cli`, then you will need to force overwrite the executable file (`npm install -g mint --force`) for the same reasons as mentioned above. As a best practice, we recommend always uninstalling `@mintlify/cli` first.
|
|
14
|
-
|
|
15
|
-
## Updating Version of Mint Client
|
|
16
|
-
|
|
17
|
-
The CLI uses GitHub releases to download specific versions of the client code used in `mintlify dev`. It will attempt to fetch the latest version of the client app specified by our [`mint-version.txt`](https://d1ctpt7j8wusba.cloudfront.net/mint-version.txt) file. The mint-version.txt and compressed versions of the client live in S3 under `mint-client-releases`.
|
|
18
|
-
|
|
19
|
-
Here's how new client changes are published, they should usually require no manual intervention:
|
|
20
|
-
|
|
21
|
-
1. When a PR is merged to main, we check whether or not the client app or any of its dependencies have changed with the `check-changes` workflow job
|
|
22
|
-
2. The `bump-packages` job always runs, which increments the version of any package that has changed in our monorepo packages, commits the version increment to github, and publishes the update to npm
|
|
23
|
-
3. If `check-changes` detected a client change, the `client-release` workflow is triggered after `bump-packages`
|
|
24
|
-
4. The `client-release` workflow creates a standalone zip of the client app and uploads it to S3 as mint-${version}.tar.gz
|
|
25
|
-
5. The `cli-test` workflow is triggered, which uses a local build of the CLI package and the new client version that was just zipped and shipped. It tests the CLI output has not changed (no new errors) and that the starter-kit passes some simple Puppeteer tests.
|
|
26
|
-
6. If the `cli-test` workflow passes, the `update-release-version` workflow is triggered, which uploads an updated version of the mint-version.txt file to S3 with the new version number, and invalidates the cache for that file.
|
|
27
|
-
|
|
28
|
-
## Manually triggering a client release and version update
|
|
29
|
-
|
|
30
|
-
You can still create a new client version whenever by dispatching the `client-release` workflow from the [Actions tab for the mint repo](https://github.com/mintlify/mint/actions). Click the "Run workflow" dropdown button and enter the version number you want to set it to, then click the green "Run workflow" button. This should run the steps 4-6 detailed above, creating a new package of the client app and updating the version number used by the CLI.
|
|
31
|
-
|
|
32
|
-
## Manually updating the version of the client app used by the CLI
|
|
33
|
-
|
|
34
|
-
If for some reason we need to roll back a client version, we can update the mint-version.txt file to swiftly change which version is downloaded by the CLI. This can be done 2 ways:
|
|
35
|
-
|
|
36
|
-
1. Via github workflow: Go to the [Actions tab for the mint repo](https://github.com/mintlify/mint/actions) and select the Update Release Version workflow. Click the "Run workflow" dropdown button and enter the version number you want to set it to, then click the green "Run workflow" button.
|
|
37
|
-
2. Manually: If you have S3 access, simply upload a new version of the mint-version.txt file in the `mint-client-releases` folder
|
|
38
|
-
|
|
39
|
-
## Manually uploading a packaged version of the client app
|
|
40
|
-
|
|
41
|
-
If for some reason the release workflow is broken or we can't access AWS programmatically, the newly zipped version of the client app is retained in the `client-release` workflow as a Github artifact for 1 day. We can download it manually from Github after the release and upload it wherever needed.
|
|
42
|
-
|
|
43
|
-
## Manually overriding the client version used locally
|
|
44
|
-
|
|
45
|
-
Run `mintlify dev --client-version x.x.xxxx` with the client version you would like to use and that version will be downloaded instead of the latest client version.
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
discoverAgentAuth,
|
|
5
|
-
exchangeAgentToken,
|
|
6
|
-
getAgentIdentity,
|
|
7
|
-
registerAgent,
|
|
8
|
-
} from '../src/agent-auth.js';
|
|
9
|
-
import { agentRequest, AgentApiError } from '../src/agent-http.js';
|
|
10
|
-
|
|
11
|
-
const issuer = 'https://auth.example.com';
|
|
12
|
-
const discovery = {
|
|
13
|
-
issuer,
|
|
14
|
-
token_endpoint: `${issuer}/oauth2/token`,
|
|
15
|
-
agent_auth: {
|
|
16
|
-
identity_endpoint: `${issuer}/agent/identity`,
|
|
17
|
-
claim_endpoint: `${issuer}/agent/identity/claim`,
|
|
18
|
-
identity_types_supported: ['anonymous'],
|
|
19
|
-
},
|
|
20
|
-
};
|
|
21
|
-
const credentials = {
|
|
22
|
-
registrationId: 'reg_1',
|
|
23
|
-
issuer,
|
|
24
|
-
assertion: 'assertion-secret',
|
|
25
|
-
claimToken: 'claim-secret',
|
|
26
|
-
};
|
|
27
|
-
function respond(body: unknown, status = 200) {
|
|
28
|
-
return new Response(JSON.stringify(body), { status });
|
|
29
|
-
}
|
|
30
|
-
afterEach(() => vi.unstubAllGlobals());
|
|
31
|
-
|
|
32
|
-
describe('agent authentication', () => {
|
|
33
|
-
it('preserves caller cancellation alongside the request timeout', async () => {
|
|
34
|
-
const controller = new AbortController();
|
|
35
|
-
let requestSignal: AbortSignal | undefined;
|
|
36
|
-
vi.stubGlobal(
|
|
37
|
-
'fetch',
|
|
38
|
-
vi.fn(async (_url, init: RequestInit) => {
|
|
39
|
-
requestSignal = init.signal ?? undefined;
|
|
40
|
-
return respond({});
|
|
41
|
-
})
|
|
42
|
-
);
|
|
43
|
-
await agentRequest('https://api.example.com', { signal: controller.signal });
|
|
44
|
-
expect(requestSignal?.aborted).toBe(false);
|
|
45
|
-
controller.abort();
|
|
46
|
-
expect(requestSignal?.aborted).toBe(true);
|
|
47
|
-
});
|
|
48
|
-
it('discovers endpoints from Mintlify and validates their origin', async () => {
|
|
49
|
-
const request = vi
|
|
50
|
-
.fn()
|
|
51
|
-
.mockResolvedValueOnce(respond({ authorization_servers: [issuer] }))
|
|
52
|
-
.mockResolvedValueOnce(respond(discovery));
|
|
53
|
-
vi.stubGlobal('fetch', request);
|
|
54
|
-
expect(await discoverAgentAuth('https://api.example.com')).toEqual(discovery);
|
|
55
|
-
expect(request.mock.calls[0]?.[0]).toBe(
|
|
56
|
-
'https://api.example.com/api/agent/.well-known/oauth-protected-resource'
|
|
57
|
-
);
|
|
58
|
-
});
|
|
59
|
-
it('does not send credentials to endpoints on a different origin', async () => {
|
|
60
|
-
vi.stubGlobal(
|
|
61
|
-
'fetch',
|
|
62
|
-
vi
|
|
63
|
-
.fn()
|
|
64
|
-
.mockResolvedValueOnce(respond({ authorization_servers: [issuer] }))
|
|
65
|
-
.mockResolvedValueOnce(
|
|
66
|
-
respond({ ...discovery, token_endpoint: 'https://other.example.com/token' })
|
|
67
|
-
)
|
|
68
|
-
);
|
|
69
|
-
await expect(discoverAgentAuth('https://api.example.com')).rejects.toThrow('do not match');
|
|
70
|
-
});
|
|
71
|
-
it('parses the hosted registration response and exchanges without resource', async () => {
|
|
72
|
-
const request = vi
|
|
73
|
-
.fn()
|
|
74
|
-
.mockResolvedValueOnce(
|
|
75
|
-
respond({
|
|
76
|
-
id: 'reg_1',
|
|
77
|
-
identity: { assertion: credentials.assertion },
|
|
78
|
-
claim: { token: credentials.claimToken },
|
|
79
|
-
})
|
|
80
|
-
)
|
|
81
|
-
.mockResolvedValueOnce(
|
|
82
|
-
respond({ access_token: 'access', token_type: 'Bearer', expires_in: 300 })
|
|
83
|
-
);
|
|
84
|
-
vi.stubGlobal('fetch', request);
|
|
85
|
-
expect(await registerAgent(discovery)).toEqual(credentials);
|
|
86
|
-
expect((await exchangeAgentToken(discovery, credentials)).accessToken).toBe('access');
|
|
87
|
-
const body = request.mock.calls[1]?.[1].body;
|
|
88
|
-
expect(body.get('assertion')).toBe(credentials.assertion);
|
|
89
|
-
expect(body.has('resource')).toBe(false);
|
|
90
|
-
});
|
|
91
|
-
it('sends the agent token without reading human credentials', async () => {
|
|
92
|
-
const request = vi
|
|
93
|
-
.fn()
|
|
94
|
-
.mockResolvedValue(respond({ registrationId: 'reg_1', identityType: 'anonymous' }));
|
|
95
|
-
vi.stubGlobal('fetch', request);
|
|
96
|
-
await getAgentIdentity('https://api.example.com', 'agent-token');
|
|
97
|
-
expect(request.mock.calls[0]?.[1].headers.Authorization).toBe('Bearer agent-token');
|
|
98
|
-
});
|
|
99
|
-
it('does not register again on outages or print upstream secrets', async () => {
|
|
100
|
-
const request = vi
|
|
101
|
-
.fn()
|
|
102
|
-
.mockResolvedValue(respond({ error: 'unavailable', message: credentials.assertion }, 503));
|
|
103
|
-
vi.stubGlobal('fetch', request);
|
|
104
|
-
await expect(exchangeAgentToken(discovery, credentials)).rejects.toThrow(AgentApiError);
|
|
105
|
-
expect(request).toHaveBeenCalledTimes(1);
|
|
106
|
-
});
|
|
107
|
-
it('rejects malformed successful responses without exposing them', async () => {
|
|
108
|
-
vi.stubGlobal(
|
|
109
|
-
'fetch',
|
|
110
|
-
vi.fn().mockResolvedValue(respond({ assertion: credentials.assertion }))
|
|
111
|
-
);
|
|
112
|
-
await expect(registerAgent(discovery)).rejects.toThrow('unexpected response');
|
|
113
|
-
});
|
|
114
|
-
});
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
|
|
3
|
-
import { claimDeployment } from '../src/agent-claim.js';
|
|
4
|
-
import { loadAgentCredentials, saveAgentCredentials } from '../src/agent-credentials.js';
|
|
5
|
-
import { trackEvent } from '../src/telemetry/track.js';
|
|
6
|
-
|
|
7
|
-
vi.mock('../src/agent-credentials.js', () => ({
|
|
8
|
-
loadAgentCredentials: vi.fn(),
|
|
9
|
-
saveAgentCredentials: vi.fn(),
|
|
10
|
-
}));
|
|
11
|
-
vi.mock('../src/telemetry/track.js', () => ({ trackEvent: vi.fn() }));
|
|
12
|
-
const issuer = 'https://auth.example.com';
|
|
13
|
-
const apiUrl = 'https://api.example.com';
|
|
14
|
-
const options = { apiUrl, directory: '/docs', code: 'ABCDEFGH' };
|
|
15
|
-
const original = {
|
|
16
|
-
registrationId: 'reg',
|
|
17
|
-
issuer,
|
|
18
|
-
assertion: 'old-assertion',
|
|
19
|
-
claimToken: 'secret-claim',
|
|
20
|
-
accessToken: 'anonymous-token',
|
|
21
|
-
accessTokenExpiresAt: Date.now() + 300_000,
|
|
22
|
-
};
|
|
23
|
-
const discovery = {
|
|
24
|
-
issuer,
|
|
25
|
-
token_endpoint: `${issuer}/oauth2/token`,
|
|
26
|
-
agent_auth: {
|
|
27
|
-
identity_endpoint: `${issuer}/agent/identity`,
|
|
28
|
-
claim_endpoint: `${issuer}/agent/identity/claim`,
|
|
29
|
-
identity_types_supported: ['anonymous'],
|
|
30
|
-
},
|
|
31
|
-
};
|
|
32
|
-
const response = (body: unknown, status = 200) => new Response(JSON.stringify(body), { status });
|
|
33
|
-
const fetchMock = vi.fn();
|
|
34
|
-
beforeEach(() => {
|
|
35
|
-
vi.resetAllMocks();
|
|
36
|
-
vi.stubGlobal('fetch', fetchMock);
|
|
37
|
-
vi.mocked(loadAgentCredentials).mockResolvedValue({ ...original });
|
|
38
|
-
vi.mocked(saveAgentCredentials).mockResolvedValue(undefined);
|
|
39
|
-
fetchMock.mockImplementation(async (url: string) => {
|
|
40
|
-
if (url.endsWith('oauth-protected-resource'))
|
|
41
|
-
return response({ authorization_servers: [issuer] });
|
|
42
|
-
if (url.endsWith('oauth-authorization-server')) return response(discovery);
|
|
43
|
-
if (url.endsWith('/complete'))
|
|
44
|
-
return response({ identity: { assertion: 'claimed-assertion' } });
|
|
45
|
-
if (url.endsWith('/oauth2/token'))
|
|
46
|
-
return response({ access_token: 'claimed-token', expires_in: 300, token_type: 'Bearer' });
|
|
47
|
-
if (url.endsWith('/signup')) return response({ orgId: 'org', claimed: true });
|
|
48
|
-
if (url.endsWith('/claim'))
|
|
49
|
-
return response({ attempt: { verification_uri: `${issuer}/verify?token=attempt` } });
|
|
50
|
-
throw new Error('Unexpected endpoint');
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
afterEach(() => vi.unstubAllGlobals());
|
|
54
|
-
|
|
55
|
-
describe('claim command', () => {
|
|
56
|
-
it('completes the existing claim, saves the new identity, and signs up with a fresh token', async () => {
|
|
57
|
-
expect(await claimDeployment(options)).toContain('successfully');
|
|
58
|
-
expect(fetchMock).toHaveBeenCalledWith(
|
|
59
|
-
`${issuer}/agent/identity/claim/complete`,
|
|
60
|
-
expect.objectContaining({
|
|
61
|
-
body: JSON.stringify({ claim_token: 'secret-claim', user_code: options.code }),
|
|
62
|
-
})
|
|
63
|
-
);
|
|
64
|
-
expect(fetchMock).not.toHaveBeenCalledWith(`${issuer}/agent/identity/claim`, expect.anything());
|
|
65
|
-
expect(fetchMock).toHaveBeenCalledWith(
|
|
66
|
-
`${apiUrl}/api/agent/signup`,
|
|
67
|
-
expect.objectContaining({ headers: { Authorization: 'Bearer claimed-token' } })
|
|
68
|
-
);
|
|
69
|
-
expect(vi.mocked(saveAgentCredentials).mock.calls[0]?.[2]).toMatchObject({
|
|
70
|
-
assertion: 'claimed-assertion',
|
|
71
|
-
claimVerified: true,
|
|
72
|
-
accessToken: undefined,
|
|
73
|
-
});
|
|
74
|
-
expect(vi.mocked(saveAgentCredentials).mock.invocationCallOrder[0]).toBeLessThan(
|
|
75
|
-
fetchMock.mock.invocationCallOrder[3]!
|
|
76
|
-
);
|
|
77
|
-
expect(vi.mocked(trackEvent).mock.calls).toEqual([
|
|
78
|
-
['agent_signup.claim_completed', { registration_id: 'reg' }],
|
|
79
|
-
]);
|
|
80
|
-
});
|
|
81
|
-
it('resumes after signup fails without consuming the code again', async () => {
|
|
82
|
-
const implementation = fetchMock.getMockImplementation()!;
|
|
83
|
-
fetchMock.mockImplementation((url, init) =>
|
|
84
|
-
url.endsWith('/signup') ? response({ code: 'unavailable' }, 503) : implementation(url, init)
|
|
85
|
-
);
|
|
86
|
-
await expect(claimDeployment(options)).rejects.toThrow('503');
|
|
87
|
-
const saved = vi.mocked(saveAgentCredentials).mock.calls[0]![2];
|
|
88
|
-
vi.mocked(loadAgentCredentials).mockResolvedValue(saved);
|
|
89
|
-
fetchMock.mockClear().mockImplementation(implementation);
|
|
90
|
-
expect(await claimDeployment(options)).toContain('successfully');
|
|
91
|
-
expect(fetchMock.mock.calls.some(([url]) => url.endsWith('/complete'))).toBe(false);
|
|
92
|
-
});
|
|
93
|
-
it('does not save or sign up after an invalid code', async () => {
|
|
94
|
-
const implementation = fetchMock.getMockImplementation()!;
|
|
95
|
-
fetchMock.mockImplementation((url, init) =>
|
|
96
|
-
url.endsWith('/complete')
|
|
97
|
-
? response({ code: 'invalid_user_code' }, 400)
|
|
98
|
-
: implementation(url, init)
|
|
99
|
-
);
|
|
100
|
-
await expect(claimDeployment(options)).rejects.toThrow('invalid_user_code');
|
|
101
|
-
expect(saveAgentCredentials).not.toHaveBeenCalled();
|
|
102
|
-
expect(trackEvent).not.toHaveBeenCalled();
|
|
103
|
-
expect(fetchMock.mock.calls.some(([url]) => url.endsWith('/signup'))).toBe(false);
|
|
104
|
-
});
|
|
105
|
-
it('fails before signup when credentials cannot be saved', async () => {
|
|
106
|
-
vi.mocked(saveAgentCredentials).mockRejectedValue(new Error('disk full'));
|
|
107
|
-
await expect(claimDeployment(options)).rejects.toThrow('disk full');
|
|
108
|
-
expect(fetchMock.mock.calls.some(([url]) => url.endsWith('/signup'))).toBe(false);
|
|
109
|
-
});
|
|
110
|
-
it('creates only a claim link for --email', async () => {
|
|
111
|
-
const output = await claimDeployment({
|
|
112
|
-
apiUrl,
|
|
113
|
-
directory: '/docs',
|
|
114
|
-
email: 'person@example.com',
|
|
115
|
-
});
|
|
116
|
-
expect(new URL(output.match(/https?:\/\/\S+/)![0]).searchParams.get('registration')).toBe(
|
|
117
|
-
'reg'
|
|
118
|
-
);
|
|
119
|
-
expect(vi.mocked(trackEvent).mock.calls).toEqual([
|
|
120
|
-
['agent_signup.claim_started', { registration_id: 'reg' }],
|
|
121
|
-
]);
|
|
122
|
-
expect(
|
|
123
|
-
fetchMock.mock.calls.some(([url]) => url.endsWith('/signup') || url.endsWith('/complete'))
|
|
124
|
-
).toBe(false);
|
|
125
|
-
});
|
|
126
|
-
it('explains a claim link refused for too many pending attempts', async () => {
|
|
127
|
-
const respond = fetchMock.getMockImplementation()!;
|
|
128
|
-
fetchMock.mockImplementation(async (url: string) =>
|
|
129
|
-
url.endsWith('/claim') ? response({ error: 'too_many_attempts' }, 429) : respond(url)
|
|
130
|
-
);
|
|
131
|
-
await expect(
|
|
132
|
-
claimDeployment({ apiUrl, directory: '/docs', email: 'person@example.com' })
|
|
133
|
-
).rejects.toThrow('Too many claim links are still pending');
|
|
134
|
-
});
|
|
135
|
-
it('fails before network calls when credentials are missing', async () => {
|
|
136
|
-
vi.mocked(loadAgentCredentials).mockResolvedValue(null);
|
|
137
|
-
await expect(claimDeployment(options)).rejects.toThrow('No saved agent registration');
|
|
138
|
-
expect(fetchMock).not.toHaveBeenCalled();
|
|
139
|
-
});
|
|
140
|
-
it.each([{ code: undefined }, { code: ' ', email: undefined }, { email: 'person@example.com' }])(
|
|
141
|
-
'rejects missing or conflicting arguments: %o',
|
|
142
|
-
async (override) => {
|
|
143
|
-
await expect(claimDeployment({ ...options, ...override })).rejects.toThrow('Provide either');
|
|
144
|
-
expect(fetchMock).not.toHaveBeenCalled();
|
|
145
|
-
}
|
|
146
|
-
);
|
|
147
|
-
it('rejects a different issuer without sending the claim token', async () => {
|
|
148
|
-
vi.mocked(loadAgentCredentials).mockResolvedValue({
|
|
149
|
-
...original,
|
|
150
|
-
issuer: 'https://other.example.com',
|
|
151
|
-
});
|
|
152
|
-
await expect(claimDeployment(options)).rejects.toThrow('another issuer');
|
|
153
|
-
expect(fetchMock.mock.calls).toHaveLength(2);
|
|
154
|
-
});
|
|
155
|
-
it('does not report success if signup returns an anonymous identity', async () => {
|
|
156
|
-
const implementation = fetchMock.getMockImplementation()!;
|
|
157
|
-
fetchMock.mockImplementation((url, init) =>
|
|
158
|
-
url.endsWith('/signup')
|
|
159
|
-
? response({ orgId: 'org', claimed: false })
|
|
160
|
-
: implementation(url, init)
|
|
161
|
-
);
|
|
162
|
-
await expect(claimDeployment(options)).rejects.toThrow('unexpected response');
|
|
163
|
-
});
|
|
164
|
-
});
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import os from 'node:os';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
5
|
-
|
|
6
|
-
const entries = new Map<string, string>();
|
|
7
|
-
const keytar = vi.hoisted(() => ({
|
|
8
|
-
getPassword: vi.fn(),
|
|
9
|
-
setPassword: vi.fn(),
|
|
10
|
-
deletePassword: vi.fn(),
|
|
11
|
-
}));
|
|
12
|
-
vi.mock('../src/keyring.js', () => ({ getKeytar: async () => keytar }));
|
|
13
|
-
const configDir = vi.hoisted(() => ({ path: '' }));
|
|
14
|
-
vi.mock('../src/constants.js', () => ({
|
|
15
|
-
get CONFIG_DIR() {
|
|
16
|
-
return configDir.path;
|
|
17
|
-
},
|
|
18
|
-
}));
|
|
19
|
-
|
|
20
|
-
import { loadAgentCredentials, saveAgentCredentials } from '../src/agent-credentials.js';
|
|
21
|
-
|
|
22
|
-
const credentials = {
|
|
23
|
-
registrationId: 'reg_1',
|
|
24
|
-
issuer: 'https://auth.example.com',
|
|
25
|
-
assertion: 'assertion',
|
|
26
|
-
claimToken: 'claim',
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
describe('agent credentials', () => {
|
|
30
|
-
beforeEach(() => {
|
|
31
|
-
entries.clear();
|
|
32
|
-
vi.resetAllMocks();
|
|
33
|
-
configDir.path = fs.mkdtempSync(path.join(os.tmpdir(), 'mint-agent-'));
|
|
34
|
-
keytar.getPassword.mockImplementation(
|
|
35
|
-
async (service, account) => entries.get(`${service}/${account}`) ?? null
|
|
36
|
-
);
|
|
37
|
-
keytar.setPassword.mockImplementation(async (service, account, value) => {
|
|
38
|
-
entries.set(`${service}/${account}`, value);
|
|
39
|
-
});
|
|
40
|
-
keytar.deletePassword.mockImplementation(async (service, account) =>
|
|
41
|
-
entries.delete(`${service}/${account}`)
|
|
42
|
-
);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('isolates projects and environments from each other and human credentials', async () => {
|
|
46
|
-
await saveAgentCredentials('https://api.example.com', '/docs', credentials);
|
|
47
|
-
expect(await loadAgentCredentials('https://api.example.com', '/docs')).toEqual(credentials);
|
|
48
|
-
expect(await loadAgentCredentials('https://dev.example.com', '/docs')).toBeNull();
|
|
49
|
-
expect(await loadAgentCredentials('https://api.example.com', '/other')).toBeNull();
|
|
50
|
-
expect([...entries.keys()].every((key) => key.startsWith('mintlify-agent/'))).toBe(true);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it('retains old registrations when the current project starts a new site', async () => {
|
|
54
|
-
await saveAgentCredentials('https://api.example.com', '/docs', credentials);
|
|
55
|
-
await saveAgentCredentials('https://api.example.com', '/docs', {
|
|
56
|
-
...credentials,
|
|
57
|
-
registrationId: 'reg_2',
|
|
58
|
-
});
|
|
59
|
-
expect((await loadAgentCredentials('https://api.example.com', '/docs'))?.registrationId).toBe(
|
|
60
|
-
'reg_2'
|
|
61
|
-
);
|
|
62
|
-
expect(entries.has('mintlify-agent/https://api.example.com:reg_1')).toBe(true);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('falls back to a local file when the keyring cannot be written or read', async () => {
|
|
66
|
-
keytar.setPassword.mockRejectedValue(new Error('locked'));
|
|
67
|
-
await saveAgentCredentials('https://api.example.com', '/docs', credentials);
|
|
68
|
-
expect(entries.size).toBe(0);
|
|
69
|
-
const file = path.join(configDir.path, 'agent-credentials.json');
|
|
70
|
-
if (process.platform !== 'win32') expect(fs.statSync(file).mode & 0o777).toBe(0o600);
|
|
71
|
-
keytar.getPassword.mockRejectedValue(new Error('locked'));
|
|
72
|
-
expect(await loadAgentCredentials('https://api.example.com', '/docs')).toEqual(credentials);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it('removes stale fallback entries once the keyring is writable again', async () => {
|
|
76
|
-
keytar.setPassword.mockRejectedValueOnce(new Error('locked'));
|
|
77
|
-
await saveAgentCredentials('https://api.example.com', '/docs', credentials);
|
|
78
|
-
await saveAgentCredentials('https://api.example.com', '/docs', {
|
|
79
|
-
...credentials,
|
|
80
|
-
assertion: 'next',
|
|
81
|
-
});
|
|
82
|
-
expect((await loadAgentCredentials('https://api.example.com', '/docs'))?.assertion).toBe(
|
|
83
|
-
'next'
|
|
84
|
-
);
|
|
85
|
-
const file = path.join(configDir.path, 'agent-credentials.json');
|
|
86
|
-
expect(JSON.parse(fs.readFileSync(file, 'utf-8'))).toEqual({});
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('reads the keyring before the fallback file', async () => {
|
|
90
|
-
await saveAgentCredentials('https://api.example.com', '/docs', credentials);
|
|
91
|
-
fs.writeFileSync(
|
|
92
|
-
path.join(configDir.path, 'agent-credentials.json'),
|
|
93
|
-
JSON.stringify({
|
|
94
|
-
'https://api.example.com:reg_1': JSON.stringify({ ...credentials, assertion: 'stale' }),
|
|
95
|
-
})
|
|
96
|
-
);
|
|
97
|
-
expect(await loadAgentCredentials('https://api.example.com', '/docs')).toEqual(credentials);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it('ignores a corrupt fallback file when the keyring has the credentials', async () => {
|
|
101
|
-
await saveAgentCredentials('https://api.example.com', '/docs', credentials);
|
|
102
|
-
fs.writeFileSync(path.join(configDir.path, 'agent-credentials.json'), '{');
|
|
103
|
-
expect(await loadAgentCredentials('https://api.example.com', '/docs')).toEqual(credentials);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('drops the stale keyring entry when a write falls back to the file', async () => {
|
|
107
|
-
await saveAgentCredentials('https://api.example.com', '/docs', credentials);
|
|
108
|
-
keytar.setPassword.mockRejectedValueOnce(new Error('locked'));
|
|
109
|
-
await saveAgentCredentials('https://api.example.com', '/docs', {
|
|
110
|
-
...credentials,
|
|
111
|
-
assertion: 'next',
|
|
112
|
-
});
|
|
113
|
-
expect((await loadAgentCredentials('https://api.example.com', '/docs'))?.assertion).toBe(
|
|
114
|
-
'next'
|
|
115
|
-
);
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
it('succeeds when the keyring write works but fallback cleanup fails', async () => {
|
|
119
|
-
keytar.setPassword.mockRejectedValueOnce(new Error('locked'));
|
|
120
|
-
await saveAgentCredentials('https://api.example.com', '/docs', credentials);
|
|
121
|
-
fs.chmodSync(configDir.path, 0o500);
|
|
122
|
-
await expect(
|
|
123
|
-
saveAgentCredentials('https://api.example.com', '/docs', {
|
|
124
|
-
...credentials,
|
|
125
|
-
assertion: 'next',
|
|
126
|
-
})
|
|
127
|
-
).resolves.toBeUndefined();
|
|
128
|
-
fs.chmodSync(configDir.path, 0o700);
|
|
129
|
-
expect((await loadAgentCredentials('https://api.example.com', '/docs'))?.assertion).toBe(
|
|
130
|
-
'next'
|
|
131
|
-
);
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it('fails when neither the keyring nor the local file can be written', async () => {
|
|
135
|
-
keytar.setPassword.mockRejectedValue(new Error('locked'));
|
|
136
|
-
configDir.path = path.join(configDir.path, 'not-a-directory');
|
|
137
|
-
fs.writeFileSync(configDir.path, '');
|
|
138
|
-
await expect(
|
|
139
|
-
saveAgentCredentials('https://api.example.com', '/docs', credentials)
|
|
140
|
-
).rejects.toThrow();
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
it('preserves read failures instead of treating them as an absent registration', async () => {
|
|
144
|
-
keytar.getPassword.mockRejectedValueOnce(new Error('locked'));
|
|
145
|
-
await expect(loadAgentCredentials('https://api.example.com', '/docs')).rejects.toThrow(
|
|
146
|
-
'locked'
|
|
147
|
-
);
|
|
148
|
-
});
|
|
149
|
-
});
|