watch-tail 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -2
- package/build/client/_app/immutable/assets/0.CocWI5K4.css +2 -0
- package/build/client/_app/immutable/assets/0.CocWI5K4.css.br +0 -0
- package/build/client/_app/immutable/assets/0.CocWI5K4.css.gz +0 -0
- package/build/client/_app/immutable/chunks/{QerT6gr3.js → CsscK9Dm.js} +1 -1
- package/build/client/_app/immutable/chunks/CsscK9Dm.js.br +0 -0
- package/build/client/_app/immutable/chunks/CsscK9Dm.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{BesB6AFH.js → KFjMNKuK.js} +1 -1
- package/build/client/_app/immutable/chunks/KFjMNKuK.js.br +0 -0
- package/build/client/_app/immutable/chunks/KFjMNKuK.js.gz +0 -0
- package/build/client/_app/immutable/entry/{app.CzjiNS6M.js → app.CO3UiVLU.js} +2 -2
- package/build/client/_app/immutable/entry/app.CO3UiVLU.js.br +0 -0
- package/build/client/_app/immutable/entry/app.CO3UiVLU.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.t_nX227Y.js +1 -0
- package/build/client/_app/immutable/entry/start.t_nX227Y.js.br +0 -0
- package/build/client/_app/immutable/entry/start.t_nX227Y.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{1.D6GC5CeD.js → 1.Bink5luw.js} +1 -1
- package/build/client/_app/immutable/nodes/1.Bink5luw.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.Bink5luw.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{2.SS1SkbCf.js → 2.U0ljz8ot.js} +1 -1
- package/build/client/_app/immutable/nodes/2.U0ljz8ot.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.U0ljz8ot.js.gz +0 -0
- package/build/client/_app/version.json +1 -1
- package/build/client/_app/version.json.br +0 -0
- package/build/client/_app/version.json.gz +0 -0
- package/build/handler.js +4 -4
- package/build/index.js +4 -4
- package/build/server/chunks/chunks/{internal.js-DLSlytOS.js → internal.js-DKaleXjD.js} +2 -2
- package/build/server/chunks/chunks/{internal.js-DLSlytOS.js.map → internal.js-DKaleXjD.js.map} +1 -1
- package/build/server/chunks/chunks/{state.js-BhSX5geM.js → state.js-B3c1pOs1.js} +2 -2
- package/build/server/chunks/chunks/{state.js-BhSX5geM.js.map → state.js-B3c1pOs1.js.map} +1 -1
- package/build/server/chunks/entries/fallbacks/{error.svelte.js-BhjmZlem.js → error.svelte.js-DwU8Vh5R.js} +3 -3
- package/build/server/chunks/entries/fallbacks/{error.svelte.js-BhjmZlem.js.map → error.svelte.js-DwU8Vh5R.js.map} +1 -1
- package/build/server/chunks/entries/pages/{_page.svelte.js-jJckgdLK.js → _page.svelte.js-CsJstkpB.js} +3 -3
- package/build/server/chunks/entries/pages/{_page.svelte.js-jJckgdLK.js.map → _page.svelte.js-CsJstkpB.js.map} +1 -1
- package/build/server/chunks/{handler-BxmFhnZG.js → handler-Dw17Mzkr.js} +3 -3
- package/build/server/chunks/{handler-BxmFhnZG.js.map → handler-Dw17Mzkr.js.map} +1 -1
- package/build/server/chunks/{index.js-Bnel_BoF.js → index.js-CAGsYI3V.js} +2 -2
- package/build/server/chunks/{index.js-Bnel_BoF.js.map → index.js-CAGsYI3V.js.map} +1 -1
- package/build/server/chunks/{manifest.js-BWGL1R_d.js → manifest.js-CVImQuLw.js} +5 -5
- package/build/server/chunks/{manifest.js-BWGL1R_d.js.map → manifest.js-CVImQuLw.js.map} +1 -1
- package/build/server/chunks/nodes/{0.js-Dl0J9d3p.js → 0.js-Bg4_hDjk.js} +3 -3
- package/build/server/chunks/nodes/{0.js-Dl0J9d3p.js.map → 0.js-Bg4_hDjk.js.map} +1 -1
- package/build/server/chunks/nodes/{1.js-Xw6DMXPq.js → 1.js-D_fOWpEW.js} +3 -3
- package/build/server/chunks/nodes/{1.js-Xw6DMXPq.js.map → 1.js-D_fOWpEW.js.map} +1 -1
- package/build/server/chunks/nodes/2.js-t5EYxWxj.js +9 -0
- package/build/server/chunks/nodes/{2.js-D-gAbfe_.js.map → 2.js-t5EYxWxj.js.map} +1 -1
- package/dist/cli/completions.js +8 -7
- package/dist/cli/index.js +73 -2
- package/dist/cli/preflight.js +73 -0
- package/dist/cli/ui.js +8 -1
- package/dist/lib/cli/aws.js +9 -1
- package/dist/lib/cli/credentials.js +163 -0
- package/dist/lib/types.js +5 -0
- package/package.json +1 -1
- package/build/client/_app/immutable/assets/0.B1NsRRrX.css +0 -2
- package/build/client/_app/immutable/assets/0.B1NsRRrX.css.br +0 -0
- package/build/client/_app/immutable/assets/0.B1NsRRrX.css.gz +0 -0
- package/build/client/_app/immutable/chunks/BesB6AFH.js.br +0 -2
- package/build/client/_app/immutable/chunks/BesB6AFH.js.gz +0 -0
- package/build/client/_app/immutable/chunks/QerT6gr3.js.br +0 -0
- package/build/client/_app/immutable/chunks/QerT6gr3.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.CzjiNS6M.js.br +0 -0
- package/build/client/_app/immutable/entry/app.CzjiNS6M.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.D4ev--zA.js +0 -1
- package/build/client/_app/immutable/entry/start.D4ev--zA.js.br +0 -0
- package/build/client/_app/immutable/entry/start.D4ev--zA.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.D6GC5CeD.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.D6GC5CeD.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.SS1SkbCf.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.SS1SkbCf.js.gz +0 -0
- package/build/server/chunks/nodes/2.js-D-gAbfe_.js +0 -9
- /package/build/client/_app/immutable/nodes/{0.D5bYt7iX.js → 0.DHBUWbOo.js} +0 -0
- /package/build/client/_app/immutable/nodes/{0.D5bYt7iX.js.br → 0.DHBUWbOo.js.br} +0 -0
- /package/build/client/_app/immutable/nodes/{0.D5bYt7iX.js.gz → 0.DHBUWbOo.js.gz} +0 -0
|
@@ -10,11 +10,11 @@ return {
|
|
|
10
10
|
assets: new Set(["robots.txt"]),
|
|
11
11
|
mimeTypes: {".txt":"text/plain"},
|
|
12
12
|
_: {
|
|
13
|
-
client: {start:"_app/immutable/entry/start.
|
|
13
|
+
client: {start:"_app/immutable/entry/start.t_nX227Y.js",app:"_app/immutable/entry/app.CO3UiVLU.js",imports:["_app/immutable/entry/start.t_nX227Y.js","_app/immutable/chunks/CsscK9Dm.js","_app/immutable/chunks/ZAD2y5xJ.js","_app/immutable/entry/app.CO3UiVLU.js","_app/immutable/chunks/ZAD2y5xJ.js","_app/immutable/chunks/xihTtKlq.js"],stylesheets:[],fonts:[],uses_env_dynamic_public:false},
|
|
14
14
|
nodes: [
|
|
15
|
-
__memo(() => import('./nodes/0.js-
|
|
16
|
-
__memo(() => import('./nodes/1.js-
|
|
17
|
-
__memo(() => import('./nodes/2.js-
|
|
15
|
+
__memo(() => import('./nodes/0.js-Bg4_hDjk.js')),
|
|
16
|
+
__memo(() => import('./nodes/1.js-D_fOWpEW.js')),
|
|
17
|
+
__memo(() => import('./nodes/2.js-t5EYxWxj.js'))
|
|
18
18
|
],
|
|
19
19
|
remotes: {
|
|
20
20
|
|
|
@@ -67,4 +67,4 @@ return {
|
|
|
67
67
|
})();
|
|
68
68
|
|
|
69
69
|
export { manifest as m };
|
|
70
|
-
//# sourceMappingURL=manifest.js-
|
|
70
|
+
//# sourceMappingURL=manifest.js-CVImQuLw.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.js-
|
|
1
|
+
{"version":3,"file":"manifest.js-CVImQuLw.js","sources":["../../../.svelte-kit/output/server/manifest.js"],"sourcesContent":["export const manifest = (() => {\nfunction __memo(fn) {\n\tlet value;\n\treturn () => value ??= (value = fn());\n}\n\nreturn {\n\tappDir: \"_app\",\n\tappPath: \"_app\",\n\tassets: new Set([\"robots.txt\"]),\n\tmimeTypes: {\".txt\":\"text/plain\"},\n\t_: {\n\t\tclient: {start:\"_app/immutable/entry/start.t_nX227Y.js\",app:\"_app/immutable/entry/app.CO3UiVLU.js\",imports:[\"_app/immutable/entry/start.t_nX227Y.js\",\"_app/immutable/chunks/CsscK9Dm.js\",\"_app/immutable/chunks/ZAD2y5xJ.js\",\"_app/immutable/entry/app.CO3UiVLU.js\",\"_app/immutable/chunks/ZAD2y5xJ.js\",\"_app/immutable/chunks/xihTtKlq.js\"],stylesheets:[],fonts:[],uses_env_dynamic_public:false},\n\t\tnodes: [\n\t\t\t__memo(() => import('./nodes/0.js')),\n\t\t\t__memo(() => import('./nodes/1.js')),\n\t\t\t__memo(() => import('./nodes/2.js'))\n\t\t],\n\t\tremotes: {\n\t\t\t\n\t\t},\n\t\troutes: [\n\t\t\t{\n\t\t\t\tid: \"/\",\n\t\t\t\tpattern: /^\\/$/,\n\t\t\t\tparams: [],\n\t\t\t\tpage: { layouts: [0,], errors: [1,], leaf: 2 },\n\t\t\t\tendpoint: null\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"/api/health\",\n\t\t\t\tpattern: /^\\/api\\/health\\/?$/,\n\t\t\t\tparams: [],\n\t\t\t\tpage: null,\n\t\t\t\tendpoint: __memo(() => import('./entries/endpoints/api/health/_server.ts.js'))\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"/api/log-groups\",\n\t\t\t\tpattern: /^\\/api\\/log-groups\\/?$/,\n\t\t\t\tparams: [],\n\t\t\t\tpage: null,\n\t\t\t\tendpoint: __memo(() => import('./entries/endpoints/api/log-groups/_server.ts.js'))\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"/api/regions\",\n\t\t\t\tpattern: /^\\/api\\/regions\\/?$/,\n\t\t\t\tparams: [],\n\t\t\t\tpage: null,\n\t\t\t\tendpoint: __memo(() => import('./entries/endpoints/api/regions/_server.ts.js'))\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"/api/stream\",\n\t\t\t\tpattern: /^\\/api\\/stream\\/?$/,\n\t\t\t\tparams: [],\n\t\t\t\tpage: null,\n\t\t\t\tendpoint: __memo(() => import('./entries/endpoints/api/stream/_server.ts.js'))\n\t\t\t}\n\t\t],\n\t\tprerendered_routes: new Set([]),\n\t\tmatchers: async () => {\n\t\t\t\n\t\t\treturn { };\n\t\t},\n\t\tserver_assets: {}\n\t}\n}\n})();\n"],"names":[],"mappings":"AAAY,MAAC,QAAQ,GAAG,CAAC,MAAM;AAC/B,SAAS,MAAM,CAAC,EAAE,EAAE;AACpB,CAAC,IAAI,KAAK;AACV,CAAC,OAAO,MAAM,KAAK,MAAM,KAAK,GAAG,EAAE,EAAE,CAAC;AACtC;;AAEA,OAAO;AACP,CAAC,MAAM,EAAE,MAAM;AACf,CAAC,OAAO,EAAE,MAAM;AAChB,CAAC,MAAM,EAAE,IAAI,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC;AAChC,CAAC,SAAS,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC;AACjC,CAAC,CAAC,EAAE;AACJ,EAAE,MAAM,EAAE,CAAC,KAAK,CAAC,wCAAwC,CAAC,GAAG,CAAC,sCAAsC,CAAC,OAAO,CAAC,CAAC,wCAAwC,CAAC,mCAAmC,CAAC,mCAAmC,CAAC,sCAAsC,CAAC,mCAAmC,CAAC,mCAAmC,CAAC,CAAC,WAAW,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,uBAAuB,CAAC,KAAK,CAAC;AACrY,EAAE,KAAK,EAAE;AACT,GAAG,MAAM,CAAC,MAAM,OAAO,0BAAc,CAAC,CAAC;AACvC,GAAG,MAAM,CAAC,MAAM,OAAO,0BAAc,CAAC,CAAC;AACvC,GAAG,MAAM,CAAC,MAAM,OAAO,0BAAc,CAAC;AACtC,GAAG;AACH,EAAE,OAAO,EAAE;AACX;AACA,GAAG;AACH,EAAE,MAAM,EAAE;AACV,GAAG;AACH,IAAI,EAAE,EAAE,GAAG;AACX,IAAI,OAAO,EAAE,MAAM;AACnB,IAAI,MAAM,EAAE,EAAE;AACd,IAAI,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE;AAClD,IAAI,QAAQ,EAAE;AACd,IAAI;AACJ,GAAG;AACH,IAAI,EAAE,EAAE,aAAa;AACrB,IAAI,OAAO,EAAE,oBAAoB;AACjC,IAAI,MAAM,EAAE,EAAE;AACd,IAAI,IAAI,EAAE,IAAI;AACd,IAAI,QAAQ,EAAE,MAAM,CAAC,MAAM,OAAO,0DAA8C,CAAC;AACjF,IAAI;AACJ,GAAG;AACH,IAAI,EAAE,EAAE,iBAAiB;AACzB,IAAI,OAAO,EAAE,wBAAwB;AACrC,IAAI,MAAM,EAAE,EAAE;AACd,IAAI,IAAI,EAAE,IAAI;AACd,IAAI,QAAQ,EAAE,MAAM,CAAC,MAAM,OAAO,8DAAkD,CAAC;AACrF,IAAI;AACJ,GAAG;AACH,IAAI,EAAE,EAAE,cAAc;AACtB,IAAI,OAAO,EAAE,qBAAqB;AAClC,IAAI,MAAM,EAAE,EAAE;AACd,IAAI,IAAI,EAAE,IAAI;AACd,IAAI,QAAQ,EAAE,MAAM,CAAC,MAAM,OAAO,2DAA+C,CAAC;AAClF,IAAI;AACJ,GAAG;AACH,IAAI,EAAE,EAAE,aAAa;AACrB,IAAI,OAAO,EAAE,oBAAoB;AACjC,IAAI,MAAM,EAAE,EAAE;AACd,IAAI,IAAI,EAAE,IAAI;AACd,IAAI,QAAQ,EAAE,MAAM,CAAC,MAAM,OAAO,0DAA8C,CAAC;AACjF;AACA,GAAG;AACH,EAAE,kBAAkB,EAAE,IAAI,GAAG,CAAC,EAAE,CAAC;AACjC,EAAE,QAAQ,EAAE,YAAY;AACxB;AACA,GAAG,OAAO,IAAI;AACd,EAAE,CAAC;AACH,EAAE,aAAa,EAAE;AACjB;AACA;AACA,CAAC;;"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
const index = 0;
|
|
2
2
|
let component_cache;
|
|
3
3
|
const component = async () => component_cache ??= (await import('../entries/pages/_layout.svelte.js-Bx7lXkAi.js')).default;
|
|
4
|
-
const imports = ["_app/immutable/nodes/0.
|
|
5
|
-
const stylesheets = ["_app/immutable/assets/0.
|
|
4
|
+
const imports = ["_app/immutable/nodes/0.DHBUWbOo.js","_app/immutable/chunks/ZAD2y5xJ.js","_app/immutable/chunks/xihTtKlq.js"];
|
|
5
|
+
const stylesheets = ["_app/immutable/assets/0.CocWI5K4.css"];
|
|
6
6
|
const fonts = [];
|
|
7
7
|
|
|
8
8
|
export { component, fonts, imports, index, stylesheets };
|
|
9
|
-
//# sourceMappingURL=0.js-
|
|
9
|
+
//# sourceMappingURL=0.js-Bg4_hDjk.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"0.js-
|
|
1
|
+
{"version":3,"file":"0.js-Bg4_hDjk.js","sources":["../../../../.svelte-kit/output/server/nodes/0.js"],"sourcesContent":["\n\nexport const index = 0;\nlet component_cache;\nexport const component = async () => component_cache ??= (await import('../entries/pages/_layout.svelte.js')).default;\nexport const imports = [\"_app/immutable/nodes/0.DHBUWbOo.js\",\"_app/immutable/chunks/ZAD2y5xJ.js\",\"_app/immutable/chunks/xihTtKlq.js\"];\nexport const stylesheets = [\"_app/immutable/assets/0.CocWI5K4.css\"];\nexport const fonts = [];\n"],"names":[],"mappings":"AAEY,MAAC,KAAK,GAAG;AACrB,IAAI,eAAe;AACP,MAAC,SAAS,GAAG,YAAY,eAAe,KAAK,CAAC,MAAM,OAAO,gDAAoC,CAAC,EAAE;AAClG,MAAC,OAAO,GAAG,CAAC,oCAAoC,CAAC,mCAAmC,CAAC,mCAAmC;AACxH,MAAC,WAAW,GAAG,CAAC,sCAAsC;AACtD,MAAC,KAAK,GAAG;;"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
const index = 1;
|
|
2
2
|
let component_cache;
|
|
3
|
-
const component = async () => component_cache ??= (await import('../entries/fallbacks/error.svelte.js-
|
|
4
|
-
const imports = ["_app/immutable/nodes/1.
|
|
3
|
+
const component = async () => component_cache ??= (await import('../entries/fallbacks/error.svelte.js-DwU8Vh5R.js')).default;
|
|
4
|
+
const imports = ["_app/immutable/nodes/1.Bink5luw.js","_app/immutable/chunks/ZAD2y5xJ.js","_app/immutable/chunks/xihTtKlq.js","_app/immutable/chunks/KFjMNKuK.js","_app/immutable/chunks/CsscK9Dm.js"];
|
|
5
5
|
const stylesheets = [];
|
|
6
6
|
const fonts = [];
|
|
7
7
|
|
|
8
8
|
export { component, fonts, imports, index, stylesheets };
|
|
9
|
-
//# sourceMappingURL=1.js-
|
|
9
|
+
//# sourceMappingURL=1.js-D_fOWpEW.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"1.js-
|
|
1
|
+
{"version":3,"file":"1.js-D_fOWpEW.js","sources":["../../../../.svelte-kit/output/server/nodes/1.js"],"sourcesContent":["\n\nexport const index = 1;\nlet component_cache;\nexport const component = async () => component_cache ??= (await import('../entries/fallbacks/error.svelte.js')).default;\nexport const imports = [\"_app/immutable/nodes/1.Bink5luw.js\",\"_app/immutable/chunks/ZAD2y5xJ.js\",\"_app/immutable/chunks/xihTtKlq.js\",\"_app/immutable/chunks/KFjMNKuK.js\",\"_app/immutable/chunks/CsscK9Dm.js\"];\nexport const stylesheets = [];\nexport const fonts = [];\n"],"names":[],"mappings":"AAEY,MAAC,KAAK,GAAG;AACrB,IAAI,eAAe;AACP,MAAC,SAAS,GAAG,YAAY,eAAe,KAAK,CAAC,MAAM,OAAO,kDAAsC,CAAC,EAAE;AACpG,MAAC,OAAO,GAAG,CAAC,oCAAoC,CAAC,mCAAmC,CAAC,mCAAmC,CAAC,mCAAmC,CAAC,mCAAmC;AAChM,MAAC,WAAW,GAAG;AACf,MAAC,KAAK,GAAG;;"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const index = 2;
|
|
2
|
+
let component_cache;
|
|
3
|
+
const component = async () => component_cache ??= (await import('../entries/pages/_page.svelte.js-CsJstkpB.js')).default;
|
|
4
|
+
const imports = ["_app/immutable/nodes/2.U0ljz8ot.js","_app/immutable/chunks/ZAD2y5xJ.js","_app/immutable/chunks/CsscK9Dm.js","_app/immutable/chunks/xihTtKlq.js","_app/immutable/chunks/KFjMNKuK.js"];
|
|
5
|
+
const stylesheets = [];
|
|
6
|
+
const fonts = [];
|
|
7
|
+
|
|
8
|
+
export { component, fonts, imports, index, stylesheets };
|
|
9
|
+
//# sourceMappingURL=2.js-t5EYxWxj.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"2.js-
|
|
1
|
+
{"version":3,"file":"2.js-t5EYxWxj.js","sources":["../../../../.svelte-kit/output/server/nodes/2.js"],"sourcesContent":["\n\nexport const index = 2;\nlet component_cache;\nexport const component = async () => component_cache ??= (await import('../entries/pages/_page.svelte.js')).default;\nexport const imports = [\"_app/immutable/nodes/2.U0ljz8ot.js\",\"_app/immutable/chunks/ZAD2y5xJ.js\",\"_app/immutable/chunks/CsscK9Dm.js\",\"_app/immutable/chunks/xihTtKlq.js\",\"_app/immutable/chunks/KFjMNKuK.js\"];\nexport const stylesheets = [];\nexport const fonts = [];\n"],"names":[],"mappings":"AAEY,MAAC,KAAK,GAAG;AACrB,IAAI,eAAe;AACP,MAAC,SAAS,GAAG,YAAY,eAAe,KAAK,CAAC,MAAM,OAAO,8CAAkC,CAAC,EAAE;AAChG,MAAC,OAAO,GAAG,CAAC,oCAAoC,CAAC,mCAAmC,CAAC,mCAAmC,CAAC,mCAAmC,CAAC,mCAAmC;AAChM,MAAC,WAAW,GAAG;AACf,MAAC,KAAK,GAAG;;"}
|
package/dist/cli/completions.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* paths are dispatched before normal flag parsing.
|
|
7
7
|
*/
|
|
8
8
|
import t from '@bomb.sh/tab';
|
|
9
|
-
import {
|
|
9
|
+
import { readProfiles } from "../lib/cli/aws.js";
|
|
10
10
|
import { DEFAULT_HOST, DEFAULT_PORT, FLOCI_ENDPOINT } from "./options.js";
|
|
11
11
|
/** Shells `tab` can generate scripts for. */
|
|
12
12
|
export const SHELLS = ['zsh', 'bash', 'fish', 'powershell'];
|
|
@@ -57,6 +57,7 @@ function registerCompletionSpec(profiles = []) {
|
|
|
57
57
|
complete(DEFAULT_HOST, 'loopback only');
|
|
58
58
|
complete('0.0.0.0', 'all interfaces (exposes your AWS access)');
|
|
59
59
|
});
|
|
60
|
+
t.option('open', 'Open a browser window (default)');
|
|
60
61
|
t.option('no-open', 'Do not open a browser window');
|
|
61
62
|
t.option('print', 'Print the environment and exit');
|
|
62
63
|
t.option('list', 'List AWS profiles and exit');
|
|
@@ -68,8 +69,6 @@ function registerCompletionSpec(profiles = []) {
|
|
|
68
69
|
for (const shell of SHELLS)
|
|
69
70
|
emit(shell, `${shell} completion script`);
|
|
70
71
|
});
|
|
71
|
-
// Historic window presets are useful for any future flag that takes a range.
|
|
72
|
-
t.command('presets', `Historic ranges: ${HISTORIC_PRESETS.join(', ')}`);
|
|
73
72
|
}
|
|
74
73
|
/**
|
|
75
74
|
* Handles `complete ...`.
|
|
@@ -77,18 +76,20 @@ function registerCompletionSpec(profiles = []) {
|
|
|
77
76
|
* Returns the exit code for the CLI, or `null` when the words are not a
|
|
78
77
|
* completion request at all.
|
|
79
78
|
*/
|
|
80
|
-
export function handleCompletion(words,
|
|
79
|
+
export function handleCompletion(words, options = {}) {
|
|
81
80
|
if (words.length === 0)
|
|
82
81
|
return null;
|
|
83
82
|
const [first, ...rest] = words;
|
|
84
|
-
|
|
83
|
+
// Profile names come from the user's own AWS files, so `--profile=<TAB>`
|
|
84
|
+
// completes the profiles they actually have.
|
|
85
|
+
registerCompletionSpec(options.profiles ?? readProfiles());
|
|
85
86
|
if (first === '--') {
|
|
86
87
|
t.parse(rest);
|
|
87
88
|
return 0;
|
|
88
89
|
}
|
|
89
90
|
if (SHELLS.includes(first)) {
|
|
90
|
-
// `tab` prints the script;
|
|
91
|
-
t.setup(PROGRAM, executable, first);
|
|
91
|
+
// `tab` prints the script; the executable is how the shell re-invokes us.
|
|
92
|
+
t.setup(PROGRAM, options.executable ?? PROGRAM, first);
|
|
92
93
|
return 0;
|
|
93
94
|
}
|
|
94
95
|
return null;
|
package/dist/cli/index.js
CHANGED
|
@@ -10,10 +10,12 @@
|
|
|
10
10
|
import { spawn } from 'node:child_process';
|
|
11
11
|
import { readFileSync } from 'node:fs';
|
|
12
12
|
import { join } from 'node:path';
|
|
13
|
-
import { buildChildEnv, describeChildEnv, isEmulatorEndpoint, isLocalEnvPresent, readConfigText, readProfiles, resolveRunRegion, signalExitCode, } from "../lib/cli/aws.js";
|
|
13
|
+
import { buildChildEnv, describeChildEnv, isEmulatorEndpoint, isLocalEnvPresent, readConfigText, readCredentialsText, readProfiles, resolveRunRegion, signalExitCode, } from "../lib/cli/aws.js";
|
|
14
|
+
import { classifyCredentialFailure, describeStyle, isCredentialFailure, loginAdvice, readProfileStyle, } from "../lib/cli/credentials.js";
|
|
14
15
|
import { handleCompletion } from "./completions.js";
|
|
15
16
|
import { isLoopbackHost, parseCliArgs, usageText } from "./options.js";
|
|
16
17
|
import { appRootFromHere, healthUrl, openBrowser as openBrowserDefault, startServer, stopServer, uiUrl, waitForHealth, } from "./server.js";
|
|
18
|
+
import { isHeadless, probeCredentials as probeCredentialsDefault, runLogin as runLoginDefault, } from "./preflight.js";
|
|
17
19
|
import { createUi, isInteractive } from "./ui.js";
|
|
18
20
|
/** Reads the version from the package manifest next to the app root. */
|
|
19
21
|
export function readVersion(appRoot) {
|
|
@@ -68,12 +70,20 @@ function defaultIo() {
|
|
|
68
70
|
appRoot,
|
|
69
71
|
version: readVersion(appRoot),
|
|
70
72
|
openBrowser: (url) => openBrowserDefault(url),
|
|
73
|
+
readCredentialsText: () => readCredentialsText(),
|
|
74
|
+
probeCredentials: (input) => probeCredentialsDefault(input),
|
|
75
|
+
runLogin: (command) => runLoginDefault(command),
|
|
71
76
|
waitForHealth,
|
|
72
77
|
startServerImpl: startServer,
|
|
73
78
|
spawnImpl: spawn,
|
|
74
79
|
waitForStop,
|
|
75
80
|
};
|
|
76
81
|
}
|
|
82
|
+
/** `AWS_PROFILE` from the environment, trimmed, or `null`. */
|
|
83
|
+
export function ambientProfile(env) {
|
|
84
|
+
const value = env.AWS_PROFILE?.trim();
|
|
85
|
+
return value !== undefined && value.length > 0 ? value : null;
|
|
86
|
+
}
|
|
77
87
|
/** Region for this run: explicit flag, then the shell, then the profile. */
|
|
78
88
|
export function resolveCliRegion(options, io) {
|
|
79
89
|
const resolved = resolveRunRegion({
|
|
@@ -90,6 +100,60 @@ export function resolveCliRegion(options, io) {
|
|
|
90
100
|
return 'us-east-1';
|
|
91
101
|
return null;
|
|
92
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Asks the running app for one log group and, when AWS refuses because of
|
|
105
|
+
* credentials, offers to run the login command that profile needs.
|
|
106
|
+
*
|
|
107
|
+
* Emulator runs are skipped: `--floci` supplies throwaway keys on purpose.
|
|
108
|
+
*/
|
|
109
|
+
async function ensureCredentials(input) {
|
|
110
|
+
const { options, io, ui, url, region } = input;
|
|
111
|
+
if (options.endpoint !== null)
|
|
112
|
+
return;
|
|
113
|
+
const probe = await io.probeCredentials({ baseUrl: url, region });
|
|
114
|
+
if (probe.ok)
|
|
115
|
+
return;
|
|
116
|
+
if (!probe.credentialProblem || !isCredentialFailure(probe.code, probe.message)) {
|
|
117
|
+
ui.warn(`could not list log groups: ${probe.message}`);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
// The flag wins, then the ambient AWS_PROFILE: both decide which login
|
|
121
|
+
// command can actually repair the failure.
|
|
122
|
+
const profile = options.profile ?? ambientProfile(io.env) ?? 'default';
|
|
123
|
+
const style = readProfileStyle(io.readConfigText(), io.readCredentialsText(), profile);
|
|
124
|
+
const failure = classifyCredentialFailure(probe.message, probe.code);
|
|
125
|
+
const advice = loginAdvice({
|
|
126
|
+
style,
|
|
127
|
+
failure,
|
|
128
|
+
profile,
|
|
129
|
+
remote: isHeadless(io.env),
|
|
130
|
+
});
|
|
131
|
+
ui.warn(`AWS credentials are not usable: ${probe.message}`);
|
|
132
|
+
if (advice === null) {
|
|
133
|
+
ui.info(style === 'static'
|
|
134
|
+
? `the ${describeStyle(style)} in profile "${profile}" look wrong - check them with \`aws configure --profile ${profile}\``
|
|
135
|
+
: `no login command applies to profile "${profile}" (${describeStyle(style)})`);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const label = `aws ${advice.command.join(' ')}`;
|
|
139
|
+
const accepted = await ui.confirm(`${advice.hint}. Run \`${label}\` now?`);
|
|
140
|
+
if (!accepted) {
|
|
141
|
+
ui.info(`run it yourself, then press Refresh in the UI: ${label}`);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
ui.info(`running ${label}`);
|
|
145
|
+
const code = await io.runLogin(advice.command);
|
|
146
|
+
if (code !== 0) {
|
|
147
|
+
ui.warn(`${label} exited with code ${code}`);
|
|
148
|
+
ui.info(`log in another terminal, then press Refresh in the UI: ${label}`);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const retry = await io.probeCredentials({ baseUrl: url, region });
|
|
152
|
+
if (retry.ok)
|
|
153
|
+
ui.info('signed in - credentials work now');
|
|
154
|
+
else
|
|
155
|
+
ui.warn(`still failing after login: ${retry.message}`);
|
|
156
|
+
}
|
|
93
157
|
/** Runs the CLI and returns its exit code. */
|
|
94
158
|
export async function run(argv, overrides = {}) {
|
|
95
159
|
const io = { ...defaultIo(), ...overrides };
|
|
@@ -101,7 +165,7 @@ export async function run(argv, overrides = {}) {
|
|
|
101
165
|
}
|
|
102
166
|
const options = parsed.options;
|
|
103
167
|
if (options.complete !== null) {
|
|
104
|
-
const code = handleCompletion(options.complete);
|
|
168
|
+
const code = handleCompletion(options.complete, { profiles: io.readProfiles() });
|
|
105
169
|
if (code !== null)
|
|
106
170
|
return code;
|
|
107
171
|
}
|
|
@@ -123,11 +187,15 @@ export async function run(argv, overrides = {}) {
|
|
|
123
187
|
return 0;
|
|
124
188
|
}
|
|
125
189
|
const region = resolveCliRegion(options, io);
|
|
190
|
+
// `--profile` wins, then the ambient AWS_PROFILE: the credential check and
|
|
191
|
+
// the login advice both need to know which profile is actually in play.
|
|
192
|
+
const profile = options.profile ?? ambientProfile(io.env);
|
|
126
193
|
const childEnv = buildChildEnv({
|
|
127
194
|
base: io.env,
|
|
128
195
|
profile: options.profile,
|
|
129
196
|
region,
|
|
130
197
|
endpoint: options.endpoint,
|
|
198
|
+
clearStaticKeys: options.profile === null && profile !== null,
|
|
131
199
|
});
|
|
132
200
|
if (options.print) {
|
|
133
201
|
for (const line of describeChildEnv(childEnv))
|
|
@@ -170,6 +238,9 @@ export async function run(argv, overrides = {}) {
|
|
|
170
238
|
return 1;
|
|
171
239
|
}
|
|
172
240
|
ui.stopSpinner(`listening on ${url}`);
|
|
241
|
+
// Before sending anyone to a UI that cannot load logs, check that AWS will
|
|
242
|
+
// actually answer, and offer the login that fixes it.
|
|
243
|
+
await ensureCredentials({ options, io, ui, url, region });
|
|
173
244
|
if (options.open)
|
|
174
245
|
io.openBrowser(url);
|
|
175
246
|
ui.outro(`${url} (Ctrl+C to stop)`);
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Startup credential check for the CLI.
|
|
3
|
+
*
|
|
4
|
+
* Rather than resolving credentials twice, the CLI asks the running app: the
|
|
5
|
+
* `/api/log-groups` route uses the same SDK client and the same environment, and
|
|
6
|
+
* it already maps provider failures onto codes such as `missing-credentials`. If
|
|
7
|
+
* that comes back as a credential failure, {@link ./credentials.ts} decides which
|
|
8
|
+
* login command repairs it, and the CLI can run it and retry.
|
|
9
|
+
*/
|
|
10
|
+
import { spawn } from 'node:child_process';
|
|
11
|
+
/**
|
|
12
|
+
* Asks the app whether it can list log groups.
|
|
13
|
+
*
|
|
14
|
+
* Only the first error is reported: with no credentials every region fails the
|
|
15
|
+
* same way, and the point is to spot it early, not to be exhaustive.
|
|
16
|
+
*/
|
|
17
|
+
export async function probeCredentials(input) {
|
|
18
|
+
const { baseUrl, region, fetchImpl = fetch, timeoutMs = 10_000 } = input;
|
|
19
|
+
const url = new URL('/api/log-groups', baseUrl);
|
|
20
|
+
if (region !== null && region !== '')
|
|
21
|
+
url.searchParams.set('region', region);
|
|
22
|
+
url.searchParams.set('limit', '1');
|
|
23
|
+
try {
|
|
24
|
+
const response = await fetchImpl(url, { signal: AbortSignal.timeout(timeoutMs) });
|
|
25
|
+
if (response.ok)
|
|
26
|
+
return { ok: true };
|
|
27
|
+
const body = (await response.json().catch(() => null));
|
|
28
|
+
const message = body?.error ?? `HTTP ${response.status}`;
|
|
29
|
+
return { ok: false, code: body?.code, message, credentialProblem: true };
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
// A transport failure is not a credentials problem.
|
|
33
|
+
return {
|
|
34
|
+
ok: false,
|
|
35
|
+
message: error instanceof Error ? error.message : String(error),
|
|
36
|
+
credentialProblem: false,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** Runs a login command with the terminal attached, so the browser flow works. */
|
|
41
|
+
export function runLogin(command, options = {}) {
|
|
42
|
+
const { spawnImpl = spawn, stdio = 'inherit' } = options;
|
|
43
|
+
return new Promise((resolveExit) => {
|
|
44
|
+
let child;
|
|
45
|
+
try {
|
|
46
|
+
child = spawnImpl('aws', command, { stdio, shell: false });
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
resolveExit(127);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
child.on('error', () => resolveExit(127));
|
|
53
|
+
child.on('close', (code) => resolveExit(code ?? 1));
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* True when there is no browser to drive the login (SSH, headless).
|
|
58
|
+
*
|
|
59
|
+
* `WATCH_TAIL_HEADLESS` overrides the guess in both directions: `1` forces the
|
|
60
|
+
* `--remote` flow, `0` forces the browser flow on a host that looks headless
|
|
61
|
+
* (a Linux container with a browser proxy, for example).
|
|
62
|
+
*/
|
|
63
|
+
export function isHeadless(env = process.env) {
|
|
64
|
+
if (env.WATCH_TAIL_HEADLESS === '1')
|
|
65
|
+
return true;
|
|
66
|
+
if (env.WATCH_TAIL_HEADLESS === '0')
|
|
67
|
+
return false;
|
|
68
|
+
if (env.SSH_CONNECTION !== undefined && env.SSH_CONNECTION !== '')
|
|
69
|
+
return true;
|
|
70
|
+
if (process.platform === 'linux' && env.DISPLAY === undefined)
|
|
71
|
+
return true;
|
|
72
|
+
return false;
|
|
73
|
+
}
|
package/dist/cli/ui.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* banner, spinner, autocomplete prompts. Anything else (pipes, CI, `--no-color`)
|
|
6
6
|
* falls back to plain lines, which also keeps the integration tests readable.
|
|
7
7
|
*/
|
|
8
|
-
import { autocomplete, cancel, intro, isCancel, log, outro, spinner } from '@clack/prompts';
|
|
8
|
+
import { autocomplete, cancel, confirm, intro, isCancel, log, outro, spinner, } from '@clack/prompts';
|
|
9
9
|
/** Decides whether the process can render animated output and ask questions. */
|
|
10
10
|
export function isInteractive(env = process.env) {
|
|
11
11
|
if (env.CI !== undefined && env.CI !== '' && env.CI !== 'false')
|
|
@@ -39,6 +39,7 @@ export function createUi(options = {}) {
|
|
|
39
39
|
const fallback = initial ?? choices[0]?.value ?? null;
|
|
40
40
|
return fallback;
|
|
41
41
|
},
|
|
42
|
+
confirm: async () => false,
|
|
42
43
|
};
|
|
43
44
|
}
|
|
44
45
|
return {
|
|
@@ -71,5 +72,11 @@ export function createUi(options = {}) {
|
|
|
71
72
|
}
|
|
72
73
|
return String(answer);
|
|
73
74
|
},
|
|
75
|
+
confirm: async (message, initial = true) => {
|
|
76
|
+
const answer = await confirm({ message, initialValue: initial });
|
|
77
|
+
if (isCancel(answer))
|
|
78
|
+
return false;
|
|
79
|
+
return answer === true;
|
|
80
|
+
},
|
|
74
81
|
};
|
|
75
82
|
}
|
package/dist/lib/cli/aws.js
CHANGED
|
@@ -185,7 +185,7 @@ const EMULATOR_CREDENTIALS = { accessKeyId: 'test', secretAccessKey: 'test' };
|
|
|
185
185
|
* `AWS_DEFAULT_REGION`, which is how `--region` (or a region resolved by
|
|
186
186
|
* {@link resolveRunRegion}) wins over the profile.
|
|
187
187
|
*/
|
|
188
|
-
export function buildChildEnv({ base, profile = null, region = null, endpoint = null, }) {
|
|
188
|
+
export function buildChildEnv({ base, profile = null, region = null, endpoint = null, clearStaticKeys = false, }) {
|
|
189
189
|
const env = { ...base };
|
|
190
190
|
const name = normalize(profile);
|
|
191
191
|
const emulator = normalize(endpoint);
|
|
@@ -194,6 +194,10 @@ export function buildChildEnv({ base, profile = null, region = null, endpoint =
|
|
|
194
194
|
for (const key of [...LOCAL_OVERRIDE_KEYS, ...REGION_KEYS])
|
|
195
195
|
env[key] = '';
|
|
196
196
|
}
|
|
197
|
+
if (name === null && clearStaticKeys) {
|
|
198
|
+
env.AWS_ACCESS_KEY_ID = '';
|
|
199
|
+
env.AWS_SECRET_ACCESS_KEY = '';
|
|
200
|
+
}
|
|
197
201
|
if (emulator !== null) {
|
|
198
202
|
env.AWS_ENDPOINT_URL = emulator;
|
|
199
203
|
env.AWS_ENDPOINT_URL_LOGS = emulator;
|
|
@@ -243,6 +247,10 @@ export function describeChildEnv(env) {
|
|
|
243
247
|
export function readConfigText(env = process.env) {
|
|
244
248
|
return readTextFile(profileFiles(env).configFile);
|
|
245
249
|
}
|
|
250
|
+
/** Reads the shared credentials file, or an empty string when it is missing. */
|
|
251
|
+
export function readCredentialsText(env = process.env) {
|
|
252
|
+
return readTextFile(profileFiles(env).credentialsFile);
|
|
253
|
+
}
|
|
246
254
|
/** True when `.env.local` (the floci settings) exists in the repository root. */
|
|
247
255
|
export function isLocalEnvPresent(cwd = process.cwd()) {
|
|
248
256
|
return existsSync(join(cwd, LOCAL_ENV_FILE));
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential diagnosis for the CLI: why AWS is refusing, and which login command
|
|
3
|
+
* fixes it.
|
|
4
|
+
*
|
|
5
|
+
* The app itself reports the failure (`/api/log-groups` answers 502 with a code
|
|
6
|
+
* such as `missing-credentials`), so this module only has to answer the second
|
|
7
|
+
* half of the question: what should the user run? That depends on how the
|
|
8
|
+
* profile is configured, which is read from the user's own AWS files.
|
|
9
|
+
*/
|
|
10
|
+
/** `true` when the failure is about credentials rather than about the region or the API. */
|
|
11
|
+
export function isCredentialFailure(code, message) {
|
|
12
|
+
if (code === 'missing-credentials' || code === 'access-denied')
|
|
13
|
+
return true;
|
|
14
|
+
return /credential|expired|sso|sign in|sign-in|reauthenticate|not authorized|unrecognized client/i.test(message);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Classifies the message the SDK produced.
|
|
18
|
+
*
|
|
19
|
+
* The three shapes that matter come from the AWS CLI's own providers: an SSO
|
|
20
|
+
* session that needs `aws sso login`, a console sign-in session that needs
|
|
21
|
+
* `aws login`, and static keys that are simply wrong.
|
|
22
|
+
*/
|
|
23
|
+
export function classifyCredentialFailure(message, code) {
|
|
24
|
+
if (/sso login|sso session|sso token/i.test(message))
|
|
25
|
+
return 'sso-expired';
|
|
26
|
+
if (/reauthenticate|sign in|sign-in|login session/i.test(message))
|
|
27
|
+
return 'missing';
|
|
28
|
+
if (/could not load credentials|unable to locate credentials|no credentials/i.test(message)) {
|
|
29
|
+
return 'missing';
|
|
30
|
+
}
|
|
31
|
+
if (code === 'access-denied' ||
|
|
32
|
+
/invalidclienttoken|unrecognizedclient|invalid signature|forbidden/i.test(message)) {
|
|
33
|
+
return 'unusable';
|
|
34
|
+
}
|
|
35
|
+
return code === 'missing-credentials' ? 'missing' : 'other';
|
|
36
|
+
}
|
|
37
|
+
/** Splits an ini-style file into `section -> lines`. */
|
|
38
|
+
function sections(text) {
|
|
39
|
+
const map = new Map();
|
|
40
|
+
let current = null;
|
|
41
|
+
for (const raw of text.split('\n')) {
|
|
42
|
+
const line = raw.trim();
|
|
43
|
+
if (line.length === 0 || line.startsWith('#') || line.startsWith(';'))
|
|
44
|
+
continue;
|
|
45
|
+
const header = /^\[([^\]]+)\]$/.exec(line);
|
|
46
|
+
if (header !== null) {
|
|
47
|
+
current = header[1].trim();
|
|
48
|
+
if (!map.has(current))
|
|
49
|
+
map.set(current, []);
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (current !== null)
|
|
53
|
+
map.get(current)?.push(line);
|
|
54
|
+
}
|
|
55
|
+
return map;
|
|
56
|
+
}
|
|
57
|
+
/** Normalises a profile's section name: `[profile x]` in config, `[x]` in credentials. */
|
|
58
|
+
function sectionNames(profile) {
|
|
59
|
+
return profile === 'default' ? ['default', 'profile default'] : [profile, `profile ${profile}`];
|
|
60
|
+
}
|
|
61
|
+
/** The profile's lines from whichever file defines it first. */
|
|
62
|
+
function profileLines(configText, credentialsText, profile) {
|
|
63
|
+
for (const text of [configText, credentialsText]) {
|
|
64
|
+
const map = sections(text);
|
|
65
|
+
for (const name of sectionNames(profile)) {
|
|
66
|
+
const lines = map.get(name);
|
|
67
|
+
if (lines !== undefined)
|
|
68
|
+
return lines;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
/** Reads a `key = value` setting from a profile's lines. */
|
|
74
|
+
function setting(lines, key) {
|
|
75
|
+
const pattern = new RegExp(`^${key}\\s*=\\s*(.*)$`, 'i');
|
|
76
|
+
for (const line of lines) {
|
|
77
|
+
const match = pattern.exec(line);
|
|
78
|
+
if (match !== null) {
|
|
79
|
+
const value = match[1].trim();
|
|
80
|
+
if (value.length > 0)
|
|
81
|
+
return value;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Works out how a profile authenticates.
|
|
88
|
+
*
|
|
89
|
+
* Order matters: `sso_session` and `login_session` are the modern providers, and
|
|
90
|
+
* a profile that has both (it should not) is treated as SSO because that is the
|
|
91
|
+
* one `aws sso login` repairs.
|
|
92
|
+
*/
|
|
93
|
+
export function readProfileStyle(configText, credentialsText, profile) {
|
|
94
|
+
const lines = profileLines(configText, credentialsText, profile);
|
|
95
|
+
if (lines.length === 0) {
|
|
96
|
+
const configured = [...sections(configText).keys()].some((name) => name.startsWith('profile '));
|
|
97
|
+
return configured ? 'unconfigured' : 'unconfigured';
|
|
98
|
+
}
|
|
99
|
+
if (setting(lines, 'sso_session') !== null || setting(lines, 'sso_start_url') !== null)
|
|
100
|
+
return 'sso';
|
|
101
|
+
if (setting(lines, 'login_session') !== null)
|
|
102
|
+
return 'login';
|
|
103
|
+
if (setting(lines, 'credential_process') !== null)
|
|
104
|
+
return 'process';
|
|
105
|
+
if (setting(lines, 'aws_access_key_id') !== null)
|
|
106
|
+
return 'static';
|
|
107
|
+
return 'unconfigured';
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Picks the login command for a failure.
|
|
111
|
+
*
|
|
112
|
+
* Returns `null` when no login command applies: static keys are fixed with
|
|
113
|
+
* `aws configure`, and a `credential_process` has to be debugged in place.
|
|
114
|
+
* `aws login` also refuses to convert a profile that already uses another style,
|
|
115
|
+
* which is why the style is checked before suggesting it.
|
|
116
|
+
*/
|
|
117
|
+
export function loginAdvice(input) {
|
|
118
|
+
const { style, failure, profile, remote = false } = input;
|
|
119
|
+
const named = profile !== null && profile !== 'default';
|
|
120
|
+
const remoteFlag = remote ? ['--remote'] : [];
|
|
121
|
+
// The profile's own style decides the command. A failure message that
|
|
122
|
+
// mentions SSO while the profile is not SSO-configured must not send anyone
|
|
123
|
+
// to `aws sso login`: AWS rejects that with "Missing the following required
|
|
124
|
+
// SSO configuration values" (verified against the real CLI).
|
|
125
|
+
if (style === 'sso') {
|
|
126
|
+
return {
|
|
127
|
+
command: ['sso', 'login', ...(named ? ['--profile', profile] : []), ...remoteFlag],
|
|
128
|
+
hint: named
|
|
129
|
+
? `Sign in to the SSO session for ${profile}`
|
|
130
|
+
: 'Sign in to the default SSO session',
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
if (style === 'login' || style === 'unconfigured') {
|
|
134
|
+
// `aws login` also creates the session, so it is the right answer for a
|
|
135
|
+
// profile that has no credentials configured yet.
|
|
136
|
+
const mismatch = failure === 'sso-expired';
|
|
137
|
+
return {
|
|
138
|
+
command: ['login', ...(named ? ['--profile', profile] : []), ...remoteFlag],
|
|
139
|
+
hint: named
|
|
140
|
+
? `Sign in to the AWS console and save the session in ${profile}`
|
|
141
|
+
: 'Sign in to the AWS console and save the session in the default profile',
|
|
142
|
+
...(mismatch ? { note: 'the SSO session that failed belongs to a different profile' } : {}),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
// Static keys are fixed with `aws configure`; a credential_process has to be
|
|
146
|
+
// debugged where it is defined.
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
/** One-line description of a style, for messages. */
|
|
150
|
+
export function describeStyle(style) {
|
|
151
|
+
switch (style) {
|
|
152
|
+
case 'sso':
|
|
153
|
+
return 'SSO';
|
|
154
|
+
case 'login':
|
|
155
|
+
return 'console sign-in';
|
|
156
|
+
case 'static':
|
|
157
|
+
return 'static access keys';
|
|
158
|
+
case 'process':
|
|
159
|
+
return 'a credential_process';
|
|
160
|
+
default:
|
|
161
|
+
return 'no credentials';
|
|
162
|
+
}
|
|
163
|
+
}
|
package/package.json
CHANGED